Initial
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.CharaData;
|
||||
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public sealed record CharaDataExtendedUpdateDto : CharaDataUpdateDto
|
||||
{
|
||||
private readonly CharaDataFullDto _charaDataFullDto;
|
||||
|
||||
public CharaDataExtendedUpdateDto(CharaDataUpdateDto dto, CharaDataFullDto charaDataFullDto) : base(dto)
|
||||
{
|
||||
_charaDataFullDto = charaDataFullDto;
|
||||
_userList = charaDataFullDto.AllowedUsers.ToList();
|
||||
_groupList = charaDataFullDto.AllowedGroups.ToList();
|
||||
_poseList = charaDataFullDto.PoseData.Select(k => new PoseEntry(k.Id)
|
||||
{
|
||||
Description = k.Description,
|
||||
PoseData = k.PoseData,
|
||||
WorldData = k.WorldData
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public CharaDataUpdateDto BaseDto => new(Id)
|
||||
{
|
||||
AllowedUsers = AllowedUsers,
|
||||
AllowedGroups = AllowedGroups,
|
||||
AccessType = base.AccessType,
|
||||
CustomizeData = base.CustomizeData,
|
||||
Description = base.Description,
|
||||
ExpiryDate = base.ExpiryDate,
|
||||
FileGamePaths = base.FileGamePaths,
|
||||
FileSwaps = base.FileSwaps,
|
||||
GlamourerData = base.GlamourerData,
|
||||
ShareType = base.ShareType,
|
||||
ManipulationData = base.ManipulationData,
|
||||
Poses = Poses
|
||||
};
|
||||
|
||||
public new string ManipulationData
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.ManipulationData ?? _charaDataFullDto.ManipulationData;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.ManipulationData = value;
|
||||
if (string.Equals(base.ManipulationData, _charaDataFullDto.ManipulationData, StringComparison.Ordinal))
|
||||
{
|
||||
base.ManipulationData = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Description ?? _charaDataFullDto.Description;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.Description = value;
|
||||
if (string.Equals(base.Description, _charaDataFullDto.Description, StringComparison.Ordinal))
|
||||
{
|
||||
base.Description = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new DateTime ExpiryDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.ExpiryDate ?? _charaDataFullDto.ExpiryDate;
|
||||
}
|
||||
private set
|
||||
{
|
||||
base.ExpiryDate = value;
|
||||
if (Equals(base.ExpiryDate, _charaDataFullDto.ExpiryDate))
|
||||
{
|
||||
base.ExpiryDate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new AccessTypeDto AccessType
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.AccessType ?? _charaDataFullDto.AccessType;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.AccessType = value;
|
||||
if (AccessType == AccessTypeDto.Public && ShareType == ShareTypeDto.Shared)
|
||||
{
|
||||
ShareType = ShareTypeDto.Private;
|
||||
}
|
||||
|
||||
if (Equals(base.AccessType, _charaDataFullDto.AccessType))
|
||||
{
|
||||
base.AccessType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new ShareTypeDto ShareType
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.ShareType ?? _charaDataFullDto.ShareType;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.ShareType = value;
|
||||
if (ShareType == ShareTypeDto.Shared && AccessType == AccessTypeDto.Public)
|
||||
{
|
||||
base.ShareType = ShareTypeDto.Private;
|
||||
}
|
||||
|
||||
if (Equals(base.ShareType, _charaDataFullDto.ShareType))
|
||||
{
|
||||
base.ShareType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new List<GamePathEntry>? FileGamePaths
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.FileGamePaths ?? _charaDataFullDto.FileGamePaths;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.FileGamePaths = value;
|
||||
if (!(base.FileGamePaths ?? []).Except(_charaDataFullDto.FileGamePaths).Any()
|
||||
&& !_charaDataFullDto.FileGamePaths.Except(base.FileGamePaths ?? []).Any())
|
||||
{
|
||||
base.FileGamePaths = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new List<GamePathEntry>? FileSwaps
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.FileSwaps ?? _charaDataFullDto.FileSwaps;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.FileSwaps = value;
|
||||
if (!(base.FileSwaps ?? []).Except(_charaDataFullDto.FileSwaps).Any()
|
||||
&& !_charaDataFullDto.FileSwaps.Except(base.FileSwaps ?? []).Any())
|
||||
{
|
||||
base.FileSwaps = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new string? GlamourerData
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.GlamourerData ?? _charaDataFullDto.GlamourerData;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.GlamourerData = value;
|
||||
if (string.Equals(base.GlamourerData, _charaDataFullDto.GlamourerData, StringComparison.Ordinal))
|
||||
{
|
||||
base.GlamourerData = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public new string? CustomizeData
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.CustomizeData ?? _charaDataFullDto.CustomizeData;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.CustomizeData = value;
|
||||
if (string.Equals(base.CustomizeData, _charaDataFullDto.CustomizeData, StringComparison.Ordinal))
|
||||
{
|
||||
base.CustomizeData = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<UserData> UserList => _userList;
|
||||
private readonly List<UserData> _userList;
|
||||
|
||||
public IEnumerable<GroupData> GroupList => _groupList;
|
||||
private readonly List<GroupData> _groupList;
|
||||
|
||||
public IEnumerable<PoseEntry> PoseList => _poseList;
|
||||
private readonly List<PoseEntry> _poseList;
|
||||
|
||||
public void AddUserToList(string user)
|
||||
{
|
||||
_userList.Add(new(user, null));
|
||||
UpdateAllowedUsers();
|
||||
}
|
||||
|
||||
public void AddGroupToList(string group)
|
||||
{
|
||||
_groupList.Add(new(group, null));
|
||||
UpdateAllowedGroups();
|
||||
}
|
||||
|
||||
private void UpdateAllowedUsers()
|
||||
{
|
||||
AllowedUsers = [.. _userList.Select(u => u.UID)];
|
||||
if (!AllowedUsers.Except(_charaDataFullDto.AllowedUsers.Select(u => u.UID), StringComparer.Ordinal).Any()
|
||||
&& !_charaDataFullDto.AllowedUsers.Select(u => u.UID).Except(AllowedUsers, StringComparer.Ordinal).Any())
|
||||
{
|
||||
AllowedUsers = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAllowedGroups()
|
||||
{
|
||||
AllowedGroups = [.. _groupList.Select(u => u.GID)];
|
||||
if (!AllowedGroups.Except(_charaDataFullDto.AllowedGroups.Select(u => u.GID), StringComparer.Ordinal).Any()
|
||||
&& !_charaDataFullDto.AllowedGroups.Select(u => u.GID).Except(AllowedGroups, StringComparer.Ordinal).Any())
|
||||
{
|
||||
AllowedGroups = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveUserFromList(string user)
|
||||
{
|
||||
_userList.RemoveAll(u => string.Equals(u.UID, user, StringComparison.Ordinal));
|
||||
UpdateAllowedUsers();
|
||||
}
|
||||
|
||||
public void RemoveGroupFromList(string group)
|
||||
{
|
||||
_groupList.RemoveAll(u => string.Equals(u.GID, group, StringComparison.Ordinal));
|
||||
UpdateAllowedGroups();
|
||||
}
|
||||
|
||||
public void AddPose()
|
||||
{
|
||||
_poseList.Add(new PoseEntry(null));
|
||||
UpdatePoseList();
|
||||
}
|
||||
|
||||
public void RemovePose(PoseEntry entry)
|
||||
{
|
||||
if (entry.Id != null)
|
||||
{
|
||||
entry.Description = null;
|
||||
entry.WorldData = null;
|
||||
entry.PoseData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_poseList.Remove(entry);
|
||||
}
|
||||
|
||||
UpdatePoseList();
|
||||
}
|
||||
|
||||
public void UpdatePoseList()
|
||||
{
|
||||
Poses = [.. _poseList];
|
||||
if (!Poses.Except(_charaDataFullDto.PoseData).Any() && !_charaDataFullDto.PoseData.Except(Poses).Any())
|
||||
{
|
||||
Poses = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetExpiry(bool expiring)
|
||||
{
|
||||
if (expiring)
|
||||
{
|
||||
var date = DateTime.UtcNow.AddDays(7);
|
||||
SetExpiry(date.Year, date.Month, date.Day);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpiryDate = DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetExpiry(int year, int month, int day)
|
||||
{
|
||||
int daysInMonth = DateTime.DaysInMonth(year, month);
|
||||
if (day > daysInMonth) day = 1;
|
||||
ExpiryDate = new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
internal void UndoChanges()
|
||||
{
|
||||
base.Description = null;
|
||||
base.AccessType = null;
|
||||
base.ShareType = null;
|
||||
base.GlamourerData = null;
|
||||
base.FileSwaps = null;
|
||||
base.FileGamePaths = null;
|
||||
base.CustomizeData = null;
|
||||
base.ManipulationData = null;
|
||||
AllowedUsers = null;
|
||||
AllowedGroups = null;
|
||||
Poses = null;
|
||||
_poseList.Clear();
|
||||
_poseList.AddRange(_charaDataFullDto.PoseData.Select(k => new PoseEntry(k.Id)
|
||||
{
|
||||
Description = k.Description,
|
||||
PoseData = k.PoseData,
|
||||
WorldData = k.WorldData
|
||||
}));
|
||||
}
|
||||
|
||||
internal void RevertDeletion(PoseEntry pose)
|
||||
{
|
||||
if (pose.Id == null) return;
|
||||
var oldPose = _charaDataFullDto.PoseData.Find(p => p.Id == pose.Id);
|
||||
if (oldPose == null) return;
|
||||
pose.Description = oldPose.Description;
|
||||
pose.PoseData = oldPose.PoseData;
|
||||
pose.WorldData = oldPose.WorldData;
|
||||
UpdatePoseList();
|
||||
}
|
||||
|
||||
internal bool PoseHasChanges(PoseEntry pose)
|
||||
{
|
||||
if (pose.Id == null) return false;
|
||||
var oldPose = _charaDataFullDto.PoseData.Find(p => p.Id == pose.Id);
|
||||
if (oldPose == null) return false;
|
||||
return !string.Equals(pose.Description, oldPose.Description, StringComparison.Ordinal)
|
||||
|| !string.Equals(pose.PoseData, oldPose.PoseData, StringComparison.Ordinal)
|
||||
|| pose.WorldData != oldPose.WorldData;
|
||||
}
|
||||
|
||||
public bool HasChanges =>
|
||||
base.Description != null
|
||||
|| base.ExpiryDate != null
|
||||
|| base.AccessType != null
|
||||
|| base.ShareType != null
|
||||
|| AllowedUsers != null
|
||||
|| AllowedGroups != null
|
||||
|| base.GlamourerData != null
|
||||
|| base.FileSwaps != null
|
||||
|| base.FileGamePaths != null
|
||||
|| base.CustomizeData != null
|
||||
|| base.ManipulationData != null
|
||||
|| Poses != null;
|
||||
|
||||
public bool IsAppearanceEqual =>
|
||||
string.Equals(GlamourerData, _charaDataFullDto.GlamourerData, StringComparison.Ordinal)
|
||||
&& string.Equals(CustomizeData, _charaDataFullDto.CustomizeData, StringComparison.Ordinal)
|
||||
&& FileGamePaths == _charaDataFullDto.FileGamePaths
|
||||
&& FileSwaps == _charaDataFullDto.FileSwaps
|
||||
&& string.Equals(ManipulationData, _charaDataFullDto.ManipulationData, StringComparison.Ordinal);
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
using MareSynchronos.API.Dto.CharaData;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public sealed record CharaDataFullExtendedDto : CharaDataFullDto
|
||||
{
|
||||
public CharaDataFullExtendedDto(CharaDataFullDto baseDto) : base(baseDto)
|
||||
{
|
||||
FullId = baseDto.Uploader.UID + ":" + baseDto.Id;
|
||||
MissingFiles = new ReadOnlyCollection<GamePathEntry>(baseDto.OriginalFiles.Except(baseDto.FileGamePaths).ToList());
|
||||
HasMissingFiles = MissingFiles.Any();
|
||||
}
|
||||
|
||||
public string FullId { get; set; }
|
||||
public bool HasMissingFiles { get; init; }
|
||||
public IReadOnlyCollection<GamePathEntry> MissingFiles { get; init; }
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
using MareSynchronos.API.Dto.CharaData;
|
||||
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public sealed record CharaDataMetaInfoExtendedDto : CharaDataMetaInfoDto
|
||||
{
|
||||
private CharaDataMetaInfoExtendedDto(CharaDataMetaInfoDto baseMeta) : base(baseMeta)
|
||||
{
|
||||
FullId = baseMeta.Uploader.UID + ":" + baseMeta.Id;
|
||||
}
|
||||
|
||||
public List<PoseEntryExtended> PoseExtended { get; private set; } = [];
|
||||
public bool HasPoses => PoseExtended.Count != 0;
|
||||
public bool HasWorldData => PoseExtended.Exists(p => p.HasWorldData);
|
||||
public bool IsOwnData { get; private set; }
|
||||
public string FullId { get; private set; }
|
||||
|
||||
public async static Task<CharaDataMetaInfoExtendedDto> Create(CharaDataMetaInfoDto baseMeta, DalamudUtilService dalamudUtilService, bool isOwnData = false)
|
||||
{
|
||||
CharaDataMetaInfoExtendedDto newDto = new(baseMeta);
|
||||
|
||||
foreach (var pose in newDto.PoseData)
|
||||
{
|
||||
newDto.PoseExtended.Add(await PoseEntryExtended.Create(pose, newDto, dalamudUtilService).ConfigureAwait(false));
|
||||
}
|
||||
|
||||
newDto.IsOwnData = isOwnData;
|
||||
|
||||
return newDto;
|
||||
}
|
||||
}
|
174
MareSynchronos/Services/CharaData/Models/GposeLobbyUserData.cs
Normal file
174
MareSynchronos/Services/CharaData/Models/GposeLobbyUserData.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using Dalamud.Utility;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Dto.CharaData;
|
||||
using MareSynchronos.Utils;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public sealed record GposeLobbyUserData(UserData UserData)
|
||||
{
|
||||
public void Reset()
|
||||
{
|
||||
HasWorldDataUpdate = WorldData != null;
|
||||
HasPoseDataUpdate = ApplicablePoseData != null;
|
||||
SpawnedVfxId = null;
|
||||
LastAppliedCharaDataDate = DateTime.MinValue;
|
||||
}
|
||||
|
||||
private WorldData? _worldData;
|
||||
public WorldData? WorldData
|
||||
{
|
||||
get => _worldData; set
|
||||
{
|
||||
_worldData = value;
|
||||
HasWorldDataUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasWorldDataUpdate { get; set; } = false;
|
||||
|
||||
private PoseData? _fullPoseData;
|
||||
private PoseData? _deltaPoseData;
|
||||
|
||||
public PoseData? FullPoseData
|
||||
{
|
||||
get => _fullPoseData;
|
||||
set
|
||||
{
|
||||
_fullPoseData = value;
|
||||
ApplicablePoseData = CombinePoseData();
|
||||
HasPoseDataUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
public PoseData? DeltaPoseData
|
||||
{
|
||||
get => _deltaPoseData;
|
||||
set
|
||||
{
|
||||
_deltaPoseData = value;
|
||||
ApplicablePoseData = CombinePoseData();
|
||||
HasPoseDataUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
public PoseData? ApplicablePoseData { get; private set; }
|
||||
public bool HasPoseDataUpdate { get; set; } = false;
|
||||
public Guid? SpawnedVfxId { get; set; }
|
||||
public Vector3? LastWorldPosition { get; set; }
|
||||
public Vector3? TargetWorldPosition { get; set; }
|
||||
public DateTime? UpdateStart { get; set; }
|
||||
private CharaDataDownloadDto? _charaData;
|
||||
public CharaDataDownloadDto? CharaData
|
||||
{
|
||||
get => _charaData; set
|
||||
{
|
||||
_charaData = value;
|
||||
LastUpdatedCharaData = _charaData?.UpdatedDate ?? DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime LastUpdatedCharaData { get; private set; } = DateTime.MaxValue;
|
||||
public DateTime LastAppliedCharaDataDate { get; set; } = DateTime.MinValue;
|
||||
public nint Address { get; set; }
|
||||
public string AssociatedCharaName { get; set; } = string.Empty;
|
||||
|
||||
private PoseData? CombinePoseData()
|
||||
{
|
||||
if (DeltaPoseData == null && FullPoseData != null) return FullPoseData;
|
||||
if (FullPoseData == null) return null;
|
||||
|
||||
PoseData output = FullPoseData!.Value.DeepClone();
|
||||
PoseData delta = DeltaPoseData!.Value;
|
||||
|
||||
foreach (var bone in FullPoseData!.Value.Bones)
|
||||
{
|
||||
if (!delta.Bones.TryGetValue(bone.Key, out var data)) continue;
|
||||
if (!data.Exists)
|
||||
{
|
||||
output.Bones.Remove(bone.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Bones[bone.Key] = data;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bone in FullPoseData!.Value.MainHand)
|
||||
{
|
||||
if (!delta.MainHand.TryGetValue(bone.Key, out var data)) continue;
|
||||
if (!data.Exists)
|
||||
{
|
||||
output.MainHand.Remove(bone.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.MainHand[bone.Key] = data;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var bone in FullPoseData!.Value.OffHand)
|
||||
{
|
||||
if (!delta.OffHand.TryGetValue(bone.Key, out var data)) continue;
|
||||
if (!data.Exists)
|
||||
{
|
||||
output.OffHand.Remove(bone.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.OffHand[bone.Key] = data;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public string WorldDataDescriptor { get; private set; } = string.Empty;
|
||||
public Vector2 MapCoordinates { get; private set; }
|
||||
public Lumina.Excel.Sheets.Map Map { get; private set; }
|
||||
public HandledCharaDataEntry? HandledChara { get; set; }
|
||||
|
||||
public async Task SetWorldDataDescriptor(DalamudUtilService dalamudUtilService)
|
||||
{
|
||||
if (WorldData == null)
|
||||
{
|
||||
WorldDataDescriptor = "No World Data found";
|
||||
}
|
||||
|
||||
var worldData = WorldData!.Value;
|
||||
MapCoordinates = await dalamudUtilService.RunOnFrameworkThread(() =>
|
||||
MapUtil.WorldToMap(new Vector2(worldData.PositionX, worldData.PositionY), dalamudUtilService.MapData.Value[worldData.LocationInfo.MapId].Map))
|
||||
.ConfigureAwait(false);
|
||||
Map = dalamudUtilService.MapData.Value[worldData.LocationInfo.MapId].Map;
|
||||
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine("Server: " + dalamudUtilService.WorldData.Value[(ushort)worldData.LocationInfo.ServerId]);
|
||||
sb.AppendLine("Territory: " + dalamudUtilService.TerritoryData.Value[worldData.LocationInfo.TerritoryId]);
|
||||
sb.AppendLine("Map: " + dalamudUtilService.MapData.Value[worldData.LocationInfo.MapId].MapName);
|
||||
|
||||
if (worldData.LocationInfo.WardId != 0)
|
||||
sb.AppendLine("Ward #: " + worldData.LocationInfo.WardId);
|
||||
if (worldData.LocationInfo.DivisionId != 0)
|
||||
{
|
||||
sb.AppendLine("Subdivision: " + worldData.LocationInfo.DivisionId switch
|
||||
{
|
||||
1 => "No",
|
||||
2 => "Yes",
|
||||
_ => "-"
|
||||
});
|
||||
}
|
||||
if (worldData.LocationInfo.HouseId != 0)
|
||||
{
|
||||
sb.AppendLine("House #: " + (worldData.LocationInfo.HouseId == 100 ? "Apartments" : worldData.LocationInfo.HouseId.ToString()));
|
||||
}
|
||||
if (worldData.LocationInfo.RoomId != 0)
|
||||
{
|
||||
sb.AppendLine("Apartment #: " + worldData.LocationInfo.RoomId);
|
||||
}
|
||||
sb.AppendLine("Coordinates: X: " + MapCoordinates.X.ToString("0.0", CultureInfo.InvariantCulture) + ", Y: " + MapCoordinates.Y.ToString("0.0", CultureInfo.InvariantCulture));
|
||||
WorldDataDescriptor = sb.ToString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public sealed record HandledCharaDataEntry(string Name, bool IsSelf, Guid? CustomizePlus, CharaDataMetaInfoExtendedDto MetaInfo)
|
||||
{
|
||||
public CharaDataMetaInfoExtendedDto MetaInfo { get; set; } = MetaInfo;
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.API.Data.Enum;
|
||||
using MareSynchronos.FileCache;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public record MareCharaFileData
|
||||
{
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string GlamourerData { get; set; } = string.Empty;
|
||||
public string CustomizePlusData { get; set; } = string.Empty;
|
||||
public string ManipulationData { get; set; } = string.Empty;
|
||||
public List<FileData> Files { get; set; } = [];
|
||||
public List<FileSwap> FileSwaps { get; set; } = [];
|
||||
|
||||
public MareCharaFileData() { }
|
||||
public MareCharaFileData(FileCacheManager manager, string description, CharacterData dto)
|
||||
{
|
||||
Description = description;
|
||||
|
||||
if (dto.GlamourerData.TryGetValue(ObjectKind.Player, out var glamourerData))
|
||||
{
|
||||
GlamourerData = glamourerData;
|
||||
}
|
||||
|
||||
dto.CustomizePlusData.TryGetValue(ObjectKind.Player, out var customizePlusData);
|
||||
CustomizePlusData = customizePlusData ?? string.Empty;
|
||||
ManipulationData = dto.ManipulationData;
|
||||
|
||||
if (dto.FileReplacements.TryGetValue(ObjectKind.Player, out var fileReplacements))
|
||||
{
|
||||
var grouped = fileReplacements.GroupBy(f => f.Hash, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var file in grouped)
|
||||
{
|
||||
if (string.IsNullOrEmpty(file.Key))
|
||||
{
|
||||
foreach (var item in file)
|
||||
{
|
||||
FileSwaps.Add(new FileSwap(item.GamePaths, item.FileSwapPath));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var filePath = manager.GetFileCacheByHash(file.First().Hash)?.ResolvedFilepath;
|
||||
if (filePath != null)
|
||||
{
|
||||
Files.Add(new FileData(file.SelectMany(f => f.GamePaths), (int)new FileInfo(filePath).Length, file.First().Hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(this));
|
||||
}
|
||||
|
||||
public static MareCharaFileData FromByteArray(byte[] data)
|
||||
{
|
||||
return JsonSerializer.Deserialize<MareCharaFileData>(Encoding.UTF8.GetString(data))!;
|
||||
}
|
||||
|
||||
public record FileSwap(IEnumerable<string> GamePaths, string FileSwapPath);
|
||||
|
||||
public record FileData(IEnumerable<string> GamePaths, int Length, string Hash);
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public record MareCharaFileHeader(byte Version, MareCharaFileData CharaFileData)
|
||||
{
|
||||
public static readonly byte CurrentVersion = 1;
|
||||
|
||||
public byte Version { get; set; } = Version;
|
||||
public MareCharaFileData CharaFileData { get; set; } = CharaFileData;
|
||||
public string FilePath { get; private set; } = string.Empty;
|
||||
|
||||
public void WriteToStream(BinaryWriter writer)
|
||||
{
|
||||
writer.Write('M');
|
||||
writer.Write('C');
|
||||
writer.Write('D');
|
||||
writer.Write('F');
|
||||
writer.Write(Version);
|
||||
var charaFileDataArray = CharaFileData.ToByteArray();
|
||||
writer.Write(charaFileDataArray.Length);
|
||||
writer.Write(charaFileDataArray);
|
||||
}
|
||||
|
||||
public static MareCharaFileHeader? FromBinaryReader(string path, BinaryReader reader)
|
||||
{
|
||||
var chars = new string(reader.ReadChars(4));
|
||||
if (!string.Equals(chars, "MCDF", StringComparison.Ordinal)) throw new InvalidDataException("Not a Mare Chara File");
|
||||
|
||||
MareCharaFileHeader? decoded = null;
|
||||
|
||||
var version = reader.ReadByte();
|
||||
if (version == 1)
|
||||
{
|
||||
var dataLength = reader.ReadInt32();
|
||||
|
||||
decoded = new(version, MareCharaFileData.FromByteArray(reader.ReadBytes(dataLength)))
|
||||
{
|
||||
FilePath = path,
|
||||
};
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
public static void AdvanceReaderToData(BinaryReader reader)
|
||||
{
|
||||
reader.ReadChars(4);
|
||||
var version = reader.ReadByte();
|
||||
if (version == 1)
|
||||
{
|
||||
var length = reader.ReadInt32();
|
||||
_ = reader.ReadBytes(length);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
using Dalamud.Utility;
|
||||
using Lumina.Excel.Sheets;
|
||||
using MareSynchronos.API.Dto.CharaData;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.Services.CharaData.Models;
|
||||
|
||||
public sealed record PoseEntryExtended : PoseEntry
|
||||
{
|
||||
private PoseEntryExtended(PoseEntry basePose, CharaDataMetaInfoExtendedDto parent) : base(basePose)
|
||||
{
|
||||
HasPoseData = !string.IsNullOrEmpty(basePose.PoseData);
|
||||
HasWorldData = (WorldData ?? default) != default;
|
||||
if (HasWorldData)
|
||||
{
|
||||
Position = new(basePose.WorldData!.Value.PositionX, basePose.WorldData!.Value.PositionY, basePose.WorldData!.Value.PositionZ);
|
||||
Rotation = new(basePose.WorldData!.Value.RotationX, basePose.WorldData!.Value.RotationY, basePose.WorldData!.Value.RotationZ, basePose.WorldData!.Value.RotationW);
|
||||
}
|
||||
MetaInfo = parent;
|
||||
}
|
||||
|
||||
public CharaDataMetaInfoExtendedDto MetaInfo { get; }
|
||||
public bool HasPoseData { get; }
|
||||
public bool HasWorldData { get; }
|
||||
public Vector3 Position { get; } = new();
|
||||
public Vector2 MapCoordinates { get; private set; } = new();
|
||||
public Quaternion Rotation { get; } = new();
|
||||
public Map Map { get; private set; }
|
||||
public string WorldDataDescriptor { get; private set; } = string.Empty;
|
||||
|
||||
public static async Task<PoseEntryExtended> Create(PoseEntry baseEntry, CharaDataMetaInfoExtendedDto parent, DalamudUtilService dalamudUtilService)
|
||||
{
|
||||
PoseEntryExtended newPose = new(baseEntry, parent);
|
||||
|
||||
if (newPose.HasWorldData)
|
||||
{
|
||||
var worldData = newPose.WorldData!.Value;
|
||||
newPose.MapCoordinates = await dalamudUtilService.RunOnFrameworkThread(() =>
|
||||
MapUtil.WorldToMap(new Vector2(worldData.PositionX, worldData.PositionY), dalamudUtilService.MapData.Value[worldData.LocationInfo.MapId].Map))
|
||||
.ConfigureAwait(false);
|
||||
newPose.Map = dalamudUtilService.MapData.Value[worldData.LocationInfo.MapId].Map;
|
||||
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine("Server: " + dalamudUtilService.WorldData.Value[(ushort)worldData.LocationInfo.ServerId]);
|
||||
sb.AppendLine("Territory: " + dalamudUtilService.TerritoryData.Value[worldData.LocationInfo.TerritoryId]);
|
||||
sb.AppendLine("Map: " + dalamudUtilService.MapData.Value[worldData.LocationInfo.MapId].MapName);
|
||||
|
||||
if (worldData.LocationInfo.WardId != 0)
|
||||
sb.AppendLine("Ward #: " + worldData.LocationInfo.WardId);
|
||||
if (worldData.LocationInfo.DivisionId != 0)
|
||||
{
|
||||
sb.AppendLine("Subdivision: " + worldData.LocationInfo.DivisionId switch
|
||||
{
|
||||
1 => "No",
|
||||
2 => "Yes",
|
||||
_ => "-"
|
||||
});
|
||||
}
|
||||
if (worldData.LocationInfo.HouseId != 0)
|
||||
{
|
||||
sb.AppendLine("House #: " + (worldData.LocationInfo.HouseId == 100 ? "Apartments" : worldData.LocationInfo.HouseId.ToString()));
|
||||
}
|
||||
if (worldData.LocationInfo.RoomId != 0)
|
||||
{
|
||||
sb.AppendLine("Apartment #: " + worldData.LocationInfo.RoomId);
|
||||
}
|
||||
sb.AppendLine("Coordinates: X: " + newPose.MapCoordinates.X.ToString("0.0", CultureInfo.InvariantCulture) + ", Y: " + newPose.MapCoordinates.Y.ToString("0.0", CultureInfo.InvariantCulture));
|
||||
newPose.WorldDataDescriptor = sb.ToString();
|
||||
}
|
||||
|
||||
return newPose;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user