mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-10 18:14:36 +01:00
Merge branch 'master' of https://github.com/goaaats/dalamud
This commit is contained in:
commit
9836db6b90
25 changed files with 521 additions and 129 deletions
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Target">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TargetFramework>net471</TargetFramework>
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Build">
|
||||
|
|
@ -14,10 +14,13 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Label="Feature">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>2.4.0.0</AssemblyVersion>
|
||||
<FileVersion>2.4.0.0</FileVersion>
|
||||
<AssemblyVersion>4.1.0.3</AssemblyVersion>
|
||||
<FileVersion>4.1.0.3</FileVersion>
|
||||
<Description>XIVLauncher addon injection</Description>
|
||||
<Version>2.4.0</Version>
|
||||
<Version>4.1.0.3</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DocumentationFile></DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EasyHook" Version="2.7.6270" />
|
||||
|
|
@ -26,4 +29,4 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud\Dalamud.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <BGM ID>"
|
||||
});
|
||||
|
||||
CommandManager.AddHandler("/xlitem", new CommandInfo(OnItemLinkCommand)
|
||||
{
|
||||
HelpMessage = "Link an item by name. Usage: /item <Item name>"
|
||||
});
|
||||
}
|
||||
|
||||
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<CustomComboPreset>()) {
|
||||
if (value == CustomComboPreset.None)
|
||||
|
|
@ -394,7 +413,7 @@ namespace Dalamud {
|
|||
}
|
||||
}
|
||||
break;
|
||||
*/
|
||||
|
||||
case "list": {
|
||||
foreach (var value in Enum.GetValues(typeof(CustomComboPreset)).Cast<CustomComboPreset>()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup Label="Target">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<TargetFramework>net471</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Build">
|
||||
|
|
@ -14,9 +14,9 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup Label="Feature">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<AssemblyVersion>2.2.0.0</AssemblyVersion>
|
||||
<Version>2.2.0</Version>
|
||||
<FileVersion>2.2.0.0</FileVersion>
|
||||
<AssemblyVersion>4.1.0.3</AssemblyVersion>
|
||||
<Version>4.1.0.3</Version>
|
||||
<FileVersion>4.1.0.3</FileVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<None Include="$(SolutionDir)/Resources/**/*" CopyToOutputDirectory="PreserveNewest" Visible="false" />
|
||||
|
|
@ -68,4 +68,4 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="Configuration\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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<IMessageChannel> 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() {
|
||||
|
|
|
|||
|
|
@ -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<ChatTypeConfiguration> ChatTypeConfigurations { get; set; }
|
||||
|
||||
public ChannelConfiguration CfNotificationChannel { get; set; }
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Structs.Actor>(offset);
|
||||
try {
|
||||
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,18 @@ namespace Dalamud.Game.ClientState
|
|||
/// <summary>
|
||||
/// The local player character, if one is present.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<uint>();
|
||||
VanillaIDs = new HashSet<uint>();
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<StdStringDeallocateDelegate>(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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ProcessZonePacketDelegate> 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<byte[]> zoneInjectQueue = new Queue<byte[]>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a chat queue.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<MarketBoardItemListing> 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<MarketBoardItemListing>();
|
||||
|
||||
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<MarketBoardItemListing.ItemMateria>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
41
Dalamud/Game/Network/Structures/MarketTaxRate.cs
Normal file
41
Dalamud/Game/Network/Structures/MarketTaxRate.cs
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Dalamud/Game/Network/WinSockHandlers.cs
Normal file
55
Dalamud/Game/Network/WinSockHandlers.cs
Normal file
|
|
@ -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<SocketDelegate> 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<SocketDelegate>.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Delegate type to represents a function prototype. This must be the same prototype as original function do.</typeparam>
|
||||
public sealed class Hook<T> : IDisposable where T : class {
|
||||
public sealed class Hook<T> : IDisposable where T : Delegate {
|
||||
private bool isDisposed;
|
||||
|
||||
private readonly IntPtr address;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue