adjust visibility flags, improve chat functionality, bump submodules

This commit is contained in:
2025-12-18 20:49:38 +09:00
parent 7b4e42c487
commit 4ffc2247b2
12 changed files with 281 additions and 95 deletions

View File

@@ -783,7 +783,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable
if (drawObject == null)
return false;
if ((ushort)gameObject->RenderFlags == 2048)
if ((gameObject->RenderFlags & VisibilityFlags.Nameplate) != VisibilityFlags.None)
return false;
var characterBase = (CharacterBase*)drawObject;

View File

@@ -3,10 +3,21 @@ using LightlessSync.API.Dto.Chat;
namespace LightlessSync.Services.Chat;
public sealed record ChatMessageEntry(
ChatMessageDto Payload,
ChatMessageDto? Payload,
string DisplayName,
bool FromSelf,
DateTime ReceivedAtUtc);
DateTime ReceivedAtUtc,
ChatSystemEntry? SystemMessage = null)
{
public bool IsSystem => SystemMessage is not null;
}
public enum ChatSystemEntryType
{
ZoneSeparator
}
public sealed record ChatSystemEntry(ChatSystemEntryType Type, string? ZoneName);
public readonly record struct ChatChannelSnapshot(
string Key,

View File

@@ -240,8 +240,22 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
}
}
public Task<ChatParticipantResolveResultDto?> ResolveParticipantAsync(ChatChannelDescriptor descriptor, string token)
=> _apiController.ResolveChatParticipant(new ChatParticipantResolveRequestDto(descriptor, token));
public async Task<bool> SetParticipantMuteAsync(ChatChannelDescriptor descriptor, string token, bool mute)
{
if (string.IsNullOrWhiteSpace(token))
return false;
try
{
await _apiController.SetChatParticipantMute(new ChatParticipantMuteRequestDto(descriptor, token, mute)).ConfigureAwait(false);
return true;
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Failed to update chat participant mute state");
return false;
}
}
public Task<ChatReportResult> ReportMessageAsync(ChatChannelDescriptor descriptor, string messageId, string reason, string? additionalContext)
{
@@ -534,6 +548,7 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
}
bool shouldForceSend;
ChatMessageEntry? zoneSeparatorEntry = null;
using (_sync.EnterScope())
{
@@ -544,11 +559,24 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
state.IsAvailable = _chatEnabled;
state.StatusText = _chatEnabled ? null : "Chat services disabled";
var previousDescriptor = _lastZoneDescriptor;
var zoneChanged = previousDescriptor.HasValue && !ChannelDescriptorsMatch(previousDescriptor.Value, descriptor.Value);
_activeChannelKey = ZoneChannelKey;
shouldForceSend = force || !_lastZoneDescriptor.HasValue || !ChannelDescriptorsMatch(_lastZoneDescriptor.Value, descriptor.Value);
shouldForceSend = force || !previousDescriptor.HasValue || zoneChanged;
if (zoneChanged && state.Messages.Any(m => !m.IsSystem))
{
zoneSeparatorEntry = AddZoneSeparatorLocked(state, definition.Value.DisplayName);
}
_lastZoneDescriptor = descriptor;
}
if (zoneSeparatorEntry is not null)
{
Mediator.Publish(new ChatChannelMessageAdded(ZoneChannelKey, zoneSeparatorEntry));
}
PublishChannelListChanged();
await SendPresenceAsync(descriptor.Value, territoryId, isActive: true, force: shouldForceSend).ConfigureAwait(false);
}
@@ -561,7 +589,6 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
private async Task LeaveCurrentZoneAsync(bool force, ushort territoryId)
{
ChatChannelDescriptor? descriptor = null;
bool clearedHistory = false;
using (_sync.EnterScope())
{
@@ -570,15 +597,6 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
if (_channels.TryGetValue(ZoneChannelKey, out var state))
{
if (state.Messages.Count > 0)
{
state.Messages.Clear();
state.HasUnread = false;
state.UnreadCount = 0;
_lastReadCounts[ZoneChannelKey] = 0;
clearedHistory = true;
}
state.IsConnected = _isConnected;
state.IsAvailable = false;
state.StatusText = !_chatEnabled
@@ -593,11 +611,6 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
}
}
if (clearedHistory)
{
PublishHistoryCleared(ZoneChannelKey);
}
PublishChannelListChanged();
if (descriptor.HasValue)
@@ -1007,6 +1020,39 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
return new ChatMessageEntry(dto, displayName, fromSelf, DateTime.UtcNow);
}
private ChatMessageEntry AddZoneSeparatorLocked(ChatChannelState state, string zoneDisplayName)
{
var separator = new ChatMessageEntry(
null,
string.Empty,
false,
DateTime.UtcNow,
new ChatSystemEntry(ChatSystemEntryType.ZoneSeparator, zoneDisplayName));
state.Messages.Add(separator);
if (state.Messages.Count > MaxMessageHistory)
{
state.Messages.RemoveAt(0);
}
if (string.Equals(_activeChannelKey, ZoneChannelKey, StringComparison.Ordinal))
{
state.HasUnread = false;
state.UnreadCount = 0;
_lastReadCounts[ZoneChannelKey] = state.Messages.Count;
}
else if (_lastReadCounts.TryGetValue(ZoneChannelKey, out var readCount))
{
_lastReadCounts[ZoneChannelKey] = readCount + 1;
}
else
{
_lastReadCounts[ZoneChannelKey] = state.Messages.Count;
}
return separator;
}
private string ResolveDisplayName(ChatMessageDto dto, bool fromSelf)
{
var isZone = dto.Channel.Type == ChatChannelType.Zone;
@@ -1070,8 +1116,6 @@ public sealed class ZoneChatService : DisposableMediatorSubscriberBase, IHostedS
private void PublishChannelListChanged() => Mediator.Publish(new ChatChannelsUpdated());
private void PublishHistoryCleared(string channelKey) => Mediator.Publish(new ChatChannelHistoryCleared(channelKey));
private static IEnumerable<string> EnumerateTerritoryKeys(string? value)
{
if (string.IsNullOrWhiteSpace(value))

View File

@@ -26,6 +26,7 @@ using System.Runtime.CompilerServices;
using System.Text;
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
using VisibilityFlags = FFXIVClientStructs.FFXIV.Client.Game.Object.VisibilityFlags;
namespace LightlessSync.Services;
@@ -707,7 +708,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
const int tick = 250;
int curWaitTime = 0;
_logger.LogTrace("RenderFlags: {flags}", obj->RenderFlags.ToString("X"));
while (obj->RenderFlags != 0x00 && curWaitTime < timeOut)
while (obj->RenderFlags != VisibilityFlags.None && curWaitTime < timeOut)
{
_logger.LogTrace($"Waiting for gpose actor to finish drawing");
curWaitTime += tick;
@@ -752,7 +753,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
bool isDrawingChanged = false;
if ((nint)drawObj != IntPtr.Zero)
{
isDrawing = (ushort)gameObj->RenderFlags == 0b100000000000;
isDrawing = (gameObj->RenderFlags & VisibilityFlags.Nameplate) != VisibilityFlags.None;
if (!isDrawing)
{
isDrawing = ((CharacterBase*)drawObj)->HasModelInSlotLoaded != 0;

View File

@@ -133,7 +133,6 @@ public record PairDownloadStatusMessage(List<(string PlayerName, float Progress,
public record VisibilityChange : MessageBase;
public record ChatChannelsUpdated : MessageBase;
public record ChatChannelMessageAdded(string ChannelKey, ChatMessageEntry Message) : MessageBase;
public record ChatChannelHistoryCleared(string ChannelKey) : MessageBase;
public record GroupCollectionChangedMessage : MessageBase;
public record OpenUserProfileMessage(UserData User) : MessageBase;
#pragma warning restore S2094

View File

@@ -327,7 +327,12 @@ public class LightlessProfileManager : MediatorSubscriberBase
if (profile == null)
return null;
var userData = profile.User;
if (profile.User is null)
{
Logger.LogWarning("Lightfinder profile response missing user info for CID {HashedCid}", hashedCid);
}
var userData = profile.User ?? new UserData(hashedCid, Alias: "Lightfinder User");
var profileTags = profile.Tags ?? _emptyTagSet;
var profileData = BuildProfileData(userData, profile, profileTags);
_lightlessProfiles[userData] = profileData;