541 lines
No EOL
20 KiB
C#
541 lines
No EOL
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using PhoenixLib.Events;
|
|
using PhoenixLib.Logging;
|
|
using WingsAPI.Communication.ServerApi.Protocol;
|
|
using WingsAPI.Game.Extensions.Families;
|
|
using WingsAPI.Game.Extensions.Groups;
|
|
using WingsAPI.Game.Extensions.MinilandExtensions;
|
|
using WingsAPI.Game.Extensions.PacketGeneration;
|
|
using WingsEmu.DTOs.Maps;
|
|
using WingsEmu.Game;
|
|
using WingsEmu.Game._enum;
|
|
using WingsEmu.Game._i18n;
|
|
using WingsEmu.Game._ItemUsage.Configuration;
|
|
using WingsEmu.Game._packetHandling;
|
|
using WingsEmu.Game.Algorithm;
|
|
using WingsEmu.Game.Battle;
|
|
using WingsEmu.Game.Buffs;
|
|
using WingsEmu.Game.Characters;
|
|
using WingsEmu.Game.Characters.Events;
|
|
using WingsEmu.Game.Configurations;
|
|
using WingsEmu.Game.Entities;
|
|
using WingsEmu.Game.Entities.Extensions;
|
|
using WingsEmu.Game.Extensions;
|
|
using WingsEmu.Game.Extensions.Mates;
|
|
using WingsEmu.Game.Families;
|
|
using WingsEmu.Game.Groups;
|
|
using WingsEmu.Game.Helpers.Damages;
|
|
using WingsEmu.Game.Managers;
|
|
using WingsEmu.Game.Maps;
|
|
using WingsEmu.Game.Maps.Event;
|
|
using WingsEmu.Game.Mates;
|
|
using WingsEmu.Game.Mates.Events;
|
|
using WingsEmu.Game.Miniland;
|
|
using WingsEmu.Game.Networking;
|
|
using WingsEmu.Game.Networking.Broadcasting;
|
|
using WingsEmu.Game.Portals;
|
|
using WingsEmu.Game.Raids;
|
|
using WingsEmu.Game.TimeSpaces;
|
|
using WingsEmu.Packets.Enums;
|
|
using WingsEmu.Packets.Enums.Chat;
|
|
|
|
namespace WingsEmu.Plugins.BasicImplementations.Event.Maps;
|
|
|
|
public class JoinMapEventHandler : IAsyncEventProcessor<JoinMapEvent>
|
|
{
|
|
private readonly IBuffFactory _buffFactory;
|
|
private readonly ICharacterAlgorithm _characterAlgorithm;
|
|
private readonly IGameLanguageService _languageService;
|
|
private readonly IMapManager _mapManager;
|
|
private readonly IMinilandManager _minilandManager;
|
|
private readonly IPortalFactory _portalFactory;
|
|
private readonly IRankingManager _rankingManager;
|
|
private readonly IReputationConfiguration _reputationConfiguration;
|
|
private readonly SerializableGameServer _serializableGameServer;
|
|
private readonly ISpPartnerConfiguration _spPartnerConfiguration;
|
|
|
|
public JoinMapEventHandler(ICharacterAlgorithm characterAlgorithm, IGameLanguageService languageService, ISpPartnerConfiguration spPartnerConfiguration,
|
|
IBuffFactory buffFactory, IMinilandManager minilandManager, IMapManager mapManager,
|
|
IReputationConfiguration reputationConfiguration, IPortalFactory portalFactory, IRankingManager rankingManager,
|
|
SerializableGameServer serializableGameServer)
|
|
{
|
|
_characterAlgorithm = characterAlgorithm;
|
|
_languageService = languageService;
|
|
_spPartnerConfiguration = spPartnerConfiguration;
|
|
_buffFactory = buffFactory;
|
|
_minilandManager = minilandManager;
|
|
_mapManager = mapManager;
|
|
_reputationConfiguration = reputationConfiguration;
|
|
_portalFactory = portalFactory;
|
|
_rankingManager = rankingManager;
|
|
_serializableGameServer = serializableGameServer;
|
|
}
|
|
|
|
public async Task HandleAsync(JoinMapEvent e, CancellationToken cancellation)
|
|
{
|
|
IClientSession session = e.Sender;
|
|
IMapInstance mapInstance;
|
|
|
|
if (e.JoinedMapInstance != default)
|
|
{
|
|
mapInstance = e.JoinedMapInstance;
|
|
}
|
|
else if (e.JoinedMapGuid != default)
|
|
{
|
|
mapInstance = _mapManager.GetMapInstance(e.JoinedMapGuid);
|
|
}
|
|
else
|
|
{
|
|
mapInstance = _mapManager.GetBaseMapInstanceByMapId(e.JoinedMapId);
|
|
}
|
|
|
|
if (mapInstance == default)
|
|
{
|
|
/*if (_serializableGameServer.ChannelType == GameChannelType.ACT_4 && e.JoinedMapId is 145)
|
|
{
|
|
await session.EmitEventAsync(new PlayerReturnFromAct4Event());
|
|
return;
|
|
}*/
|
|
|
|
Log.Error(
|
|
"Seems like the provided information for the Map to join doesn't equal to any known instance." +
|
|
$"JoinedMapInstance: {e.JoinedMapInstance?.Id}|{e.JoinedMapInstance?.MapId}" +
|
|
$"JoinedMapGuid: {e.JoinedMapGuid}" +
|
|
$"JoinedMapId: {e.JoinedMapId}" +
|
|
$"ChannelType: {_serializableGameServer.ChannelType} | ChannelId: {_serializableGameServer.ChannelId}", new ArgumentNullException("MapInstance"));
|
|
//session.ChangeToLastBaseMap();
|
|
return;
|
|
}
|
|
|
|
Stopwatch watch = null;
|
|
|
|
if (session.DebugMode)
|
|
{
|
|
watch = Stopwatch.StartNew();
|
|
}
|
|
|
|
if (session.CurrentMapInstance != default)
|
|
{
|
|
await session.EmitEventAsync(new LeaveMapEvent());
|
|
}
|
|
|
|
ServerSideJoinMap(e, mapInstance);
|
|
IFamily family = session.PlayerEntity.Family;
|
|
|
|
await SendJoinMapInformation(e, family);
|
|
await BroadcastAndReceiveJoinInformation(e, family);
|
|
await session.EmitEventAsync(new JoinMapEndEvent(mapInstance));
|
|
|
|
await session.PlayerEntity.CheckAct52Buff(_buffFactory);
|
|
await session.PlayerEntity.CheckAct4Buff(_buffFactory);
|
|
await session.PlayerEntity.CheckPvPBuff();
|
|
await session.EmitEventAsync(new VehicleCheckMapSpeedEvent());
|
|
session.PlayerEntity.ShadowAppears(false);
|
|
|
|
session.PlayerEntity.LastMapChange = DateTime.UtcNow;
|
|
|
|
if (session.DebugMode && watch != null)
|
|
{
|
|
watch.Stop();
|
|
session.SendDebugMessage($"The map change took: {watch.ElapsedMilliseconds.ToString()}ms");
|
|
}
|
|
|
|
if (session.PlayerEntity.IsRaidLeader(session.PlayerEntity.Id))
|
|
{
|
|
if (session.PlayerEntity.Raid?.Instance?.RaidSubInstances == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (session.PlayerEntity.Raid.Instance.RaidSubInstances.TryGetValue(session.CurrentMapInstance.Id, out RaidSubInstance subInstance) && subInstance != null)
|
|
{
|
|
subInstance.IsDiscoveredByLeader = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ServerSideJoinMap(JoinMapEvent e, IMapInstance mapInstance)
|
|
{
|
|
IClientSession session = e.Sender;
|
|
|
|
if (mapInstance.HasMapFlag(MapFlags.IS_BASE_MAP) && mapInstance.MapInstanceType != MapInstanceType.TimeSpaceInstance)
|
|
{
|
|
session.PlayerEntity.MapId = mapInstance.MapId;
|
|
}
|
|
|
|
if (mapInstance.HasMapFlag(MapFlags.IS_BASE_MAP) && mapInstance.MapInstanceType != MapInstanceType.TimeSpaceInstance && e.X != null && e.Y != null)
|
|
{
|
|
session.PlayerEntity.MapX = e.X.Value;
|
|
session.PlayerEntity.MapY = e.Y.Value;
|
|
}
|
|
|
|
if (e.X != null && e.Y != null)
|
|
{
|
|
session.PlayerEntity.Position = new Position(e.X.Value, e.Y.Value);
|
|
}
|
|
|
|
switch (mapInstance.MapInstanceType)
|
|
{
|
|
case MapInstanceType.Miniland:
|
|
Game.Configurations.Miniland.Miniland minilandConfiguration = _minilandManager.GetMinilandConfiguration(mapInstance);
|
|
session.PlayerEntity.Position = new Position(minilandConfiguration.ArrivalSerializablePosition.X, minilandConfiguration.ArrivalSerializablePosition.Y);
|
|
|
|
if (mapInstance.Id == session.PlayerEntity.Miniland.Id)
|
|
{
|
|
foreach (IMateEntity mate in session.PlayerEntity.MateComponent.GetMates(x => !x.IsTeamMember))
|
|
{
|
|
session.SendCondMate(mate);
|
|
if (mapInstance.GetMateById(mate.Id) != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
mapInstance.AddMate(mate);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case MapInstanceType.ArenaInstance:
|
|
session.PlayerEntity.ArenaImmunity = DateTime.UtcNow;
|
|
session.SendArenaStatistics(false, session.PlayerEntity.GetGroup());
|
|
break;
|
|
case MapInstanceType.TimeSpaceInstance:
|
|
|
|
List<INpcEntity> partners = session.PlayerEntity.TimeSpaceComponent.Partners;
|
|
if (!partners.Any())
|
|
{
|
|
break;
|
|
}
|
|
|
|
foreach (INpcEntity partner in partners)
|
|
{
|
|
partner.ChangeMapInstance(mapInstance);
|
|
mapInstance.AddNpc(partner);
|
|
partner.Position = session.PlayerEntity.Position;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
session.PlayerEntity.ArenaDeaths = 0;
|
|
session.PlayerEntity.ArenaKills = 0;
|
|
session.PlayerEntity.TimeSpaceComponent.Partners.Clear();
|
|
break;
|
|
}
|
|
|
|
session.PlayerEntity.MapInstanceId = mapInstance.Id;
|
|
session.CurrentMapInstance = session.PlayerEntity.MapInstance;
|
|
|
|
session.CurrentMapInstance.RegisterSession(session);
|
|
|
|
HashSet<Guid> toRemovePlayer = new();
|
|
foreach (Guid id in session.PlayerEntity.AggroedEntities)
|
|
{
|
|
IMonsterEntity monster = mapInstance.GetMonsterByUniqueId(id);
|
|
if (monster != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
toRemovePlayer.Add(id);
|
|
}
|
|
|
|
foreach (Guid remove in toRemovePlayer)
|
|
{
|
|
session.PlayerEntity.AggroedEntities.Remove(remove);
|
|
}
|
|
|
|
foreach (IMateEntity mate in session.PlayerEntity.MateComponent.TeamMembers())
|
|
{
|
|
HashSet<Guid> toRemoveMate = new();
|
|
foreach (Guid id in mate.AggroedEntities)
|
|
{
|
|
IMonsterEntity monster = mapInstance.GetMonsterByUniqueId(id);
|
|
if (monster != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
toRemoveMate.Add(id);
|
|
}
|
|
|
|
foreach (Guid remove in toRemoveMate)
|
|
{
|
|
mate.AggroedEntities.Remove(remove);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task SendJoinMapInformation(JoinMapEvent e, IFamily family)
|
|
{
|
|
IClientSession session = e.Sender;
|
|
|
|
session.SendCInfoPacket(family, _reputationConfiguration, _rankingManager.TopReputation);
|
|
session.SendCModePacket();
|
|
session.RefreshEquipment();
|
|
session.RefreshLevel(_characterAlgorithm);
|
|
session.RefreshStat();
|
|
session.SendAtPacket();
|
|
|
|
session.SendGidxPacket(family, _languageService);
|
|
session.SendCondPacket();
|
|
session.SendCondPacket();
|
|
session.SendTitInfoPacket();
|
|
session.SendEqPacket();
|
|
session.SendCMapPacket(true);
|
|
|
|
if (session.CurrentMapInstance.MapId == (int)MapIds.HATUS_BOSS_MAP)
|
|
{
|
|
session.SendEmptyHatusHeads();
|
|
}
|
|
|
|
session.RefreshStatChar();
|
|
if (session.CurrentMapInstance.MapInstanceType != MapInstanceType.TimeSpaceInstance)
|
|
{
|
|
session.SendRsfpPacket();
|
|
}
|
|
|
|
session.RefreshFairy();
|
|
session.SendCharConstBuffEffect();
|
|
session.RefreshZoom();
|
|
|
|
await SendPartyInfo(e);
|
|
|
|
session.SendPacket(session.GenerateAct6EmptyPacket());
|
|
|
|
session.SendCondPacket();
|
|
|
|
session.SendPacket(session.CurrentMapInstance.GenerateMapDesignObjects());
|
|
foreach (MapDesignObject mapObject in session.CurrentMapInstance.MapDesignObjects)
|
|
{
|
|
session.SendPacket(mapObject.GenerateEffect(false));
|
|
}
|
|
|
|
if (session.CurrentMapInstance.MapInstanceType == MapInstanceType.TimeSpaceInstance && session.PlayerEntity.TimeSpaceComponent.IsInTimeSpaceParty)
|
|
{
|
|
IClientSession[] timeSpaceMembers = session.PlayerEntity.TimeSpaceComponent.TimeSpace.Members.ToArray();
|
|
foreach (IClientSession member in timeSpaceMembers)
|
|
{
|
|
member.SendPacket(e.JoinedMapInstance.GenerateRsfn(isVisit: true));
|
|
}
|
|
|
|
if (session.PlayerEntity.TimeSpaceComponent.TimeSpace.Started)
|
|
{
|
|
session.SendRsfpPacket();
|
|
}
|
|
}
|
|
|
|
session.SendPackets(session.CurrentMapInstance.GetEntitiesOnMapPackets());
|
|
|
|
foreach (IPortalEntity p in session.CurrentMapInstance.GenerateMinilandEntryPortals(session.PlayerEntity.Miniland, _portalFactory))
|
|
{
|
|
session.SendPacket(p.GenerateGp());
|
|
}
|
|
|
|
session.SendTimeSpacePortals();
|
|
|
|
session.TrySendScalPacket();
|
|
session.SendDancePacket(session.CurrentMapInstance.IsDance);
|
|
|
|
if (session.PlayerEntity.ShowRaidDeathInfo)
|
|
{
|
|
session.PlayerEntity.ShowRaidDeathInfo = false;
|
|
session.SendInfo(session.GetLanguage(GameDialogKey.RAID_INFO_NO_LIVES_LEFT));
|
|
}
|
|
|
|
List<INpcEntity> partners = session.PlayerEntity.TimeSpaceComponent.Partners;
|
|
if (partners.Any())
|
|
{
|
|
foreach (INpcEntity partner in partners)
|
|
{
|
|
session.PlayerEntity.MapInstance.Broadcast(x => partner.GenerateIn());
|
|
session.SendMateControl(partner);
|
|
session.SendCondMate(partner);
|
|
}
|
|
}
|
|
|
|
await MinilandInfoSend(e);
|
|
}
|
|
|
|
private async Task MinilandInfoSend(PlayerEvent e)
|
|
{
|
|
if (e.Sender.CurrentMapInstance?.MapInstanceType != MapInstanceType.Miniland)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IClientSession session = e.Sender;
|
|
IClientSession minilandOwner = _minilandManager.GetSessionByMiniland(session.CurrentMapInstance);
|
|
if (minilandOwner == default)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (session.PlayerEntity.Miniland.Id != minilandOwner.PlayerEntity.Miniland.Id)
|
|
{
|
|
if (minilandOwner.PlayerEntity.MinilandState == MinilandState.LOCK && !session.IsGameMaster()
|
|
|| minilandOwner.PlayerEntity.MinilandState == MinilandState.PRIVATE && !session.PlayerEntity.IsFriend(minilandOwner.PlayerEntity.Id)
|
|
&& !session.PlayerEntity.IsMarried(minilandOwner.PlayerEntity.Id) && !session.IsGameMaster())
|
|
{
|
|
session.ChangeToLastBaseMap();
|
|
session.SendMsg(_languageService.GetLanguage(GameDialogKey.MINILAND_SHOUTMESSAGE_CLOSED, session.UserLanguage), MsgMessageType.Middle);
|
|
return;
|
|
}
|
|
|
|
int count = minilandOwner.PlayerEntity.Miniland.Sessions.Count(x => x.PlayerEntity.Id != minilandOwner.PlayerEntity.Id && !x.GmMode);
|
|
int capacity = _minilandManager.GetMinilandMaximumCapacity(minilandOwner.PlayerEntity.Id);
|
|
|
|
if (count > capacity)
|
|
{
|
|
session.ChangeToLastBaseMap();
|
|
session.SendMsg(_languageService.GetLanguage(GameDialogKey.MINILAND_SHOUTMESSAGE_FULL, session.UserLanguage), MsgMessageType.Middle);
|
|
return;
|
|
}
|
|
|
|
session.SendMsg(minilandOwner.GetMinilandCleanMessage(_languageService), MsgMessageType.Middle);
|
|
|
|
if (!session.IsGameMaster())
|
|
{
|
|
await _minilandManager.IncreaseMinilandVisitCounter(minilandOwner.PlayerEntity.Id);
|
|
}
|
|
|
|
session.SendMinilandPublicInformation(_minilandManager, _languageService);
|
|
minilandOwner.SendMinilandPrivateInformation(_minilandManager, _languageService);
|
|
}
|
|
else
|
|
{
|
|
session.SendMinilandPrivateInformation(_minilandManager, _languageService);
|
|
}
|
|
|
|
foreach (IMateEntity mate in minilandOwner.PlayerEntity.MateComponent.GetMates())
|
|
{
|
|
if (!mate.IsAlive() && session.PlayerEntity.Id == minilandOwner.PlayerEntity.Id)
|
|
{
|
|
await session.EmitEventAsync(new MateReviveEvent(mate, false));
|
|
continue;
|
|
}
|
|
|
|
if (!mate.IsTeamMember)
|
|
{
|
|
session.SendPacket(mate.GenerateIn(_languageService, session.UserLanguage, _spPartnerConfiguration));
|
|
}
|
|
}
|
|
|
|
long visitCount = await _minilandManager.GetMinilandVisitCounter(session.PlayerEntity.Id);
|
|
session.SendInformationChatMessage(_languageService.GetLanguageFormat(GameDialogKey.MINILAND_MESSAGE_VISITOR, session.UserLanguage,
|
|
session.PlayerEntity.LifetimeStats.TotalMinilandVisits, visitCount));
|
|
}
|
|
|
|
private async Task SendPartyInfo(PlayerEvent e)
|
|
{
|
|
IClientSession session = e.Sender;
|
|
|
|
session.SendPstPackets();
|
|
if (!session.PlayerEntity.IsOnVehicle && !session.PlayerEntity.CheatComponent.IsInvisible)
|
|
{
|
|
foreach (IMateEntity mate in session.PlayerEntity.MateComponent.TeamMembers())
|
|
{
|
|
if (!mate.IsAlive())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (mate.IsSitting)
|
|
{
|
|
await session.EmitEventAsync(new MateRestEvent
|
|
{
|
|
MateEntity = mate,
|
|
Force = true
|
|
});
|
|
}
|
|
|
|
mate.TeleportNearCharacter();
|
|
session.SendPacket(mate.GenerateIn(_languageService, session.UserLanguage, _spPartnerConfiguration));
|
|
session.TrySendScalPacket(mate);
|
|
session.SendMateControl(mate);
|
|
session.SendTargetConstBuffEffects(mate);
|
|
session.SendCondMate(mate);
|
|
}
|
|
}
|
|
|
|
session.RefreshParty(_spPartnerConfiguration);
|
|
|
|
if (!session.PlayerEntity.IsInGroup())
|
|
{
|
|
return;
|
|
}
|
|
|
|
PlayerGroup group = session.PlayerEntity.GetGroup();
|
|
|
|
foreach (IPlayerEntity member in group.Members)
|
|
{
|
|
if (session.PlayerEntity.Id == member.Id)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
member.Session.RefreshParty(_spPartnerConfiguration);
|
|
}
|
|
|
|
session.CurrentMapInstance?.Broadcast(session.GeneratePidx(), new ExceptSessionBroadcast(session));
|
|
}
|
|
|
|
private async Task BroadcastAndReceiveJoinInformation(JoinMapEvent e, IFamily family)
|
|
{
|
|
IClientSession session = e.Sender;
|
|
foreach (IClientSession currentPlayerOnMap in session.CurrentMapInstance.Sessions)
|
|
{
|
|
if (currentPlayerOnMap.PlayerEntity.Id == session.PlayerEntity.Id)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!currentPlayerOnMap.PlayerEntity.CheatComponent.IsInvisible)
|
|
{
|
|
await TargetedSendJoinInformation(session, currentPlayerOnMap, currentPlayerOnMap.PlayerEntity.Family, true);
|
|
}
|
|
|
|
if (!session.PlayerEntity.CheatComponent.IsInvisible)
|
|
{
|
|
await TargetedSendJoinInformation(currentPlayerOnMap, session, family, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task TargetedSendJoinInformation(IClientSession receiverSession, IClientSession targetSession, IFamily family, bool showInEffect)
|
|
{
|
|
bool isAnonymous = targetSession.CurrentMapInstance.HasMapFlag(MapFlags.ACT_4)
|
|
&& receiverSession.PlayerEntity.Faction != targetSession.PlayerEntity.Faction && !receiverSession.IsGameMaster();
|
|
|
|
receiverSession.SendTargetInPacket(targetSession, _reputationConfiguration, _rankingManager.TopReputation, isAnonymous, showInEffect);
|
|
if (!isAnonymous)
|
|
{
|
|
receiverSession.SendTargetGidxPacket(targetSession, family, _languageService);
|
|
}
|
|
|
|
receiverSession.SendTargetTitInfoPacket(targetSession);
|
|
receiverSession.SendTargetConstBuffEffects(targetSession.PlayerEntity);
|
|
|
|
if (targetSession.PlayerEntity.HasShopOpened)
|
|
{
|
|
receiverSession.SendPlayerShopTitle(targetSession);
|
|
receiverSession.SendPacket(targetSession.GeneratePlayerFlag((long)DialogVnums.SHOP_PLAYER));
|
|
}
|
|
|
|
if (targetSession.PlayerEntity.IsOnVehicle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (IMateEntity mate in targetSession.PlayerEntity.MateComponent.TeamMembers())
|
|
{
|
|
string inPacket = mate.GenerateIn(_languageService, receiverSession.UserLanguage, _spPartnerConfiguration, isAnonymous);
|
|
receiverSession.SendPacket(inPacket);
|
|
receiverSession.SendTargetConstBuffEffects(mate);
|
|
}
|
|
}
|
|
} |