mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Initial pieces of payload rework; everything is read-only at the moment, pending next stages. Lumina is now used for on-demand property resolution, properties are only exposed for user-facing values, and mostly expose the lumina object(s) where applicable
This commit is contained in:
parent
22e9eb89b8
commit
f2cee5f5bc
17 changed files with 355 additions and 253 deletions
|
|
@ -12,6 +12,7 @@ using Dalamud.Data;
|
|||
using Dalamud.DiscordBot;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Game.Chat;
|
||||
using Dalamud.Game.Chat.SeStringHandling;
|
||||
using Dalamud.Game.ClientState;
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Game.ClientState.Actors.Types.NonPlayer;
|
||||
|
|
@ -103,6 +104,9 @@ namespace Dalamud {
|
|||
this.Data = new DataManager(this.StartInfo.Language);
|
||||
this.Data.Initialize();
|
||||
|
||||
// TODO: better way to do this? basically for lumina injection
|
||||
SeString.Dalamud = this;
|
||||
|
||||
this.ClientState = new ClientState(this, info, this.SigScanner);
|
||||
|
||||
this.BotManager = new DiscordBotManager(this, this.Configuration.DiscordFeatureConfig);
|
||||
|
|
|
|||
53
Dalamud/Data/TransientSheet/Completion.cs
Normal file
53
Dalamud/Data/TransientSheet/Completion.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using Lumina.Excel;
|
||||
|
||||
namespace Dalamud.Data.TransientSheet
|
||||
{
|
||||
[Sheet( "Completion", columnHash: 0x2e6c55a3 )]
|
||||
public class Completion : IExcelRow
|
||||
{
|
||||
// column defs from Mon, 02 Mar 2020 11:00:20 GMT
|
||||
|
||||
|
||||
// col: 03 offset: 0000
|
||||
public string Text;
|
||||
|
||||
// col: 04 offset: 0004
|
||||
public string GroupTitle;
|
||||
|
||||
// col: 02 offset: 0008
|
||||
public string LookupTable;
|
||||
|
||||
// col: 00 offset: 000c
|
||||
public ushort Group;
|
||||
|
||||
// col: 01 offset: 000e
|
||||
public ushort Key;
|
||||
|
||||
|
||||
public int RowId { get; set; }
|
||||
public int SubRowId { get; set; }
|
||||
|
||||
public void PopulateData( RowParser parser, Lumina.Lumina lumina )
|
||||
{
|
||||
RowId = parser.Row;
|
||||
SubRowId = parser.SubRow;
|
||||
|
||||
// col: 3 offset: 0000
|
||||
Text = parser.ReadOffset< string >( 0x0 );
|
||||
|
||||
// col: 4 offset: 0004
|
||||
GroupTitle = parser.ReadOffset< string >( 0x4 );
|
||||
|
||||
// col: 2 offset: 0008
|
||||
LookupTable = parser.ReadOffset< string >( 0x8 );
|
||||
|
||||
// col: 0 offset: 000c
|
||||
Group = parser.ReadOffset< ushort >( 0xc );
|
||||
|
||||
// col: 1 offset: 000e
|
||||
Key = parser.ReadOffset< ushort >( 0xe );
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,10 +205,8 @@ namespace Dalamud.DiscordBot {
|
|||
|
||||
senderWorld = this.dalamud.ClientState.LocalPlayer.HomeWorld.GameData.Name;
|
||||
} else {
|
||||
playerLink.Resolve();
|
||||
|
||||
senderName = wasOutgoingTell ? this.dalamud.ClientState.LocalPlayer.Name : playerLink.PlayerName;
|
||||
senderWorld = playerLink.ServerName;
|
||||
senderWorld = playerLink.World.Name;
|
||||
}
|
||||
|
||||
var rawMessage = SeString.Parse(message.RawData).TextValue;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Chat.SeStringHandling.Payloads;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -11,6 +12,7 @@ using Serilog;
|
|||
// - lumina DI
|
||||
// - design for handling raw values vs resolved values, both for input and output
|
||||
// - wrapper class(es) for handling of composite links in chat (item, map etc) and formatting operations
|
||||
// - add italics payload
|
||||
|
||||
namespace Dalamud.Game.Chat.SeStringHandling
|
||||
{
|
||||
|
|
@ -21,12 +23,23 @@ namespace Dalamud.Game.Chat.SeStringHandling
|
|||
{
|
||||
public abstract PayloadType Type { get; }
|
||||
|
||||
public abstract void Resolve();
|
||||
|
||||
public abstract byte[] Encode();
|
||||
|
||||
protected abstract void ProcessChunkImpl(BinaryReader reader, long endOfStream);
|
||||
|
||||
// :(
|
||||
protected DataManager dataResolver;
|
||||
|
||||
public Payload()
|
||||
{
|
||||
// this is not a good way to do this, but I don't want to have to include a dalamud
|
||||
// reference on multiple methods in every payload class
|
||||
// We could also just directly reference this static where we use it, but this at least
|
||||
// allows for more easily changing how this is injected later, without affecting code
|
||||
// that makes use of it
|
||||
this.dataResolver = SeString.Dalamud.Data;
|
||||
}
|
||||
|
||||
public static Payload Process(BinaryReader reader)
|
||||
{
|
||||
Payload payload = null;
|
||||
|
|
@ -78,10 +91,13 @@ namespace Dalamud.Game.Chat.SeStringHandling
|
|||
case EmbeddedInfoType.LinkTerminator:
|
||||
// this has no custom handling and so needs to fallthrough to ensure it is captured
|
||||
default:
|
||||
Log.Verbose("Unhandled EmbeddedInfoType: {0}", subType);
|
||||
// but I'm also tired of this log
|
||||
if (subType != EmbeddedInfoType.LinkTerminator)
|
||||
{
|
||||
Log.Verbose("Unhandled EmbeddedInfoType: {0}", subType);
|
||||
}
|
||||
// rewind so we capture the Interactable byte in the raw data
|
||||
reader.BaseStream.Seek(-1, SeekOrigin.Current);
|
||||
payload = new RawPayload((byte)chunkType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -101,11 +117,11 @@ namespace Dalamud.Game.Chat.SeStringHandling
|
|||
|
||||
default:
|
||||
Log.Verbose("Unhandled SeStringChunkType: {0}", chunkType);
|
||||
payload = new RawPayload((byte)chunkType);
|
||||
break;
|
||||
}
|
||||
|
||||
payload?.ProcessChunkImpl(reader, reader.BaseStream.Position + chunkLen - 1);
|
||||
payload ??= new RawPayload((byte)chunkType);
|
||||
payload.ProcessChunkImpl(reader, reader.BaseStream.Position + chunkLen - 1);
|
||||
|
||||
// read through the rest of the packet
|
||||
var readBytes = (uint)(reader.BaseStream.Position - packetStart);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
using Dalamud.Data.TransientSheet;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
using System.Linq;
|
||||
|
||||
namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
||||
{
|
||||
|
|
@ -9,78 +12,29 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.AutoTranslateText;
|
||||
|
||||
public uint Group { get; set; }
|
||||
|
||||
public uint Key { get; set; }
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
public override void Resolve()
|
||||
private string text;
|
||||
public string Text
|
||||
{
|
||||
// TODO: fixup once lumina DI is in
|
||||
|
||||
//if (string.IsNullOrEmpty(Text))
|
||||
//{
|
||||
// var sheet = dalamud.Data.GetExcelSheet<Completion>();
|
||||
|
||||
// Completion row = null;
|
||||
// try
|
||||
// {
|
||||
// // try to get the row in the Completion table itself, because this is 'easiest'
|
||||
// // The row may not exist at all (if the Key is for another table), or it could be the wrong row
|
||||
// // (again, if it's meant for another table)
|
||||
// row = sheet.GetRow(Key);
|
||||
// }
|
||||
// catch {} // don't care, row will be null
|
||||
|
||||
// if (row?.Group == Group)
|
||||
// {
|
||||
// // if the row exists in this table and the group matches, this is actually the correct data
|
||||
// Text = $"{{ {row.Text} }} ";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Log.Verbose("row mismatch");
|
||||
// try
|
||||
// {
|
||||
// // we need to get the linked table and do the lookup there instead
|
||||
// // in this case, there will only be one entry for this group id
|
||||
// row = sheet.GetRows().First(r => r.Group == Group);
|
||||
// // many of the names contain valid id ranges after the table name, but we don't need those
|
||||
// var actualTableName = row.LookupTable.Split('[')[0];
|
||||
|
||||
// var name = actualTableName switch
|
||||
// {
|
||||
// // TODO: rest of xref'd tables
|
||||
// "Action" => dalamud.Data.GetExcelSheet<Data.TransientSheet.Action>().GetRow(Key).Name,
|
||||
// "ClassJob" => dalamud.Data.GetExcelSheet<ClassJob>().GetRow(Key).Name,
|
||||
// "CraftAction" => dalamud.Data.GetExcelSheet<CraftAction>().GetRow(Key).Name,
|
||||
// "Mount" => dalamud.Data.GetExcelSheet<Mount>().GetRow(Key).Singular,
|
||||
// "PlaceName" => dalamud.Data.GetExcelSheet<PlaceName>().GetRow(Key).Name,
|
||||
// "Race" => dalamud.Data.GetExcelSheet<Race>().GetRow(Key).Masculine,
|
||||
// _ => throw new Exception(actualTableName)
|
||||
// };
|
||||
|
||||
// Text = $"{{ {name} }} ";
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// Log.Error(e, $"AutoTranslatePayload - failed to resolve: {this}");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
get
|
||||
{
|
||||
this.text ??= Resolve();
|
||||
return this.text;
|
||||
}
|
||||
}
|
||||
|
||||
private uint group;
|
||||
private uint key;
|
||||
|
||||
public override byte[] Encode()
|
||||
{
|
||||
var keyBytes = MakeInteger(Key);
|
||||
var keyBytes = MakeInteger(this.key);
|
||||
|
||||
var chunkLen = keyBytes.Length + 2;
|
||||
var bytes = new List<byte>()
|
||||
{
|
||||
START_BYTE,
|
||||
(byte)SeStringChunkType.AutoTranslateKey, (byte)chunkLen,
|
||||
(byte)Group
|
||||
(byte)this.group
|
||||
};
|
||||
bytes.AddRange(keyBytes);
|
||||
bytes.Add(END_BYTE);
|
||||
|
|
@ -90,16 +44,83 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - Group: {Group}, Key: {Key}, Text: {Text}";
|
||||
return $"{Type} - Group: {group}, Key: {key}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
// this seems to always be a bare byte, and not following normal integer encoding
|
||||
// the values in the table are all <70 so this is presumably ok
|
||||
Group = reader.ReadByte();
|
||||
this.group = reader.ReadByte();
|
||||
|
||||
Key = GetInteger(reader);
|
||||
this.key = GetInteger(reader);
|
||||
}
|
||||
|
||||
private string Resolve()
|
||||
{
|
||||
string value = null;
|
||||
|
||||
var sheet = this.dataResolver.GetExcelSheet<Completion>();
|
||||
|
||||
Completion row = null;
|
||||
try
|
||||
{
|
||||
// try to get the row in the Completion table itself, because this is 'easiest'
|
||||
// The row may not exist at all (if the Key is for another table), or it could be the wrong row
|
||||
// (again, if it's meant for another table)
|
||||
row = sheet.GetRow((int)this.key);
|
||||
}
|
||||
catch { } // don't care, row will be null
|
||||
|
||||
if (row?.Group == this.group)
|
||||
{
|
||||
// if the row exists in this table and the group matches, this is actually the correct data
|
||||
value = row.Text;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// we need to get the linked table and do the lookup there instead
|
||||
// in this case, there will only be one entry for this group id
|
||||
row = sheet.GetRows().First(r => r.Group == this.group);
|
||||
// many of the names contain valid id ranges after the table name, but we don't need those
|
||||
var actualTableName = row.LookupTable.Split('[')[0];
|
||||
|
||||
var ikey = (int)this.key;
|
||||
|
||||
var name = actualTableName switch
|
||||
{
|
||||
"Action" => this.dataResolver.GetExcelSheet<Lumina.Excel.GeneratedSheets.Action>().GetRow(ikey).Name,
|
||||
"ActionComboRoute" => this.dataResolver.GetExcelSheet<ActionComboRoute>().GetRow(ikey).Name,
|
||||
"BuddyAction" => this.dataResolver.GetExcelSheet<BuddyAction>().GetRow(ikey).Name,
|
||||
"ClassJob" => this.dataResolver.GetExcelSheet<ClassJob>().GetRow(ikey).Name,
|
||||
"Companion" => this.dataResolver.GetExcelSheet<Companion>().GetRow(ikey).Singular,
|
||||
"CraftAction" => this.dataResolver.GetExcelSheet<CraftAction>().GetRow(ikey).Name,
|
||||
"GeneralAction" => this.dataResolver.GetExcelSheet<GeneralAction>().GetRow(ikey).Name,
|
||||
"GuardianDeity" => this.dataResolver.GetExcelSheet<GuardianDeity>().GetRow(ikey).Name,
|
||||
"MainCommand" => this.dataResolver.GetExcelSheet<MainCommand>().GetRow(ikey).Name,
|
||||
"Mount" => this.dataResolver.GetExcelSheet<Mount>().GetRow(ikey).Singular,
|
||||
"Pet" => this.dataResolver.GetExcelSheet<Pet>().GetRow(ikey).Name,
|
||||
"PetAction" => this.dataResolver.GetExcelSheet<PetAction>().GetRow(ikey).Name,
|
||||
// TODO: lumina doesn't have PetMirage
|
||||
"PlaceName" => this.dataResolver.GetExcelSheet<PlaceName>().GetRow(ikey).Name,
|
||||
"Race" => this.dataResolver.GetExcelSheet<Race>().GetRow(ikey).Masculine,
|
||||
"TextCommand" => this.dataResolver.GetExcelSheet<TextCommand>().GetRow(ikey).Command,
|
||||
"Tribe" => this.dataResolver.GetExcelSheet<Tribe>().GetRow(ikey).Masculine,
|
||||
"Weather" => this.dataResolver.GetExcelSheet<Weather>().GetRow(ikey).Name,
|
||||
_ => throw new Exception(actualTableName)
|
||||
};
|
||||
|
||||
value = name;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, $"AutoTranslatePayload - failed to resolve: {this}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
using Dalamud.Data.TransientSheet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
||||
{
|
||||
|
|
@ -11,38 +11,34 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.Item;
|
||||
|
||||
public uint ItemId { get; private set; }
|
||||
public string ItemName { get; private set; } = string.Empty;
|
||||
public bool IsHQ { get; private set; } = false;
|
||||
|
||||
public ItemPayload() { }
|
||||
|
||||
public ItemPayload(uint itemId, bool isHQ)
|
||||
private Item item;
|
||||
public Item Item
|
||||
{
|
||||
ItemId = itemId;
|
||||
IsHQ = isHQ;
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
if (string.IsNullOrEmpty(ItemName))
|
||||
get
|
||||
{
|
||||
dynamic item = XivApi.GetItem((int)ItemId).GetAwaiter().GetResult();
|
||||
ItemName = item.Name;
|
||||
this.item ??= this.dataResolver.GetExcelSheet<Item>().GetRow((int)this.itemId);
|
||||
return this.item;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsHQ { get; private set; } = false;
|
||||
|
||||
private uint itemId;
|
||||
// mainly to allow overriding the name (for things like owo)
|
||||
private string displayName;
|
||||
|
||||
|
||||
public override byte[] Encode()
|
||||
{
|
||||
var actualItemId = IsHQ ? ItemId + 1000000 : ItemId;
|
||||
var actualItemId = IsHQ ? this.itemId + 1000000 : this.itemId;
|
||||
var idBytes = MakeInteger(actualItemId);
|
||||
bool hasName = !string.IsNullOrEmpty(ItemName);
|
||||
bool hasName = !string.IsNullOrEmpty(this.displayName);
|
||||
|
||||
var chunkLen = idBytes.Length + 4;
|
||||
if (hasName)
|
||||
{
|
||||
// 1 additional unknown byte compared to the nameless version, 1 byte for the name length, and then the name itself
|
||||
chunkLen += (1 + 1 + ItemName.Length);
|
||||
chunkLen += (1 + 1 + this.displayName.Length);
|
||||
if (IsHQ)
|
||||
{
|
||||
chunkLen += 4; // unicode representation of the HQ symbol is 3 bytes, preceded by a space
|
||||
|
|
@ -61,7 +57,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
// Links don't have to include the name, but if they do, it requires additional work
|
||||
if (hasName)
|
||||
{
|
||||
var nameLen = ItemName.Length + 1;
|
||||
var nameLen = this.displayName.Length + 1;
|
||||
if (IsHQ)
|
||||
{
|
||||
nameLen += 4; // space plus 3 bytes for HQ symbol
|
||||
|
|
@ -72,7 +68,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
0xFF, // unk
|
||||
(byte)nameLen
|
||||
});
|
||||
bytes.AddRange(Encoding.UTF8.GetBytes(ItemName));
|
||||
bytes.AddRange(Encoding.UTF8.GetBytes(this.displayName));
|
||||
|
||||
if (IsHQ)
|
||||
{
|
||||
|
|
@ -88,16 +84,16 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - ItemId: {ItemId}, ItemName: {ItemName}, IsHQ: {IsHQ}";
|
||||
return $"{Type} - ItemId: {itemId}, IsHQ: {IsHQ}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
ItemId = GetInteger(reader);
|
||||
this.itemId = GetInteger(reader);
|
||||
|
||||
if (ItemId > 1000000)
|
||||
if (this.itemId > 1000000)
|
||||
{
|
||||
ItemId -= 1000000;
|
||||
this.itemId -= 1000000;
|
||||
IsHQ = true;
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +105,11 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
var itemNameLen = (int)GetInteger(reader);
|
||||
var itemNameBytes = reader.ReadBytes(itemNameLen);
|
||||
|
||||
// it probably isn't necessary to store this, as we now get the lumina Item
|
||||
// on demand from the id, which will have the name
|
||||
// For incoming links, the name "should?" always match
|
||||
// but we'll store it for use in encode just in case it doesn't
|
||||
|
||||
// HQ items have the HQ symbol as part of the name, but since we already recorded
|
||||
// the HQ flag, we want just the bare name
|
||||
if (IsHQ)
|
||||
|
|
@ -116,7 +117,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
itemNameBytes = itemNameBytes.Take(itemNameLen - 4).ToArray();
|
||||
}
|
||||
|
||||
ItemName = Encoding.UTF8.GetString(itemNameBytes);
|
||||
this.displayName = Encoding.UTF8.GetString(itemNameBytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,18 +9,69 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.MapLink;
|
||||
|
||||
// pre-Resolve() values
|
||||
public uint TerritoryTypeId { get; set; }
|
||||
public uint MapId { get; set; }
|
||||
public uint RawX { get; set; }
|
||||
public uint RawY { get; set; }
|
||||
private Map map;
|
||||
public Map Map
|
||||
{
|
||||
get
|
||||
{
|
||||
this.map ??= this.dataResolver.GetExcelSheet<Map>().GetRow((int)this.mapId);
|
||||
return this.map;
|
||||
}
|
||||
}
|
||||
|
||||
// Resolved values
|
||||
// It might make sense to have Territory be an external type, that has assorted relevant info
|
||||
public string Territory { get; private set; }
|
||||
public string Zone { get; private set; }
|
||||
public float XCoord { get; private set; }
|
||||
public float YCoord { get; private set; }
|
||||
private TerritoryType territoryType;
|
||||
public TerritoryType TerritoryType
|
||||
{
|
||||
get
|
||||
{
|
||||
this.territoryType ??= this.dataResolver.GetExcelSheet<TerritoryType>().GetRow((int)this.territoryTypeId);
|
||||
return this.territoryType;
|
||||
}
|
||||
}
|
||||
|
||||
// these could be cached, but this isn't really too egregious
|
||||
public float XCoord
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConvertRawPositionToMapCoordinate(this.rawX, Map.SizeFactor);
|
||||
}
|
||||
}
|
||||
|
||||
public float YCoord
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConvertRawPositionToMapCoordinate(this.rawY, Map.SizeFactor);
|
||||
}
|
||||
}
|
||||
|
||||
private string placeNameRegion;
|
||||
public string PlaceNameRegion
|
||||
{
|
||||
get
|
||||
{
|
||||
this.placeNameRegion ??= this.dataResolver.GetExcelSheet<PlaceName>().GetRow(TerritoryType.PlaceNameRegion).Name;
|
||||
return this.placeNameRegion;
|
||||
}
|
||||
}
|
||||
|
||||
private string placeName;
|
||||
public string PlaceName
|
||||
{
|
||||
get
|
||||
{
|
||||
this.placeName ??= this.dataResolver.GetExcelSheet<PlaceName>().GetRow(TerritoryType.PlaceName).Name;
|
||||
return this.placeName;
|
||||
}
|
||||
}
|
||||
|
||||
public string DataString => $"m:{TerritoryType.RowId},{Map.RowId},{unchecked((int)rawX)},{unchecked((int)rawY)}";
|
||||
|
||||
private uint territoryTypeId;
|
||||
private uint mapId;
|
||||
private uint rawX;
|
||||
private uint rawY;
|
||||
// there is no Z; it's purely in the text payload where applicable
|
||||
|
||||
public override byte[] Encode()
|
||||
|
|
@ -28,9 +79,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
// TODO: for now we just encode the raw/internal values
|
||||
// eventually we should allow creation using 'nice' values that then encode properly
|
||||
|
||||
var packedTerritoryAndMapBytes = MakePackedInteger(TerritoryTypeId, MapId);
|
||||
var xBytes = MakeInteger(RawX);
|
||||
var yBytes = MakeInteger(RawY);
|
||||
var packedTerritoryAndMapBytes = MakePackedInteger(this.territoryTypeId, this.mapId);
|
||||
var xBytes = MakeInteger(this.rawX);
|
||||
var yBytes = MakeInteger(this.rawY);
|
||||
|
||||
var chunkLen = 4 + packedTerritoryAndMapBytes.Length + xBytes.Length + yBytes.Length;
|
||||
|
||||
|
|
@ -50,24 +101,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
// TODO: add once lumina DI is figured out
|
||||
//if (string.IsNullOrEmpty(Territory))
|
||||
//{
|
||||
// var terrRow = dataResolver.GetExcelSheet<TerritoryType>().GetRow((int)TerritoryTypeId);
|
||||
// Territory = dataResolver.GetExcelSheet<PlaceName>().GetRow(terrRow.PlaceName).Name;
|
||||
// Zone = dataResolver.GetExcelSheet<PlaceName>().GetRow(terrRow.PlaceNameZone).Name;
|
||||
|
||||
// var mapSizeFactor = dataResolver.GetExcelSheet<Map>().GetRow((int)MapId).SizeFactor;
|
||||
// XCoord = ConvertRawPositionToMapCoordinate(RawX, mapSizeFactor);
|
||||
// YCoord = ConvertRawPositionToMapCoordinate(RawY, mapSizeFactor);
|
||||
//}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - TerritoryTypeId: {TerritoryTypeId}, MapId: {MapId}, RawX: {RawX}, RawY: {RawY}";
|
||||
return $"{Type} - TerritoryTypeId: {territoryTypeId}, MapId: {mapId}, RawX: {rawX}, RawY: {rawY}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
|
|
@ -79,9 +115,9 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
|
||||
try
|
||||
{
|
||||
(TerritoryTypeId, MapId) = GetPackedIntegers(reader);
|
||||
RawX = (uint)GetInteger(reader);
|
||||
RawY = (uint)GetInteger(reader);
|
||||
(this.territoryTypeId, this.mapId) = GetPackedIntegers(reader);
|
||||
this.rawX = (uint)GetInteger(reader);
|
||||
this.rawY = (uint)GetInteger(reader);
|
||||
// the Z coordinate is never in this chunk, just the text (if applicable)
|
||||
|
||||
// seems to always be FF 01
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
||||
{
|
||||
|
|
@ -12,26 +11,19 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
public override PayloadType Type => PayloadType.Player;
|
||||
|
||||
public string PlayerName { get; private set; }
|
||||
public uint ServerId { get; private set; }
|
||||
public string ServerName { get; private set; } = String.Empty;
|
||||
|
||||
public PlayerPayload() { }
|
||||
|
||||
public PlayerPayload(string playerName, uint serverId)
|
||||
private World world;
|
||||
public World World
|
||||
{
|
||||
PlayerName = playerName;
|
||||
ServerId = serverId;
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
if (string.IsNullOrEmpty(ServerName))
|
||||
get
|
||||
{
|
||||
dynamic server = XivApi.Get($"World/{ServerId}").GetAwaiter().GetResult();
|
||||
ServerName = server.Name;
|
||||
this.world ??= this.dataResolver.GetExcelSheet<World>().GetRow((int)this.serverId);
|
||||
return this.world;
|
||||
}
|
||||
}
|
||||
|
||||
private uint serverId;
|
||||
|
||||
public override byte[] Encode()
|
||||
{
|
||||
var chunkLen = PlayerName.Length + 7;
|
||||
|
|
@ -40,7 +32,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
START_BYTE,
|
||||
(byte)SeStringChunkType.Interactable, (byte)chunkLen, (byte)EmbeddedInfoType.PlayerName,
|
||||
/* unk */ 0x01,
|
||||
(byte)(ServerId+1), // I didn't want to deal with single-byte values in MakeInteger, so we have to do the +1 manually
|
||||
(byte)(this.serverId+1), // I didn't want to deal with single-byte values in MakeInteger, so we have to do the +1 manually
|
||||
/* unk */0x01, /* unk */0xFF, // these sometimes vary but are frequently this
|
||||
(byte)(PlayerName.Length+1)
|
||||
};
|
||||
|
|
@ -50,7 +42,10 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
|
||||
// encoded names are followed by the name in plain text again
|
||||
// use the payload parsing for consistency, as this is technically a new chunk
|
||||
bytes.AddRange(new TextPayload(PlayerName).Encode());
|
||||
// bytes.AddRange(new TextPayload(PlayerName).Encode());
|
||||
|
||||
// FIXME
|
||||
bytes.AddRange(Encoding.UTF8.GetBytes(PlayerName));
|
||||
|
||||
// unsure about this entire packet, but it seems to always follow a name
|
||||
bytes.AddRange(new byte[]
|
||||
|
|
@ -65,7 +60,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - PlayerName: {PlayerName}, ServerId: {ServerId}, ServerName: {ServerName}";
|
||||
return $"{Type} - PlayerName: {PlayerName}, ServerId: {serverId}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
|
|
@ -73,7 +68,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
// unk
|
||||
reader.ReadByte();
|
||||
|
||||
ServerId = GetInteger(reader);
|
||||
this.serverId = GetInteger(reader);
|
||||
|
||||
// unk
|
||||
reader.ReadBytes(2);
|
||||
|
|
|
|||
|
|
@ -8,17 +8,13 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.Unknown;
|
||||
|
||||
public byte ChunkType { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
private byte chunkType;
|
||||
|
||||
public RawPayload(byte chunkType)
|
||||
{
|
||||
ChunkType = chunkType;
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
// nothing to do
|
||||
this.chunkType = chunkType;
|
||||
}
|
||||
|
||||
public override byte[] Encode()
|
||||
|
|
@ -28,7 +24,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
var bytes = new List<byte>()
|
||||
{
|
||||
START_BYTE,
|
||||
ChunkType,
|
||||
this.chunkType,
|
||||
(byte)chunkLen
|
||||
};
|
||||
bytes.AddRange(Data);
|
||||
|
|
@ -40,7 +36,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - Chunk type: {ChunkType:X}, Data: {BitConverter.ToString(Data).Replace("-", " ")}";
|
||||
return $"{Type} - Chunk type: {chunkType:X}, Data: {BitConverter.ToString(Data).Replace("-", " ")}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
||||
{
|
||||
|
|
@ -11,30 +9,21 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.Status;
|
||||
|
||||
public uint StatusId { get; private set; }
|
||||
|
||||
public string StatusName { get; private set; } = string.Empty;
|
||||
|
||||
public StatusPayload() { }
|
||||
|
||||
public StatusPayload(uint statusId)
|
||||
private Status status;
|
||||
public Status Status
|
||||
{
|
||||
StatusId = statusId;
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
if (string.IsNullOrEmpty(StatusName))
|
||||
get
|
||||
{
|
||||
dynamic status = XivApi.Get($"Status/{StatusId}").GetAwaiter().GetResult();
|
||||
//Console.WriteLine($"Resolved status {StatusId} to {status.Name}");
|
||||
StatusName = status.Name;
|
||||
status ??= this.dataResolver.GetExcelSheet<Status>().GetRow((int)this.statusId);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
private uint statusId;
|
||||
|
||||
public override byte[] Encode()
|
||||
{
|
||||
var idBytes = MakeInteger(StatusId);
|
||||
var idBytes = MakeInteger(this.statusId);
|
||||
|
||||
var chunkLen = idBytes.Length + 7;
|
||||
var bytes = new List<byte>()
|
||||
|
|
@ -51,12 +40,12 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - StatusId: {StatusId}, StatusName: {StatusName}";
|
||||
return $"{Type} - StatusId: {statusId}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
StatusId = GetInteger(reader);
|
||||
this.statusId = GetInteger(reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
||||
{
|
||||
|
|
@ -11,36 +9,7 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.RawText;
|
||||
|
||||
private string textConverted = null;
|
||||
|
||||
/// <summary>
|
||||
/// The Text of this text payload as an UTF-8 converted string.
|
||||
/// Don't rely on this for accurate representation of SE payload data, please check RawData instead.
|
||||
/// </summary>
|
||||
public string Text {
|
||||
get { return this.textConverted ??= Encoding.UTF8.GetString(RawData); }
|
||||
set {
|
||||
this.textConverted = value;
|
||||
RawData = Encoding.UTF8.GetBytes(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The raw unconverted data of this text payload.
|
||||
/// </summary>
|
||||
public byte[] RawData { get; set; }
|
||||
|
||||
public TextPayload() { }
|
||||
|
||||
public TextPayload(string text)
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
public string Text { get; private set; }
|
||||
|
||||
public override byte[] Encode()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
|
@ -8,15 +9,29 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.UIForeground;
|
||||
|
||||
public ushort RawColor { get; private set; }
|
||||
private UIColor color;
|
||||
public UIColor UIColor
|
||||
{
|
||||
get
|
||||
{
|
||||
this.color ??= this.dataResolver.GetExcelSheet<UIColor>().GetRow(this.colorKey);
|
||||
return this.color;
|
||||
}
|
||||
}
|
||||
|
||||
//public int Red { get; private set; }
|
||||
//public int Green { get; private set; }
|
||||
//public int Blue { get; private set; }
|
||||
public uint RGB
|
||||
{
|
||||
get
|
||||
{
|
||||
return UIColor.UIForeground;
|
||||
}
|
||||
}
|
||||
|
||||
private ushort colorKey;
|
||||
|
||||
public override byte[] Encode()
|
||||
{
|
||||
var colorBytes = MakeInteger(RawColor);
|
||||
var colorBytes = MakeInteger(this.colorKey);
|
||||
var chunkLen = colorBytes.Length + 1;
|
||||
|
||||
var bytes = new List<byte>(new byte[]
|
||||
|
|
@ -30,19 +45,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
// TODO: resolve color keys to hex colors via UIColor table
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - RawColor: {RawColor}";
|
||||
return $"{Type} - UIColor: {colorKey}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
RawColor = (ushort)GetInteger(reader);
|
||||
this.colorKey = (ushort)GetInteger(reader);
|
||||
}
|
||||
|
||||
protected override byte GetMarkerForIntegerBytes(byte[] bytes)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Lumina.Excel.GeneratedSheets;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
|
@ -8,15 +9,29 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
{
|
||||
public override PayloadType Type => PayloadType.UIGlow;
|
||||
|
||||
public ushort RawColor { get; private set; }
|
||||
private UIColor color;
|
||||
public UIColor UIColor
|
||||
{
|
||||
get
|
||||
{
|
||||
this.color ??= this.dataResolver.GetExcelSheet<UIColor>().GetRow(this.colorKey);
|
||||
return this.color;
|
||||
}
|
||||
}
|
||||
|
||||
//public int Red { get; private set; }
|
||||
//public int Green { get; private set; }
|
||||
//public int Blue { get; private set; }
|
||||
public uint RGB
|
||||
{
|
||||
get
|
||||
{
|
||||
return UIColor.UIGlow;
|
||||
}
|
||||
}
|
||||
|
||||
private ushort colorKey;
|
||||
|
||||
public override byte[] Encode()
|
||||
{
|
||||
var colorBytes = MakeInteger(RawColor);
|
||||
var colorBytes = MakeInteger(this.colorKey);
|
||||
var chunkLen = colorBytes.Length + 1;
|
||||
|
||||
var bytes = new List<byte>(new byte[]
|
||||
|
|
@ -30,19 +45,14 @@ namespace Dalamud.Game.Chat.SeStringHandling.Payloads
|
|||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
public override void Resolve()
|
||||
{
|
||||
// TODO: resolve color keys to hex colors via UIColor table
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type} - RawColor: {RawColor}";
|
||||
return $"{Type} - UIColor: {colorKey}";
|
||||
}
|
||||
|
||||
protected override void ProcessChunkImpl(BinaryReader reader, long endOfStream)
|
||||
{
|
||||
RawColor = (ushort)GetInteger(reader);
|
||||
this.colorKey = (ushort)GetInteger(reader);
|
||||
}
|
||||
|
||||
protected override byte GetMarkerForIntegerBytes(byte[] bytes)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Data;
|
||||
using Dalamud.Game.Chat.SeStringHandling.Payloads;
|
||||
|
||||
namespace Dalamud.Game.Chat.SeStringHandling
|
||||
|
|
@ -14,6 +11,9 @@ namespace Dalamud.Game.Chat.SeStringHandling
|
|||
/// </summary>
|
||||
public class SeString
|
||||
{
|
||||
// TODO: probably change how this is done/where it comes from
|
||||
public static Dalamud Dalamud { get; internal set; }
|
||||
|
||||
public List<Payload> Payloads { get; }
|
||||
|
||||
public SeString(List<Payload> payloads)
|
||||
|
|
@ -37,6 +37,11 @@ namespace Dalamud.Game.Chat.SeStringHandling
|
|||
{
|
||||
sb.Append(((TextPayload)p).Text);
|
||||
}
|
||||
// TODO: temporary (probably)
|
||||
else if (p.Type == PayloadType.AutoTranslateText)
|
||||
{
|
||||
sb.Append($"{{ {((AutoTranslatePayload)p).Text} }}");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
|
|
@ -52,9 +57,9 @@ namespace Dalamud.Game.Chat.SeStringHandling
|
|||
{
|
||||
var payloads = new List<Payload>();
|
||||
|
||||
using (var stream = new MemoryStream(bytes)) {
|
||||
using var reader = new BinaryReader(stream);
|
||||
|
||||
using (var stream = new MemoryStream(bytes))
|
||||
using (var reader = new BinaryReader(stream))
|
||||
{
|
||||
while (stream.Position < bytes.Length)
|
||||
{
|
||||
var payload = Payload.Process(reader);
|
||||
|
|
|
|||
|
|
@ -181,14 +181,14 @@ namespace Dalamud.Game {
|
|||
break;
|
||||
}
|
||||
|
||||
Log.Debug($"Probable retainer sale: {message}, decoded item {itemLink.ItemId}, HQ {itemLink.IsHQ}");
|
||||
Log.Debug($"Probable retainer sale: {message}, decoded item {itemLink.Item.RowId}, HQ {itemLink.IsHQ}");
|
||||
|
||||
var valueInfo = matchInfo.Groups["value"];
|
||||
// not sure if using a culture here would work correctly, so just strip symbols instead
|
||||
if (!valueInfo.Success || !int.TryParse(valueInfo.Value.Replace(",", "").Replace(".", ""), out var itemValue))
|
||||
continue;
|
||||
|
||||
Task.Run(() => this.dalamud.BotManager.ProcessRetainerSale((int)itemLink.ItemId, itemValue, itemLink.IsHQ));
|
||||
Task.Run(() => this.dalamud.BotManager.ProcessRetainerSale((int)itemLink.Item.RowId, itemValue, itemLink.IsHQ));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
if (!FastByteArrayCompare(oldEdited, newEdited)) {
|
||||
Log.Verbose("SeString was edited, taking precedence over StdString edit.");
|
||||
message.RawData = newEdited;
|
||||
}
|
||||
Log.Debug($"\nOLD: {BitConverter.ToString(originalMessageData)}\nNEW: {BitConverter.ToString(newEdited)}");
|
||||
Log.Debug($"\nOLD: {BitConverter.ToString(originalMessageData)}\nNEW: {BitConverter.ToString(newEdited)}");
|
||||
}
|
||||
|
||||
var messagePtr = pMessage;
|
||||
OwnedStdString allocatedString = null;
|
||||
|
|
|
|||
|
|
@ -151,8 +151,7 @@ namespace Dalamud.Game.Internal.Gui {
|
|||
openMapWithFlag =
|
||||
Address.GetVirtualFunction<OpenMapWithFlagDelegate>(uiMapObjectPtr, 0, 63);
|
||||
|
||||
var mapLinkString =
|
||||
$"m:{mapLink.TerritoryTypeId},{mapLink.MapId},{unchecked((int)mapLink.RawX)},{unchecked((int)mapLink.RawY)}";
|
||||
var mapLinkString = mapLink.DataString;
|
||||
|
||||
Log.Debug($"OpenMapWithMapLink: Opening Map Link: {mapLinkString}");
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue