diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj
index 1499d105c..0c6702332 100644
--- a/Dalamud.Injector/Dalamud.Injector.csproj
+++ b/Dalamud.Injector/Dalamud.Injector.csproj
@@ -1,8 +1,8 @@
-
+
AnyCPU
- net471
- 7.2
+ net48
+ 8.0
AnyCPU;x64
@@ -14,10 +14,13 @@
true
- 2.4.0.0
- 2.4.0.0
+ 4.1.0.3
+ 4.1.0.3
XIVLauncher addon injection
- 2.4.0
+ 4.1.0.3
+
+
+
@@ -26,4 +29,4 @@
-
\ No newline at end of file
+
diff --git a/Dalamud.Injector/Program.cs b/Dalamud.Injector/Program.cs
index 75c0a8a75..02ec98bfc 100644
--- a/Dalamud.Injector/Program.cs
+++ b/Dalamud.Injector/Program.cs
@@ -30,7 +30,7 @@ namespace Dalamud.Injector {
case -2:
process = Process.Start(
"C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe",
- "DEV.TestSID=5fa077c389a61c4a45ea35153162753d7cdb34268cc38c9e206859a7 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0");
+ "DEV.TestSID=0 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0");
break;
default:
process = Process.GetProcessById(pid);
diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index 3f756922a..f9d0132ec 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -2,7 +2,9 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+using System.Text;
using System.Threading;
+using System.Threading.Tasks;
using Dalamud.DiscordBot;
using Dalamud.Game;
using Dalamud.Game.Chat;
@@ -44,6 +46,8 @@ namespace Dalamud {
public readonly DalamudConfiguration Configuration;
+ internal readonly WinSockHandlers WinSock2;
+
public Dalamud(DalamudStartInfo info) {
this.StartInfo = info;
this.Configuration = DalamudConfiguration.Load(info.ConfigurationPath);
@@ -73,7 +77,9 @@ namespace Dalamud {
this.PluginManager = new PluginManager(this, info.PluginDirectory, info.DefaultPluginDirectory);
this.IconReplacer = new IconReplacer(this, this.sigScanner);
-
+
+ this.WinSock2 = new WinSockHandlers();
+
try {
this.PluginManager.LoadPlugins();
} catch (Exception ex) {
@@ -88,7 +94,8 @@ namespace Dalamud {
this.BotManager.Start();
- this.IconReplacer.Enable();
+ if (this.Configuration.ComboPresets != CustomComboPreset.None)
+ this.IconReplacer.Enable();
}
public void Unload() {
@@ -106,7 +113,10 @@ namespace Dalamud {
this.unloadSignal.Dispose();
- this.IconReplacer.Dispose();
+ this.WinSock2.Dispose();
+
+ if (this.Configuration.ComboPresets != CustomComboPreset.None)
+ this.IconReplacer.Dispose();
}
private void SetupCommands() {
@@ -170,6 +180,16 @@ namespace Dalamud {
CommandManager.AddHandler("/xlbotjoin", new CommandInfo(OnBotJoinCommand) {
HelpMessage = "Add the XIVLauncher discord bot you set up to your server."
});
+
+ CommandManager.AddHandler("/xlbgmset", new CommandInfo(OnBgmSetCommand)
+ {
+ HelpMessage = "Set the Game background music. Usage: /bgmset "
+ });
+
+ CommandManager.AddHandler("/xlitem", new CommandInfo(OnItemLinkCommand)
+ {
+ HelpMessage = "Link an item by name. Usage: /item - "
+ });
}
private void OnUnloadCommand(string command, string arguments) {
@@ -196,7 +216,7 @@ namespace Dalamud {
var msg = string.Join(" ", parts.Take(1).ToArray());
Framework.Gui.Chat.PrintChat(new XivChatEntry {
- Message = msg,
+ MessageBytes = Encoding.UTF8.GetBytes(msg),
Name = "Xiv Launcher",
Type = chatType
});
@@ -346,7 +366,6 @@ namespace Dalamud {
var argumentsParts = arguments.Split();
switch (argumentsParts[0]) {
- /* Sorry!
case "setall": {
foreach (var value in Enum.GetValues(typeof(CustomComboPreset)).Cast()) {
if (value == CustomComboPreset.None)
@@ -394,7 +413,7 @@ namespace Dalamud {
}
}
break;
- */
+
case "list": {
foreach (var value in Enum.GetValues(typeof(CustomComboPreset)).Cast()) {
if (this.Configuration.ComboPresets.HasFlag(value))
@@ -418,5 +437,63 @@ namespace Dalamud {
Framework.Gui.Chat.Print(
"The XIVLauncher discord bot was not set up correctly or could not connect to discord. Please check the settings and the FAQ.");
}
+
+ private void OnBgmSetCommand(string command, string arguments)
+ {
+ Framework.Network.InjectBgmTest(int.Parse(arguments));
+ }
+
+ private void OnItemLinkCommand(string command, string arguments) {
+ Task.Run(async () => {
+ try {
+ dynamic results = await XivApi.Search(arguments, "Item", 1);
+ var itemId = (short) results.Results[0].ID;
+ var itemName = (string)results.Results[0].Name;
+
+ var hexData = new byte[] {
+ 0x02, 0x13, 0x06, 0xFE, 0xFF, 0xF3, 0xF3, 0xF3, 0x03, 0x02, 0x27, 0x07, 0x03, 0xF2, 0x3A, 0x2F,
+ 0x02, 0x01, 0x03, 0x02, 0x13, 0x06, 0xFE, 0xFF, 0xFF, 0x7B, 0x1A, 0x03, 0xEE, 0x82, 0xBB, 0x02,
+ 0x13, 0x02, 0xEC, 0x03
+ };
+
+ var endTag = new byte[] {
+ 0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03, 0x02, 0x13, 0x02, 0xEC, 0x03
+ };
+
+ BitConverter.GetBytes(itemId).Reverse().ToArray().CopyTo(hexData, 14);
+
+ hexData = hexData.Concat(Encoding.UTF8.GetBytes(itemName)).Concat(endTag).ToArray();
+
+ Framework.Gui.Chat.PrintChat(new XivChatEntry {
+ MessageBytes = hexData
+ });
+ }
+ catch {
+ Framework.Gui.Chat.PrintError("Could not find item.");
+ }
+
+ });
+ }
+
+ public static byte[] StringToByteArray(String value)
+ {
+ byte[] bytes = new byte[value.Length * sizeof(char)];
+ Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
+ return bytes;
+ }
+
+ public static String ByteArrayToString(byte[] value)
+ {
+ var chars = new char[value.Length / sizeof(char)];
+
+ var atValue = 0;
+ for (var i = 0; i < chars.Length; i++) {
+ chars[i] = BitConverter.ToChar(value, atValue);
+
+ atValue += 2;
+ }
+
+ return new string(chars);
+ }
}
}
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 9ef9364a2..0a2626404 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -1,8 +1,8 @@
AnyCPU
- net471
- 7.3
+ net48
+ 8.0
AnyCPU;x64
@@ -14,9 +14,9 @@
true
- 2.2.0.0
- 2.2.0
- 2.2.0.0
+ 4.1.0.3
+ 4.1.0.3
+ 4.1.0.3
@@ -68,4 +68,4 @@
-
\ No newline at end of file
+
diff --git a/Dalamud/DiscordBot/DiscordBotManager.cs b/Dalamud/DiscordBot/DiscordBotManager.cs
index f0a86cce0..b2d6d11db 100644
--- a/Dalamud/DiscordBot/DiscordBotManager.cs
+++ b/Dalamud/DiscordBot/DiscordBotManager.cs
@@ -231,13 +231,13 @@ namespace Dalamud.DiscordBot {
wasOutgoingTell = true;
}
- var chatTypeConfig =
- this.config.ChatTypeConfigurations.FirstOrDefault(typeConfig => typeConfig.ChatType == type);
+ var chatTypeConfigs =
+ this.config.ChatTypeConfigurations.Where(typeConfig => typeConfig.ChatType == type);
- if (chatTypeConfig == null)
+ if (!chatTypeConfigs.Any())
return;
- var channel = await GetChannel(chatTypeConfig.Channel);
+ var channels = chatTypeConfigs.Select(c => GetChannel(c.Channel).GetAwaiter().GetResult());
var senderSplit = sender.Split(new[] {this.worldIcon}, StringSplitOptions.None);
@@ -260,58 +260,89 @@ namespace Dalamud.DiscordBot {
var avatarUrl = "";
var lodestoneId = 0;
- try {
- dynamic charCandidates = await XivApi.GetCharacterSearch(sender, world);
+ if (!this.config.DisableEmbeds) {
+ try
+ {
+ dynamic charCandidates = await XivApi.GetCharacterSearch(sender, world);
- if (charCandidates.Results.Count > 0) {
- avatarUrl = charCandidates.Results[0].Avatar;
- lodestoneId = charCandidates.Results[0].ID;
+ if (charCandidates.Results.Count > 0)
+ {
+ avatarUrl = charCandidates.Results[0].Avatar;
+ lodestoneId = charCandidates.Results[0].ID;
+ }
}
- } catch (Exception ex) {
- Log.Error(ex, "Could not get XIVAPI character search result.");
- }
-
- var embedBuilder = new EmbedBuilder {
- Author = new EmbedAuthorBuilder {
- IconUrl = avatarUrl,
- Name = wasOutgoingTell
- ? "You"
- : sender + (string.IsNullOrEmpty(world) || string.IsNullOrEmpty(sender)
- ? ""
- : $" on {world}"),
- Url = lodestoneId != 0 ? "https://eu.finalfantasyxiv.com/lodestone/character/" + lodestoneId : null
- },
- Description = message,
- Timestamp = DateTimeOffset.Now,
- Footer = new EmbedFooterBuilder {Text = type.GetDetails().FancyName},
- Color = new Color((uint) (chatTypeConfig.Color & 0xFFFFFF))
- };
-
- if (this.config.CheckForDuplicateMessages) {
- var recentMsg = this.recentMessages.FirstOrDefault(
- msg => msg.Embeds.FirstOrDefault(
- embed => embed.Description == embedBuilder.Description &&
- embed.Author.HasValue &&
- embed.Author.Value.Name == embedBuilder.Author.Name &&
- embed.Timestamp.HasValue &&
- Math.Abs(
- (embed.Timestamp.Value.ToUniversalTime().Date -
- embedBuilder
- .Timestamp.Value.ToUniversalTime().Date)
- .Milliseconds) < 15000)
- != null);
-
- if (recentMsg != null) {
- Log.Verbose("Duplicate message: [{0}] {1}", embedBuilder.Author.Name, embedBuilder.Description);
- this.recentMessages.Remove(recentMsg);
- return;
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Could not get XIVAPI character search result.");
}
}
-
Thread.Sleep(this.config.ChatDelayMs);
- await channel.SendMessageAsync(embed: embedBuilder.Build());
+ var name = wasOutgoingTell
+ ? "You"
+ : sender + (string.IsNullOrEmpty(world) || string.IsNullOrEmpty(sender)
+ ? ""
+ : $" on {world}");
+
+ for (var chatTypeIndex = 0; chatTypeIndex < chatTypeConfigs.Count(); chatTypeIndex++) {
+ if (!this.config.DisableEmbeds) {
+ var embedBuilder = new EmbedBuilder
+ {
+ Author = new EmbedAuthorBuilder
+ {
+ IconUrl = avatarUrl,
+ Name = name,
+ Url = lodestoneId != 0 ? "https://eu.finalfantasyxiv.com/lodestone/character/" + lodestoneId : null
+ },
+ Description = message,
+ Timestamp = DateTimeOffset.Now,
+ Footer = new EmbedFooterBuilder { Text = type.GetDetails().FancyName },
+ Color = new Color((uint)(chatTypeConfigs.ElementAt(chatTypeIndex).Color & 0xFFFFFF))
+ };
+
+ if (this.config.CheckForDuplicateMessages)
+ {
+ var recentMsg = this.recentMessages.FirstOrDefault(
+ msg => msg.Embeds.FirstOrDefault(
+ embed => embed.Description == embedBuilder.Description &&
+ embed.Author.HasValue &&
+ embed.Author.Value.Name == embedBuilder.Author.Name &&
+ embed.Timestamp.HasValue &&
+ Math.Abs(
+ (embed.Timestamp.Value.ToUniversalTime().Date -
+ embedBuilder
+ .Timestamp.Value.ToUniversalTime().Date)
+ .Milliseconds) < 15000)
+ != null);
+
+ if (recentMsg != null)
+ {
+ Log.Verbose("Duplicate message: [{0}] {1}", embedBuilder.Author.Name, embedBuilder.Description);
+ this.recentMessages.Remove(recentMsg);
+ return;
+ }
+ }
+
+ await channels.ElementAt(chatTypeIndex).SendMessageAsync(embed: embedBuilder.Build());
+ } else {
+ var simpleMessage = $"{name}: {message}";
+
+ if (this.config.CheckForDuplicateMessages) {
+ var recentMsg = this.recentMessages.FirstOrDefault(
+ msg => msg.Content == simpleMessage);
+
+ if (recentMsg != null)
+ {
+ Log.Verbose("Duplicate message: {0}", simpleMessage);
+ this.recentMessages.Remove(recentMsg);
+ return;
+ }
+ }
+
+ await channels.ElementAt(chatTypeIndex).SendMessageAsync($"{name}: {message}");
+ }
+ }
}
private async Task GetChannel(ChannelConfiguration channelConfig) {
@@ -321,7 +352,7 @@ namespace Dalamud.DiscordBot {
}
private string RemoveAllNonLanguageCharacters(string input) {
- return Regex.Replace(input, @"[^\p{L} ]", "");
+ return Regex.Replace(input, @"[^\p{L} ']", "");
}
public void Dispose() {
diff --git a/Dalamud/DiscordBot/DiscordFeatureConfiguration.cs b/Dalamud/DiscordBot/DiscordFeatureConfiguration.cs
index 02c7f1228..2bd717863 100644
--- a/Dalamud/DiscordBot/DiscordFeatureConfiguration.cs
+++ b/Dalamud/DiscordBot/DiscordFeatureConfiguration.cs
@@ -32,11 +32,14 @@ namespace Dalamud.DiscordBot
public class DiscordFeatureConfiguration
{
public string Token { get; set; }
- public ulong OwnerUserId { get; set; }
public bool CheckForDuplicateMessages { get; set; }
public int ChatDelayMs { get; set; }
+ public bool DisableEmbeds { get; set; }
+
+ public ulong OwnerUserId { get; set; }
+
public List ChatTypeConfigurations { get; set; }
public ChannelConfiguration CfNotificationChannel { get; set; }
diff --git a/Dalamud/Game/Chat/XivChatEntry.cs b/Dalamud/Game/Chat/XivChatEntry.cs
index 8fd338d02..3f4075535 100644
--- a/Dalamud/Game/Chat/XivChatEntry.cs
+++ b/Dalamud/Game/Chat/XivChatEntry.cs
@@ -8,7 +8,7 @@ namespace Dalamud.Game.Chat {
public string Name { get; set; } = string.Empty;
- public string Message { get; set; } = string.Empty;
+ public byte[] MessageBytes { get; set; }
public IntPtr Parameters { get; set; }
}
diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs
index 4005107a4..5512a5c30 100644
--- a/Dalamud/Game/ChatHandlers.cs
+++ b/Dalamud/Game/ChatHandlers.cs
@@ -1,6 +1,8 @@
using System.Collections.Generic;
+using System.Diagnostics;
using System.Drawing;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -65,11 +67,17 @@ namespace Dalamud.Game {
private void ChatOnOnChatMessage(XivChatType type, uint senderId, string sender, ref string message,
ref bool isHandled) {
+
if (type == XivChatType.Notice && !this.hasSeenLoadingMsg) {
- this.dalamud.Framework.Gui.Chat.Print("XIVLauncher in-game addon loaded.");
+ this.dalamud.Framework.Gui.Chat.Print($"XIVLauncher in-game addon v{Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version} loaded.");
this.hasSeenLoadingMsg = true;
}
+#if !DEBUG
+ if (!this.hasSeenLoadingMsg)
+ return;
+#endif
+
var matched = this.rmtRegex.IsMatch(message);
if (matched) {
// This seems to be a RMT ad - let's not show it
diff --git a/Dalamud/Game/ClientState/Actors/ActorTable.cs b/Dalamud/Game/ClientState/Actors/ActorTable.cs
index 2dd497bc4..586404fd1 100644
--- a/Dalamud/Game/ClientState/Actors/ActorTable.cs
+++ b/Dalamud/Game/ClientState/Actors/ActorTable.cs
@@ -32,25 +32,30 @@ namespace Dalamud.Game.ClientState.Actors {
if (index > Length)
return null;
- Log.Information("Trying to get actor at {0}", index);
+ //Log.Information("Trying to get actor at {0}", index);
var tblIndex = Address.ActorTable + 8 + index * 8;
var offset = Marshal.ReadIntPtr(tblIndex);
- Log.Information("Actor at {0}", offset.ToString());
+ //Log.Information("Actor at {0}", offset.ToString());
if (offset == IntPtr.Zero)
- throw new Exception($"Actor slot at index {index} is invalid");
+ return null;
- var actorStruct = Marshal.PtrToStructure(offset);
+ try {
+ var actorStruct = Marshal.PtrToStructure(offset);
- //Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"),
- // actorStruct.ObjectKind.ToString());
+ //Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"),
+ // actorStruct.ObjectKind.ToString());
- switch (actorStruct.ObjectKind) {
- case ObjectKind.Player: return new PlayerCharacter(actorStruct);
- case ObjectKind.BattleNpc: return new BattleNpc(actorStruct);
- default: return new Actor(actorStruct);
+ switch (actorStruct.ObjectKind)
+ {
+ case ObjectKind.Player: return new PlayerCharacter(actorStruct);
+ case ObjectKind.BattleNpc: return new BattleNpc(actorStruct);
+ default: return new Actor(actorStruct);
+ }
+ } catch (AccessViolationException) {
+ return null;
}
}
}
diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs
index e0e038382..ade048517 100644
--- a/Dalamud/Game/ClientState/ClientState.cs
+++ b/Dalamud/Game/ClientState/ClientState.cs
@@ -32,7 +32,18 @@ namespace Dalamud.Game.ClientState
///
/// The local player character, if one is present.
///
- public PlayerCharacter LocalPlayer { get; private set; }
+ //public PlayerCharacter LocalPlayer { get; private set; }
+ public PlayerCharacter LocalPlayer {
+ get {
+ var actor = this.Actors[0];
+
+ if (actor is PlayerCharacter pc)
+ return pc;
+
+ return null;
+ }
+ }
+ //public PlayerCharacter LocalPlayer => null;
///
/// The content ID of the local character.
@@ -67,8 +78,7 @@ namespace Dalamud.Game.ClientState
}
private void FrameworkOnOnUpdateEvent(Framework framework) {
- LocalPlayer = (PlayerCharacter) this.Actors[0];
- Log.Verbose("FRAMEWORK UPDATE");
+ //LocalPlayer = (PlayerCharacter) this.Actors[0];
}
}
}
diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs
index 885f0b31e..4a5b94782 100644
--- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs
+++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs
@@ -13,9 +13,9 @@ namespace Dalamud.Game.ClientState
public IntPtr JobGaugeData { get; set; }
protected override void Setup64Bit(SigScanner sig) {
- ActorTable = sig.Module.BaseAddress + 0x1C01D90;
+ ActorTable = sig.Module.BaseAddress + 0x1BFBA38;
LocalContentId = sig.Module.BaseAddress + 0x1C2E000;
- JobGaugeData = sig.Module.BaseAddress + 0x1BFD110;
+ JobGaugeData = sig.Module.BaseAddress + 0x1BF8110;
}
}
}
diff --git a/Dalamud/Game/Internal/Framework.cs b/Dalamud/Game/Internal/Framework.cs
index b970ac49a..119b9b2dc 100644
--- a/Dalamud/Game/Internal/Framework.cs
+++ b/Dalamud/Game/Internal/Framework.cs
@@ -90,6 +90,7 @@ namespace Dalamud.Game.Internal {
private bool HandleFrameworkUpdate(IntPtr framework) {
try {
Gui.Chat.UpdateQueue(this);
+ Network.UpdateQueue(this);
} catch (Exception ex) {
Log.Error(ex, "Exception while handling Framework::Update hook.");
}
diff --git a/Dalamud/Game/Internal/Gui/ChatGui.cs b/Dalamud/Game/Internal/Gui/ChatGui.cs
index 4b64f9364..4d254a71c 100644
--- a/Dalamud/Game/Internal/Gui/ChatGui.cs
+++ b/Dalamud/Game/Internal/Gui/ChatGui.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
+using System.Text;
using Dalamud.Game.Chat;
using Dalamud.Game.Internal.Libc;
using Dalamud.Hooking;
@@ -9,9 +10,9 @@ using Serilog;
namespace Dalamud.Game.Internal.Gui {
public sealed class ChatGui : IDisposable {
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- private delegate void PrintMessageDelegate(IntPtr manager, XivChatType chatType, IntPtr senderName,
+ private delegate IntPtr PrintMessageDelegate(IntPtr manager, XivChatType chatType, IntPtr senderName,
IntPtr message,
- uint senderId, byte isLocal);
+ uint senderId, IntPtr parameter);
public delegate void OnMessageDelegate(XivChatType type, uint senderId, string sender, ref string message,
ref bool isHandled);
@@ -78,13 +79,15 @@ namespace Dalamud.Game.Internal.Gui {
}
}
- private void HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage,
- uint senderid, byte isLocal) {
+ private IntPtr HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage,
+ uint senderid, IntPtr parameter) {
+ IntPtr retVal = IntPtr.Zero;
+
try {
var senderName = StdString.ReadFromPointer(pSenderName);
var message = StdString.ReadFromPointer(pMessage);
- //Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(Encoding.UTF8.GetBytes(message))}] {message} from {senderName}");
+ Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(Encoding.UTF8.GetBytes(message)).Replace("-", " ")}] {message} from {senderName}");
var originalMessage = string.Copy(message);
@@ -93,10 +96,10 @@ namespace Dalamud.Game.Internal.Gui {
OnChatMessage?.Invoke(chattype, senderid, senderName, ref message, ref isHandled);
var messagePtr = pMessage;
- OwnedStdString allocatedString = null;
+ OwnedStdString allocatedString = null;
if (originalMessage != message) {
- allocatedString = this.dalamud.Framework.Libc.NewString(message);
+ allocatedString = this.dalamud.Framework.Libc.NewString(Encoding.UTF8.GetBytes(message));
Log.Debug(
$"HandlePrintMessageDetour String modified: {originalMessage}({messagePtr}) -> {message}({allocatedString.Address})");
messagePtr = allocatedString.Address;
@@ -104,7 +107,7 @@ namespace Dalamud.Game.Internal.Gui {
// Print the original chat if it's handled.
if (!isHandled)
- this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, isLocal);
+ retVal = this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, parameter);
if (this.baseAddress == IntPtr.Zero)
this.baseAddress = manager;
@@ -112,8 +115,10 @@ namespace Dalamud.Game.Internal.Gui {
allocatedString?.Dispose();
} catch (Exception ex) {
Log.Error(ex, "Exception on OnChatMessage hook.");
- this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, isLocal);
+ retVal = this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, parameter);
}
+
+ return retVal;
}
///
@@ -127,13 +132,13 @@ namespace Dalamud.Game.Internal.Gui {
public void Print(string message) {
PrintChat(new XivChatEntry {
- Message = message
+ MessageBytes = Encoding.UTF8.GetBytes(message)
});
}
public void PrintError(string message) {
PrintChat(new XivChatEntry {
- Message = message,
+ MessageBytes = Encoding.UTF8.GetBytes(message),
Type = XivChatType.Urgent
});
}
@@ -146,13 +151,15 @@ namespace Dalamud.Game.Internal.Gui {
var chat = this.chatQueue.Dequeue();
var sender = chat.Name ?? "";
- var message = chat.Message ?? "";
+ var message = chat.MessageBytes ?? new byte[0];
if (this.baseAddress != IntPtr.Zero)
- using (var senderVec = framework.Libc.NewString(sender))
- using (var messageVec = framework.Libc.NewString(message)) {
+ using (var senderVec = framework.Libc.NewString(Encoding.UTF8.GetBytes(sender)))
+ using (var messageVec = framework.Libc.NewString(message))
+ {
+ Log.Verbose($"String allocated to {messageVec.Address.ToInt64():X}");
this.printMessageHook.Original(this.baseAddress, chat.Type, senderVec.Address,
- messageVec.Address, chat.SenderId, 0);
+ messageVec.Address, chat.SenderId, chat.Parameters);
}
}
}
diff --git a/Dalamud/Game/Internal/Gui/ChatGuiAddressResolver.cs b/Dalamud/Game/Internal/Gui/ChatGuiAddressResolver.cs
index 0516fce54..974310e18 100644
--- a/Dalamud/Game/Internal/Gui/ChatGuiAddressResolver.cs
+++ b/Dalamud/Game/Internal/Gui/ChatGuiAddressResolver.cs
@@ -86,8 +86,8 @@ namespace Dalamud.Game.Internal.Gui {
//PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 FA F2 B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A");
- //PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 ?? ?? B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A"); 5.0
- PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 7A 12 AF FF 8B C8 EB 1D 0F B6 42 14 8B 4A");
+ //PopulateItemLinkObject = sig.ScanText( "48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 ?? ?? B0 FF 8B C8 EB 1D 0F B6 42 14 8B 4A"); 5.0
+ PopulateItemLinkObject = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 80 7A 06 00 48 8B DA 48 8B F9 74 14 48 8B CA E8 32 03 00 00 48 8B C8 E8 ?? ?? ?? FF 8B C8 EB 1D 0F B6 42 14 8B 4A");
}
}
}
diff --git a/Dalamud/Game/Internal/Gui/IconReplacer.cs b/Dalamud/Game/Internal/Gui/IconReplacer.cs
index 99a54b8bd..9b0a1d0a8 100644
--- a/Dalamud/Game/Internal/Gui/IconReplacer.cs
+++ b/Dalamud/Game/Internal/Gui/IconReplacer.cs
@@ -35,8 +35,8 @@ namespace Dalamud.Game.Internal.Gui {
this.Address.Setup(scanner);
this.byteBase = scanner.Module.BaseAddress;
- this.comboTimer = byteBase + 0x1BB5B50;
- this.lastComboMove = byteBase + 0x1BB5B54;
+ this.comboTimer = byteBase + 0x1BB0B50;
+ this.lastComboMove = byteBase + 0x1BB0B54;
CustomIDs = new HashSet();
VanillaIDs = new HashSet();
@@ -99,7 +99,7 @@ namespace Dalamud.Game.Internal.Gui {
// TODO: this is currently broken
// As it stands, don't rely on localCharacter.level for anything.
- var localPlayer = this.dalamud.ClientState.LocalPlayer;
+ //var localPlayer = this.dalamud.ClientState.LocalPlayer;
// Don't clutter the spaghetti any worse than it already is.
var lastMove = Marshal.ReadInt32(this.lastComboMove);
diff --git a/Dalamud/Game/Internal/Libc/LibcFunction.cs b/Dalamud/Game/Internal/Libc/LibcFunction.cs
index 93e7fcebb..88261c3f7 100644
--- a/Dalamud/Game/Internal/Libc/LibcFunction.cs
+++ b/Dalamud/Game/Internal/Libc/LibcFunction.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Runtime.InteropServices;
using Serilog;
@@ -6,7 +6,7 @@ namespace Dalamud.Game.Internal.Libc {
public sealed class LibcFunction {
// TODO: prolly callconv is not okay in x86
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- private delegate IntPtr StdStringFromCStringDelegate(IntPtr pStdString, [MarshalAs(UnmanagedType.LPUTF8Str)]string content, IntPtr size);
+ private delegate IntPtr StdStringFromCStringDelegate(IntPtr pStdString, [MarshalAs(UnmanagedType.LPArray)]byte[] content, IntPtr size);
// TODO: prolly callconv is not okay in x86
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
@@ -25,7 +25,7 @@ namespace Dalamud.Game.Internal.Libc {
this.stdStringDeallocate = Marshal.GetDelegateForFunctionPointer(Address.StdStringDeallocate);
}
- public OwnedStdString NewString(string content) {
+ public OwnedStdString NewString(byte[] content) {
Log.Verbose("Allocating");
// While 0x70 bytes in the memory should be enough in DX11 version,
@@ -36,7 +36,7 @@ namespace Dalamud.Game.Internal.Libc {
var npos = new IntPtr(0xFFFFFFFF); // assumed to be -1 (0xFFFFFFFF in x86, 0xFFFFFFFF_FFFFFFFF in amd64)
var pReallocString = this.stdStringCtorCString(pString, content, npos);
- Log.Verbose("Prev: {Prev} Now: {Now}", pString, pReallocString);
+ //Log.Verbose("Prev: {Prev} Now: {Now}", pString, pReallocString);
return new OwnedStdString(pReallocString, DeallocateStdString);
}
diff --git a/Dalamud/Game/Internal/Network/GameNetwork.cs b/Dalamud/Game/Internal/Network/GameNetwork.cs
index 25acfa562..a2093e1cd 100644
--- a/Dalamud/Game/Internal/Network/GameNetwork.cs
+++ b/Dalamud/Game/Internal/Network/GameNetwork.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Dalamud.Hooking;
using Serilog;
@@ -11,6 +13,7 @@ namespace Dalamud.Game.Internal.Network {
private readonly Hook processZonePacketHook;
private GameNetworkAddressResolver Address { get; }
+ private IntPtr baseAddress;
public delegate void OnZonePacketDelegate(IntPtr dataPtr);
@@ -18,6 +21,8 @@ namespace Dalamud.Game.Internal.Network {
private readonly Dalamud dalamud;
+ private readonly Queue zoneInjectQueue = new Queue();
+
public GameNetwork(Dalamud dalamud, SigScanner scanner) {
this.dalamud = dalamud;
Address = new GameNetworkAddressResolver();
@@ -40,6 +45,8 @@ namespace Dalamud.Game.Internal.Network {
}
private void ProcessZonePacketDetour(IntPtr a, IntPtr b, IntPtr dataPtr) {
+ this.baseAddress = a;
+
// Call events
this.OnZonePacket?.Invoke(dataPtr);
@@ -60,5 +67,47 @@ namespace Dalamud.Game.Internal.Network {
this.processZonePacketHook.Original(a, b, dataPtr);
}
}
+
+ private void InjectZoneProtoPacket(byte[] data) {
+ this.zoneInjectQueue.Enqueue(data);
+ }
+
+ private void InjectActorControl(short cat, int param1) {
+ var packetData = new byte[] {
+ 0x14, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x17, 0x7C, 0xC5, 0x5D, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x48, 0xB2, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x43, 0x7F, 0x00, 0x00
+ };
+
+ BitConverter.GetBytes((short) cat).CopyTo(packetData, 0x10);
+
+ BitConverter.GetBytes((UInt32) param1).CopyTo(packetData, 0x14);
+
+ InjectZoneProtoPacket(packetData);
+ }
+
+ public void InjectBgmTest(int key) {
+ InjectActorControl(0xa1, key);
+ }
+
+ ///
+ /// Process a chat queue.
+ ///
+ public void UpdateQueue(Framework framework)
+ {
+ while (this.zoneInjectQueue.Count > 0)
+ {
+ var packetData = this.zoneInjectQueue.Dequeue();
+
+ var unmanagedPacketData = Marshal.AllocHGlobal(packetData.Length);
+ Marshal.Copy(packetData, 0, unmanagedPacketData, packetData.Length);
+
+ if (this.baseAddress != IntPtr.Zero) {
+ this.processZonePacketHook.Original(this.baseAddress, IntPtr.Zero, unmanagedPacketData);
+ }
+
+ Marshal.FreeHGlobal(unmanagedPacketData);
+ }
+ }
}
}
diff --git a/Dalamud/Game/Network/MarketBoardUploaders/IMarketBoardUploader.cs b/Dalamud/Game/Network/MarketBoardUploaders/IMarketBoardUploader.cs
index aa8eed3ec..cd675d865 100644
--- a/Dalamud/Game/Network/MarketBoardUploaders/IMarketBoardUploader.cs
+++ b/Dalamud/Game/Network/MarketBoardUploaders/IMarketBoardUploader.cs
@@ -1,5 +1,8 @@
+using Dalamud.Game.Network.Structures;
+
namespace Dalamud.Game.Network.MarketBoardUploaders {
internal interface IMarketBoardUploader {
void Upload(MarketBoardItemRequest itemRequest);
+ void UploadTax(MarketTaxRates taxRates);
}
}
diff --git a/Dalamud/Game/Network/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs b/Dalamud/Game/Network/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs
index c138d20fb..83c657b14 100644
--- a/Dalamud/Game/Network/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs
+++ b/Dalamud/Game/Network/MarketBoardUploaders/Universalis/UniversalisMarketBoardUploader.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Net;
using Dalamud.Game.Network.MarketBoardUploaders;
using Dalamud.Game.Network.MarketBoardUploaders.Universalis;
+using Dalamud.Game.Network.Structures;
using Newtonsoft.Json;
using Serilog;
@@ -86,5 +87,31 @@ namespace Dalamud.Game.Network.Universalis.MarketBoardUploaders {
Log.Verbose("Universalis data upload for item#{0} completed.", request.CatalogId);
}
}
+
+ public void UploadTax(MarketTaxRates taxRates) {
+ using (var client = new WebClient())
+ {
+ var taxRatesRequest = new UniversalisTaxUploadRequest();
+ taxRatesRequest.WorldId = this.dalamud.ClientState.LocalPlayer.CurrentWorld.Id;
+ taxRatesRequest.UploaderId = this.dalamud.ClientState.LocalContentId;
+
+ taxRatesRequest.TaxData = new UniversalisTaxData {
+ LimsaLominsa = taxRates.LimsaLominsaTax,
+ Gridania = taxRates.GridaniaTax,
+ Uldah = taxRates.UldahTax,
+ Ishgard = taxRates.IshgardTax,
+ Kugane = taxRates.KuganeTax,
+ Crystarium = taxRates.CrystariumTax
+ };
+
+ client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
+
+ var historyUpload = JsonConvert.SerializeObject(taxRatesRequest);
+ client.UploadString(ApiBase + $"/upload/{ApiKey}", "POST", historyUpload);
+ Log.Verbose(historyUpload);
+
+ Log.Verbose("Universalis tax upload completed.");
+ }
+ }
}
}
diff --git a/Dalamud/Game/Network/MarketBoardUploaders/Universalis/UniversalisTaxUploadRequest.cs b/Dalamud/Game/Network/MarketBoardUploaders/Universalis/UniversalisTaxUploadRequest.cs
new file mode 100644
index 000000000..0e142c17c
--- /dev/null
+++ b/Dalamud/Game/Network/MarketBoardUploaders/Universalis/UniversalisTaxUploadRequest.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace Dalamud.Game.Network.MarketBoardUploaders.Universalis
+{
+ class UniversalisTaxUploadRequest
+ {
+ [JsonProperty("uploaderID")]
+ public ulong UploaderId { get; set; }
+
+ [JsonProperty("worldID")]
+ public int WorldId { get; set; }
+
+ [JsonProperty("marketTaxRates")]
+ public UniversalisTaxData TaxData { get; set; }
+ }
+
+ class UniversalisTaxData {
+ [JsonProperty("limsaLominsa")]
+ public uint LimsaLominsa { get; set; }
+
+ [JsonProperty("gridania")]
+ public uint Gridania { get; set; }
+
+ [JsonProperty("uldah")]
+ public uint Uldah { get; set; }
+
+ [JsonProperty("ishgard")]
+ public uint Ishgard { get; set; }
+
+ [JsonProperty("kugane")]
+ public uint Kugane { get; set; }
+
+ [JsonProperty("crystarium")]
+ public uint Crystarium { get; set; }
+ }
+}
diff --git a/Dalamud/Game/Network/NetworkHandlers.cs b/Dalamud/Game/Network/NetworkHandlers.cs
index ba3a636ac..5f9ad6a9a 100644
--- a/Dalamud/Game/Network/NetworkHandlers.cs
+++ b/Dalamud/Game/Network/NetworkHandlers.cs
@@ -29,6 +29,7 @@ namespace Dalamud.Game.Network {
private void OnZonePacket(IntPtr dataPtr) {
var opCode = (ZoneOpCode) Marshal.ReadInt16(dataPtr, 2);
+ /*
if (opCode == ZoneOpCode.RetainerSaleItemId) {
var itemId = Marshal.ReadInt32(dataPtr + 16);
var amount = Marshal.ReadByte(dataPtr + 32);
@@ -58,22 +59,25 @@ namespace Dalamud.Game.Network {
return;
}
+ */
- if (opCode == ZoneOpCode.CfNotify) {
+ if (opCode == ZoneOpCode.CfNotifyPop) {
var data = new byte[64];
Marshal.Copy(dataPtr, data, 0, 64);
var notifyType = data[16];
- var contentFinderConditionId = BitConverter.ToInt16(data, 28);
+ var contentFinderConditionId = BitConverter.ToInt16(data, 36);
Task.Run(async () => {
- if (notifyType != 2 || contentFinderConditionId == 0)
+ if (notifyType != 3 || contentFinderConditionId == 0)
return;
var contentFinderCondition =
await XivApi.GetContentFinderCondition(contentFinderConditionId);
+ this.dalamud.Framework.Gui.Chat.Print($"Duty pop: " + contentFinderCondition["Name"]);
+
if (this.dalamud.BotManager.IsConnected)
await this.dalamud.BotManager.ProcessCfPop(contentFinderCondition);
});
@@ -173,17 +177,35 @@ namespace Dalamud.Game.Network {
Log.Verbose("Added history for item#{0}", listing.CatalogId);
}
+
+ if (opCode == ZoneOpCode.MarketTaxRates)
+ {
+ var taxes = MarketTaxRates.Read(dataPtr + 0x10);
+
+ Log.Verbose("MarketTaxRates: limsa#{0} grid#{1} uldah#{2} ish#{3} kugane#{4} cr#{5}",
+ taxes.LimsaLominsaTax, taxes.GridaniaTax, taxes.UldahTax, taxes.IshgardTax, taxes.KuganeTax, taxes.CrystariumTax);
+ try
+ {
+ Task.Run(() => this.uploader.UploadTax(taxes));
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Market Board data upload failed.");
+ }
+ }
}
}
private enum ZoneOpCode {
+ CfNotifyPop = 0x32D,
CfNotify = 0x8F,
RetainerSaleItemId = 0x13F, // TODO these are probably not accurate
RetainerSaleFinish = 0x138,
FateSpawn = 0x226,
- MarketBoardItemRequestStart = 0x39D,
- MarketBoardOfferings = 0x36A,
- MarketBoardHistory = 0x194
+ MarketTaxRates = 0x39F,
+ MarketBoardItemRequestStart = 0xF2,
+ MarketBoardOfferings = 0x1E2,
+ MarketBoardHistory = 0x123
}
}
}
diff --git a/Dalamud/Game/Network/Structures/MarketBoardCurrentOfferings.cs b/Dalamud/Game/Network/Structures/MarketBoardCurrentOfferings.cs
index cf65797c4..7d9f72cc8 100644
--- a/Dalamud/Game/Network/Structures/MarketBoardCurrentOfferings.cs
+++ b/Dalamud/Game/Network/Structures/MarketBoardCurrentOfferings.cs
@@ -3,22 +3,28 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
-namespace Dalamud.Game.Network.Structures {
- public class MarketBoardCurrentOfferings {
+namespace Dalamud.Game.Network.Structures
+{
+ public class MarketBoardCurrentOfferings
+ {
public List ItemListings;
public int ListingIndexEnd;
public int ListingIndexStart;
public int RequestId;
- public static unsafe MarketBoardCurrentOfferings Read(IntPtr dataPtr) {
+ public static unsafe MarketBoardCurrentOfferings Read(IntPtr dataPtr)
+ {
var output = new MarketBoardCurrentOfferings();
- using (var stream = new UnmanagedMemoryStream((byte*) dataPtr.ToPointer(), 1544)) {
- using (var reader = new BinaryReader(stream)) {
+ using (var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544))
+ {
+ using (var reader = new BinaryReader(stream))
+ {
output.ItemListings = new List();
- for (var i = 0; i < 10; i++) {
+ for (var i = 0; i < 10; i++)
+ {
var listingEntry = new MarketBoardItemListing();
listingEntry.ListingId = reader.ReadUInt64();
@@ -38,7 +44,8 @@ namespace Dalamud.Game.Network.Structures {
listingEntry.Materia = new List();
- for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++) {
+ for (var materiaIndex = 0; materiaIndex < 5; materiaIndex++)
+ {
var materiaVal = reader.ReadUInt16();
var materiaEntry = new MarketBoardItemListing.ItemMateria();
@@ -76,7 +83,8 @@ namespace Dalamud.Game.Network.Structures {
return output;
}
- public class MarketBoardItemListing {
+ public class MarketBoardItemListing
+ {
public ulong ArtisanId;
public uint CatalogId;
public bool IsHq;
@@ -97,7 +105,8 @@ namespace Dalamud.Game.Network.Structures {
public int StainId;
public uint TotalTax;
- public class ItemMateria {
+ public class ItemMateria
+ {
public int Index;
public int MateriaId;
}
diff --git a/Dalamud/Game/Network/Structures/MarketTaxRate.cs b/Dalamud/Game/Network/Structures/MarketTaxRate.cs
new file mode 100644
index 000000000..b0ce2772c
--- /dev/null
+++ b/Dalamud/Game/Network/Structures/MarketTaxRate.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Text;
+
+namespace Dalamud.Game.Network.Structures
+{
+ public class MarketTaxRates
+ {
+ public uint LimsaLominsaTax;
+ public uint GridaniaTax;
+ public uint UldahTax;
+ public uint IshgardTax;
+ public uint KuganeTax;
+ public uint CrystariumTax;
+
+
+ public static unsafe MarketTaxRates Read(IntPtr dataPtr)
+ {
+ var output = new MarketTaxRates();
+
+ using (var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544))
+ {
+ using (var reader = new BinaryReader(stream))
+ {
+ stream.Position += 8;
+
+ output.LimsaLominsaTax = reader.ReadUInt32();
+ output.GridaniaTax = reader.ReadUInt32();
+ output.UldahTax = reader.ReadUInt32();
+ output.IshgardTax = reader.ReadUInt32();
+ output.KuganeTax = reader.ReadUInt32();
+ output.CrystariumTax = reader.ReadUInt32();
+ }
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/Dalamud/Game/Network/WinSockHandlers.cs b/Dalamud/Game/Network/WinSockHandlers.cs
new file mode 100644
index 000000000..f2d9d9706
--- /dev/null
+++ b/Dalamud/Game/Network/WinSockHandlers.cs
@@ -0,0 +1,55 @@
+using Dalamud.Hooking;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Dalamud.Game
+{
+ internal sealed class WinSockHandlers : IDisposable
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Winapi)]
+ private delegate IntPtr SocketDelegate(int af, int type, int protocol);
+ private Hook ws2SocketHook;
+
+ [DllImport("ws2_32.dll", CallingConvention = CallingConvention.Winapi)]
+ private static extern int setsockopt(IntPtr socket, SocketOptionLevel level, SocketOptionName optName, ref IntPtr optVal, int optLen);
+
+ public WinSockHandlers() {
+ this.ws2SocketHook = Hook.FromSymbol("ws2_32.dll", "socket", new SocketDelegate(OnSocket));
+ this.ws2SocketHook.Enable();
+ }
+
+ private IntPtr OnSocket(int af, int type, int protocol)
+ {
+ var socket = this.ws2SocketHook.Original(af, type, protocol);
+
+ // IPPROTO_TCP
+ if (type == 1)
+ {
+ // INVALID_SOCKET
+ if (socket != new IntPtr(-1))
+ {
+ // In case you're not aware of it: (albeit you should)
+ // https://linux.die.net/man/7/tcp
+ // https://assets.extrahop.com/whitepapers/TCP-Optimization-Guide-by-ExtraHop.pdf
+ var value = new IntPtr(1);
+ setsockopt(socket, SocketOptionLevel.Tcp, SocketOptionName.NoDelay, ref value, 4);
+
+ // Enable tcp_quickack option. This option is undocumented in MSDN but it is supported in Windows 7 and onwards.
+ value = new IntPtr(1);
+ setsockopt(socket, SocketOptionLevel.Tcp, (SocketOptionName)12, ref value, 4);
+ }
+ }
+
+ return socket;
+ }
+
+ public void Dispose() {
+ ws2SocketHook.Dispose();
+ }
+ }
+}
diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs
index 4101d5912..c6311a79d 100644
--- a/Dalamud/Hooking/Hook.cs
+++ b/Dalamud/Hooking/Hook.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using EasyHook;
@@ -9,7 +9,7 @@ namespace Dalamud.Hooking {
/// This class is basically a thin wrapper around the LocalHook type to provide helper functions.
///
/// Delegate type to represents a function prototype. This must be the same prototype as original function do.
- public sealed class Hook : IDisposable where T : class {
+ public sealed class Hook : IDisposable where T : Delegate {
private bool isDisposed;
private readonly IntPtr address;