server-master/srcs/FamilyServer/Services/FamilyService.cs
2026-02-10 18:21:30 +01:00

650 lines
No EOL
27 KiB
C#

// WingsEmu
//
// Developed by NosWings Team
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FamilyServer.Logs;
using FamilyServer.Managers;
using Foundatio.AsyncEx;
using PhoenixLib.DAL.Redis.Locks;
using PhoenixLib.ServiceBus;
using Plugin.FamilyImpl.Messages;
using WingsAPI.Communication;
using WingsAPI.Communication.Families;
using WingsAPI.Data.Families;
using WingsAPI.Packets.Enums.Families;
using WingsEmu.Game.Families;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Families;
namespace FamilyServer.Services
{
public class FamilyService : IFamilyService
{
private readonly IExpirableLockService _expirableLockService;
private readonly FamilyLogManager _familyLogManager;
private readonly FamilyManager _familyManager;
private readonly FamilyMembershipManager _familyMembershipManager;
private readonly IMessagePublisher<FamilyChangeFactionMessage> _messagePublisherChangedFaction;
private readonly IMessagePublisher<FamilyCreatedMessage> _messagePublisherCreated;
private readonly IMessagePublisher<FamilyDisbandMessage> _messagePublisherDisbandedFamily;
private readonly IMessagePublisher<FamilyUpdateMessage> _messagePublisherFamilyUpdate;
private readonly IMessagePublisher<FamilyMemberAddedMessage> _messagePublisherMemberAdded;
private readonly IMessagePublisher<FamilyMemberRemovedMessage> _messagePublisherMemberRemoved;
private readonly IMessagePublisher<FamilyMemberUpdateMessage> _messagePublisherMemberUpdate;
private readonly AsyncReaderWriterLock _readerWriter = new();
public FamilyService(FamilyManager familyManager, IMessagePublisher<FamilyMemberAddedMessage> messagePublisherMemberAdded, IMessagePublisher<FamilyCreatedMessage> messagePublisherCreated,
IMessagePublisher<FamilyMemberUpdateMessage> messagePublisherMemberUpdate, IMessagePublisher<FamilyMemberRemovedMessage> messagePublisherMemberRemoved,
FamilyLogManager familyLogManager, FamilyMembershipManager familyMembershipManager, IMessagePublisher<FamilyUpdateMessage> messagePublisherFamilyUpdate,
IMessagePublisher<FamilyChangeFactionMessage> messagePublisherChangedFaction, IMessagePublisher<FamilyDisbandMessage> messagePublisherDisbandedFamily,
IExpirableLockService expirableLockService)
{
_familyManager = familyManager;
_messagePublisherMemberAdded = messagePublisherMemberAdded;
_messagePublisherCreated = messagePublisherCreated;
_messagePublisherMemberUpdate = messagePublisherMemberUpdate;
_messagePublisherMemberRemoved = messagePublisherMemberRemoved;
_familyLogManager = familyLogManager;
_familyMembershipManager = familyMembershipManager;
_messagePublisherFamilyUpdate = messagePublisherFamilyUpdate;
_messagePublisherChangedFaction = messagePublisherChangedFaction;
_messagePublisherDisbandedFamily = messagePublisherDisbandedFamily;
_expirableLockService = expirableLockService;
}
public async ValueTask<FamilyCreateResponse> CreateFamilyAsync(FamilyCreateRequest req)
{
using IDisposable writerLock = await _readerWriter.WriterLockAsync();
{
if (await _familyManager.GetFamilyByNameAsync(req.Name) != null)
{
return new FamilyCreateResponse
{
Status = FamilyCreateResponseType.NAME_ALREADY_TAKEN
};
}
FamilyDTO familyDto = await _familyManager.AddFamilyAsync(new FamilyDTO
{
Name = req.Name,
Level = req.Level,
Faction = req.Faction,
Upgrades = new FamilyUpgradeDto(),
Missions = CreateBasicFamilyMissions(),
Achievements = CreateBasicAchievements()
});
foreach (FamilyMembershipDto member in req.Members)
{
member.FamilyId = familyDto.Id;
}
await _familyMembershipManager.AddFamilyMembershipsAsync(req.Members);
await _messagePublisherCreated.PublishAsync(new FamilyCreatedMessage
{
FamilyName = req.Name
});
return new FamilyCreateResponse
{
Status = FamilyCreateResponseType.SUCCESS,
Family = familyDto
};
}
}
public async ValueTask<BasicRpcResponse> DisbandFamilyAsync(FamilyDisbandRequest request)
{
IReadOnlyCollection<FamilyMembershipDto> memberships = await _familyMembershipManager.GetFamilyMembershipsByFamilyIdAsync(request.FamilyId);
if (memberships == null)
{
return new BasicRpcResponse
{
ResponseType = RpcResponseType.UNKNOWN_ERROR
};
}
foreach (FamilyMembershipDto membership in memberships)
{
await _familyMembershipManager.RemoveFamilyMembershipByCharAndFamIdAsync(membership);
}
await _familyManager.RemoveFamilyByIdAsync(request.FamilyId);
await _messagePublisherDisbandedFamily.PublishAsync(new FamilyDisbandMessage
{
FamilyId = request.FamilyId
});
return new BasicRpcResponse
{
ResponseType = RpcResponseType.SUCCESS
};
}
public async ValueTask<EmptyResponse> ChangeAuthorityByIdAsync(FamilyChangeAuthorityRequest request)
{
if (request.FamilyMembers.Count <= 0)
{
return new EmptyResponse();
}
var list = new List<FamilyMembershipDto>();
foreach (FamilyChangeContainer container in request.FamilyMembers)
{
FamilyMembershipDto membership = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(container.CharacterId);
if (membership == null)
{
continue;
}
membership.Authority = container.RequestedFamilyAuthority;
list.Add(membership);
}
await _familyMembershipManager.SaveFamilyMembershipsAsync(list);
await _messagePublisherMemberUpdate.PublishAsync(new FamilyMemberUpdateMessage
{
UpdatedMembers = list,
ChangedInfoMemberUpdate = ChangedInfoMemberUpdate.Authority
});
return new EmptyResponse();
}
public async ValueTask<FamilyChangeFactionResponse> ChangeFactionByIdAsync(FamilyChangeFactionRequest request)
{
FactionType newFaction = request.NewFaction;
FamilyDTO familyDto = await _familyManager.GetFamilyByFamilyIdAsync(request.FamilyId);
if (familyDto == null)
{
return new FamilyChangeFactionResponse
{
Status = FamilyChangeFactionResponseType.GENERIC_ERROR
};
}
if (familyDto.Faction == (byte)newFaction)
{
return new FamilyChangeFactionResponse
{
Status = FamilyChangeFactionResponseType.ALREADY_THAT_FACTION
};
}
if (!await _expirableLockService.TryAddTemporaryLockAsync($"game:locks:family:{familyDto.Id}:change-faction", DateTime.UtcNow.Date.AddDays(1)))
{
return new FamilyChangeFactionResponse
{
Status = FamilyChangeFactionResponseType.UNDER_COOLDOWN
};
}
familyDto.Faction = (byte)newFaction;
await _familyManager.AddFamilyAsync(familyDto);
await _messagePublisherChangedFaction.PublishAsync(new FamilyChangeFactionMessage
{
FamilyId = request.FamilyId,
NewFaction = newFaction
});
return new FamilyChangeFactionResponse
{
Status = FamilyChangeFactionResponseType.SUCCESS
};
}
public async ValueTask<EmptyResponse> ChangeTitleByIdAsync(FamilyChangeTitleRequest request)
{
FamilyMembershipDto membership = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(request.CharacterId);
if (membership == null)
{
return new EmptyResponse();
}
membership.Title = request.RequestedFamilyTitle;
await _familyMembershipManager.SaveFamilyMembershipAsync(membership);
await _messagePublisherMemberUpdate.PublishAsync(new FamilyMemberUpdateMessage
{
UpdatedMembers = new List<FamilyMembershipDto> { membership },
ChangedInfoMemberUpdate = ChangedInfoMemberUpdate.Authority
});
return new EmptyResponse();
}
public async ValueTask<FamilyUpgradeResponse> TryAddFamilyUpgrade(FamilyUpgradeRequest request)
{
FamilyDTO family = await _familyManager.GetFamilyByFamilyIdAsync(request.FamilyId);
if (family == null)
{
return new FamilyUpgradeResponse { ResponseType = FamilyUpgradeAddResponseType.GENERIC_SERVER_ERROR };
}
FamilyUpgradeType familyUpgrade = request.FamilyUpgradeType;
short value = request.Value;
family.Upgrades ??= new FamilyUpgradeDto();
family.Upgrades.UpgradesBought ??= new HashSet<int>();
if (family.Upgrades.UpgradesBought.Contains(request.UpgradeId))
{
return new FamilyUpgradeResponse { ResponseType = FamilyUpgradeAddResponseType.UPGRADE_ALREADY_UNLOCKED };
}
family.Upgrades.UpgradesBought.Add(request.UpgradeId);
family.Upgrades.UpgradeValues ??= new Dictionary<FamilyUpgradeType, short>();
family.Upgrades.UpgradeValues[familyUpgrade] = value;
FamilyDTO familyDto = await _familyManager.SaveFamilyAsync(family);
await _messagePublisherFamilyUpdate.PublishAsync(new FamilyUpdateMessage
{
ChangedInfoFamilyUpdate = ChangedInfoFamilyUpdate.Upgrades,
Families = new[] { familyDto }
});
return new FamilyUpgradeResponse { ResponseType = FamilyUpgradeAddResponseType.SUCCESS };
}
public async ValueTask<EmptyResponse> AddMemberToFamilyAsync(FamilyAddMemberRequest request)
{
await _familyMembershipManager.AddFamilyMembershipAsync(request.Member);
await _messagePublisherMemberAdded.PublishAsync(new FamilyMemberAddedMessage
{
AddedMember = request.Member,
Nickname = request.Nickname,
SenderId = request.SenderId
});
return new EmptyResponse();
}
public async ValueTask<EmptyResponse> MemberDisconnectedAsync(FamilyMemberDisconnectedRequest request)
{
FamilyMembershipDto member = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(request.CharacterId);
if (member == null)
{
return new EmptyResponse();
}
member.LastOnlineDate = request.DisconnectionTime;
await _familyMembershipManager.SaveFamilyMembershipAsync(member);
return new EmptyResponse();
}
public async ValueTask<EmptyResponse> RemoveMemberToFamilyAsync(FamilyRemoveMemberRequest request)
{
FamilyMembershipDto membership = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(request.CharacterId);
if (membership == null || membership.FamilyId != request.FamilyId)
{
return new EmptyResponse();
}
await _familyMembershipManager.RemoveFamilyMembershipByCharAndFamIdAsync(membership);
await _messagePublisherMemberRemoved.PublishAsync(new FamilyMemberRemovedMessage
{
CharacterId = request.CharacterId,
FamilyId = request.FamilyId
});
return new EmptyResponse();
}
public async ValueTask<BasicRpcResponse> RemoveMemberByCharIdAsync(FamilyRemoveMemberByCharIdRequest request)
{
FamilyMembershipDto membership = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(request.CharacterId);
if (membership == null)
{
return new BasicRpcResponse
{
ResponseType = RpcResponseType.GENERIC_SERVER_ERROR
};
}
await _familyMembershipManager.RemoveFamilyMembershipByCharAndFamIdAsync(membership);
await _messagePublisherMemberRemoved.PublishAsync(new FamilyMemberRemovedMessage
{
CharacterId = request.CharacterId,
FamilyId = membership.FamilyId
});
return new BasicRpcResponse
{
ResponseType = RpcResponseType.SUCCESS
};
}
public async ValueTask<FamilyIdResponse> GetFamilyByIdAsync(FamilyIdRequest req)
{
FamilyDTO family = await _familyManager.GetFamilyByFamilyIdAsync(req.FamilyId);
List<FamilyMembershipDto> members = await _familyMembershipManager.GetFamilyMembershipsByFamilyIdAsync(req.FamilyId);
List<FamilyLogDto> logs = await _familyLogManager.GetFamilyLogsByFamilyId(req.FamilyId);
await RemoveLevelAchievements(family);
DateTime resetDate = DateTime.UtcNow.Date;
if (family.Missions?.Missions != null)
{
foreach (FamilyMissionDto mission in family.Missions.Missions.Values)
{
// progressType => 1 = Completed
// progressType => 2 = InProgress
bool hasCompleted = mission.CompletionDate.HasValue;
if (!hasCompleted)
{
continue;
}
if (mission.CompletionDate.Value.Date >= resetDate)
{
continue;
}
mission.CompletionDate = null;
mission.Count = 0;
}
}
return new FamilyIdResponse
{
Family = family,
Members = members,
Logs = logs
};
}
public async ValueTask<FamilyListMembersResponse> GetFamilyMembersByFamilyId(FamilyIdRequest req)
{
List<FamilyMembershipDto> members = await _familyMembershipManager.GetFamilyMembershipsByFamilyIdAsync(req.FamilyId);
return new FamilyListMembersResponse
{
Members = members
};
}
public async ValueTask<MembershipResponse> GetMembershipByCharacterIdAsync(MembershipRequest req)
{
FamilyMembershipDto membership = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(req.CharacterId);
return new MembershipResponse
{
Membership = membership
};
}
public async ValueTask<MembershipTodayResponse> CanPerformTodayMessageAsync(MembershipTodayRequest req)
{
long characterId = req.CharacterId;
string characterName = req.CharacterName;
FamilyMembershipDto familyMember = await _familyMembershipManager.GetFamilyMembershipByCharacterIdAsync(characterId);
if (familyMember == null)
{
return new MembershipTodayResponse
{
CanPerformAction = false
};
}
if (!await _expirableLockService.TryAddTemporaryLockAsync($"game:locks:family:{familyMember.FamilyId}:{characterId}:quote-of-the-day", DateTime.UtcNow.Date.AddDays(1)))
{
return new MembershipTodayResponse
{
CanPerformAction = false
};
}
return new MembershipTodayResponse
{
CanPerformAction = true
};
}
public async ValueTask<BasicRpcResponse> UpdateFamilySettingsAsync(FamilySettingsRequest request)
{
FamilyDTO familyDto = await _familyManager.GetFamilyByFamilyIdAsync(request.FamilyId);
if (familyDto == null)
{
return new BasicRpcResponse
{
ResponseType = RpcResponseType.UNKNOWN_ERROR
};
}
byte value = request.Value;
switch (request.Authority)
{
case FamilyAuthority.Keeper:
switch (request.FamilyActionType)
{
case FamilyActionType.SendInvite:
familyDto.AssistantCanInvite = value == 1;
break;
case FamilyActionType.Notice:
familyDto.AssistantCanNotice = value == 1;
break;
case FamilyActionType.FamilyShout:
familyDto.AssistantCanShout = value == 1;
break;
case FamilyActionType.FamilyWarehouseHistory:
familyDto.AssistantCanGetHistory = value == 1;
break;
case FamilyActionType.FamilyWarehouse:
if (!Enum.TryParse(value.ToString(), out FamilyWarehouseAuthorityType authorityType))
{
return new BasicRpcResponse
{
ResponseType = RpcResponseType.UNKNOWN_ERROR
};
}
familyDto.AssistantWarehouseAuthorityType = authorityType;
break;
default:
return new BasicRpcResponse
{
ResponseType = RpcResponseType.UNKNOWN_ERROR
};
}
break;
case FamilyAuthority.Member:
switch (request.FamilyActionType)
{
case FamilyActionType.SendInvite: // Member History
familyDto.MemberCanGetHistory = value == 1;
break;
case FamilyActionType.Notice: // Member Warehouse Authority
if (!Enum.TryParse(value.ToString(), out FamilyWarehouseAuthorityType authorityType))
{
return new BasicRpcResponse
{
ResponseType = RpcResponseType.UNKNOWN_ERROR
};
}
familyDto.MemberWarehouseAuthorityType = authorityType;
break;
default:
return new BasicRpcResponse
{
ResponseType = RpcResponseType.UNKNOWN_ERROR
};
}
break;
default:
return new BasicRpcResponse
{
ResponseType = RpcResponseType.UNKNOWN_ERROR
};
}
FamilyDTO family = await _familyManager.SaveFamilyAsync(familyDto);
await _messagePublisherFamilyUpdate.PublishAsync(new FamilyUpdateMessage
{
ChangedInfoFamilyUpdate = ChangedInfoFamilyUpdate.Settings,
Families = new[] { family }
});
return new BasicRpcResponse
{
ResponseType = RpcResponseType.SUCCESS
};
}
public async ValueTask<EmptyResponse> ResetFamilyMissions()
{
DateTime resetDate = DateTime.UtcNow.Date;
List<FamilyDTO> families = _familyManager.GetFamiliesInMemory();
HashSet<FamilyDTO> toUpdate = new();
foreach (FamilyDTO family in families.Where(x => x.Missions?.Missions != null))
{
foreach (FamilyMissionDto mission in family.Missions.Missions.Values)
{
// progressType => 1 = Completed
// progressType => 2 = InProgress
bool hasCompleted = mission.CompletionDate.HasValue;
if (!hasCompleted)
{
continue;
}
if (mission.CompletionDate.Value.Date >= resetDate)
{
continue;
}
mission.CompletionDate = null;
mission.Count = 0;
if (toUpdate.Contains(family))
{
continue;
}
toUpdate.Add(family);
}
}
await _messagePublisherFamilyUpdate.PublishAsync(new FamilyUpdateMessage
{
Families = toUpdate,
ChangedInfoFamilyUpdate = ChangedInfoFamilyUpdate.AchievementsAndMissions
});
return new EmptyResponse();
}
private FamilyAchievementsDto CreateBasicAchievements()
{
FamilyAchievementsDto newAchievements = new()
{
Achievements = new Dictionary<int, FamilyAchievementCompletionDto>(),
Progress = new Dictionary<int, FamilyAchievementProgressDto>()
};
Dictionary<int, FamilyAchievementProgressDto> progress = newAchievements.Progress;
progress[(short)FamilyAchievementsVnum.FAMILY_LEVEL_2_UNLOCKED] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.FAMILY_LEVEL_2_UNLOCKED };
progress[(short)FamilyAchievementsVnum.ENTER_20_QUOTES_OF_THE_DAY] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.ENTER_20_QUOTES_OF_THE_DAY };
progress[(short)FamilyAchievementsVnum.DEFEAT_ANY_ACT4_DUNGEON_10_TIMES] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.DEFEAT_ANY_ACT4_DUNGEON_10_TIMES };
progress[(short)FamilyAchievementsVnum.DEFEAT_MORCOS_ACT4_DUNGEON_1_TIME] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.DEFEAT_MORCOS_ACT4_DUNGEON_1_TIME };
progress[(short)FamilyAchievementsVnum.DEFEAT_HATUS_ACT4_DUNGEON_1_TIME] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.DEFEAT_HATUS_ACT4_DUNGEON_1_TIME };
progress[(short)FamilyAchievementsVnum.DEFEAT_CALVINAS_ACT4_DUNGEON_1_TIME] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.DEFEAT_CALVINAS_ACT4_DUNGEON_1_TIME };
progress[(short)FamilyAchievementsVnum.DEFEAT_BERIOS_ACT4_DUNGEON_1_TIME] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.DEFEAT_BERIOS_ACT4_DUNGEON_1_TIME };
progress[(short)FamilyAchievementsVnum.COMPLETE_10_RAINBOW_BATTLE] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.COMPLETE_10_RAINBOW_BATTLE };
progress[(short)FamilyAchievementsVnum.WIN_5_RAINBOW_BATTLE] = new FamilyAchievementProgressDto { Id = (short)FamilyAchievementsVnum.WIN_5_RAINBOW_BATTLE };
return newAchievements;
}
private FamilyMissionsDto CreateBasicFamilyMissions()
{
FamilyMissionsDto newFamilyMissions = new()
{
Missions = new Dictionary<int, FamilyMissionDto>()
};
Dictionary<int, FamilyMissionDto> missions = newFamilyMissions.Missions;
missions[(int)FamilyMissionVnums.DAILY_DEFEAT_5_CUBY_RAID] = new FamilyMissionDto { Id = (int)FamilyMissionVnums.DAILY_DEFEAT_5_CUBY_RAID };
missions[(int)FamilyMissionVnums.DAILY_DEFEAT_5_GINSENG_RAID] = new FamilyMissionDto { Id = (int)FamilyMissionVnums.DAILY_DEFEAT_5_GINSENG_RAID };
missions[(int)FamilyMissionVnums.DAILY_DEFEAT_5_CASTRA_RAID] = new FamilyMissionDto { Id = (int)FamilyMissionVnums.DAILY_DEFEAT_5_CASTRA_RAID };
missions[(int)FamilyMissionVnums.DAILY_DEFEAT_5_GIANT_SPIDER_RAID] = new FamilyMissionDto { Id = (int)FamilyMissionVnums.DAILY_DEFEAT_5_GIANT_SPIDER_RAID };
missions[(int)FamilyMissionVnums.DAILY_DEFEAT_10_INSTANT_BATTLES] = new FamilyMissionDto { Id = (int)FamilyMissionVnums.DAILY_DEFEAT_10_INSTANT_BATTLES };
return newFamilyMissions;
}
private async Task RemoveLevelAchievements(FamilyDTO family)
{
if (family == null)
{
return;
}
if (family.Level <= 1)
{
return;
}
if (family.Achievements?.Achievements == null)
{
return;
}
byte familyLevel = family.Level;
short secondLevelFamily = (short)FamilyAchievementsVnum.FAMILY_LEVEL_2_UNLOCKED - 1;
bool isFirst = true;
bool addNewProgress = true;
var toRemove = new List<int>();
for (int i = 0; i < 20; i++)
{
if (isFirst)
{
isFirst = false;
}
else
{
secondLevelFamily++;
}
if (!family.Achievements.Achievements.ContainsKey(secondLevelFamily))
{
continue;
}
if (familyLevel > i)
{
continue;
}
if (addNewProgress)
{
addNewProgress = false;
family.Achievements.Progress[secondLevelFamily] = new FamilyAchievementProgressDto
{
Id = secondLevelFamily
};
}
toRemove.Add(secondLevelFamily);
}
foreach (int remove in toRemove)
{
family.Achievements.Achievements.Remove(remove);
}
await _familyManager.SaveFamilyAsync(family);
}
}
}