using Yuanshen.Gameserver.Network.Session; using Yuanshen.Protocol; namespace Yuanshen.Gameserver.Game; internal sealed class SceneManager { private const int LevelSyncAttempts = 3; private readonly NetSession _session; private readonly Player _player; private readonly GameState _state; public SceneManager(NetSession session, Player player, GameState state) { _session = session; _player = player; _state = state; } public SceneTeamUpdateNotify BuildSceneTeamUpdate() { SceneTeamUpdateNotify notify = new(); foreach (GameAvatar avatar in _player.TeamAvatars) { GameWeapon weapon = _player.FindWeapon(avatar.WeaponGuid)!; notify.SceneTeamAvatarList.Add(new SceneTeamAvatar { PlayerId = _player.Uid, AvatarGuid = avatar.Guid, SceneId = GameState.SceneId, EntityId = avatar.EntityId, WeaponGuid = avatar.WeaponGuid, WeaponEntityId = avatar.WeaponEntityId, IsPlayerCurAvatar = avatar.Guid == _player.CurrentAvatarGuid, IsOnScene = avatar.Guid == _player.CurrentAvatarGuid, AbilityControlBlock = avatar.BuildAbilityControlBlock(), AvatarAbilityInfo = new AbilitySyncStateInfo { IsInited = true }, WeaponAbilityInfo = new AbilitySyncStateInfo { IsInited = true }, SceneEntityInfo = avatar.AsSceneEntity(_state.CurrentPos, weapon, EquippedReliquaries(avatar)) }); } return notify; } public PlayerEnterSceneInfoNotify BuildEnterSceneInfo() { GameAvatar active = _player.ActiveAvatar!; PlayerEnterSceneInfoNotify notify = new() { CurAvatarEntityId = active.EntityId, EnterSceneToken = _state.EnterSceneToken, MpLevelEntityInfo = new MPLevelEntityInfo { EntityId = Player.MpLevelEntityId, AuthorityPeerId = Player.PeerId, AbilityInfo = new AbilitySyncStateInfo { IsInited = true } }, TeamEnterInfo = new TeamEnterSceneInfo { TeamEntityId = Player.TeamEntityId, TeamAbilityInfo = new AbilitySyncStateInfo { IsInited = true } } }; foreach (GameAvatar avatar in _player.TeamAvatars) { notify.AvatarEnterInfo.Add(new AvatarEnterSceneInfo { AvatarGuid = avatar.Guid, AvatarEntityId = avatar.EntityId, WeaponGuid = avatar.WeaponGuid, WeaponEntityId = avatar.WeaponEntityId, AvatarAbilityInfo = new AbilitySyncStateInfo { IsInited = true }, WeaponAbilityInfo = new AbilitySyncStateInfo { IsInited = true } }); } return notify; } public async ValueTask SpawnActiveAvatarAsync(VisionType visionType) { GameAvatar active = _player.ActiveAvatar!; GameWeapon weapon = _player.FindWeapon(active.WeaponGuid)!; await _session.NotifyAsync(CmdType.SceneEntityAppearNotify, new SceneEntityAppearNotify { AppearType = visionType, EntityList = { active.AsSceneEntity(_state.CurrentPos, weapon, EquippedReliquaries(active)) } }); } public async ValueTask DespawnMonsterAsync(GameMonster monster) { _player.Monsters.Remove(monster); SceneEntityDisappearNotify notify = new() { DisappearType = VisionType.VisionRemove }; notify.EntityList.Add(monster.EntityId); await _session.NotifyAsync(CmdType.SceneEntityDisappearNotify, notify); } public async ValueTask AppearMonsterAsync(GameMonster monster) { SceneEntityAppearNotify notify = new() { AppearType = VisionType.VisionBorn }; notify.EntityList.Add(monster.AsSceneEntity()); await _session.NotifyAsync(CmdType.SceneEntityAppearNotify, notify); } public async ValueTask AppearGadgetAsync(GameGadget gadget) { SceneEntityAppearNotify notify = new() { AppearType = VisionType.VisionBorn }; notify.EntityList.Add(gadget.AsSceneEntity()); await _session.NotifyAsync(CmdType.SceneEntityAppearNotify, notify); } public async ValueTask DespawnGadgetAsync(GameGadget gadget, VisionType visionType) { _player.Gadgets.Remove(gadget); SceneEntityDisappearNotify notify = new() { DisappearType = visionType }; notify.EntityList.Add(gadget.EntityId); await _session.NotifyAsync(CmdType.SceneEntityDisappearNotify, notify); } public async ValueTask SetGadgetStateAsync(GameGadget gadget, uint state, bool enableInteract) { await _session.NotifyAsync(CmdType.GadgetStateNotify, new GadgetStateNotify { GadgetEntityId = gadget.EntityId, GadgetState = state, IsEnableInteract = enableInteract }); } public async ValueTask SyncMonsterLevelAsync(GameMonster monster) { await _session.NotifyAsync(CmdType.EntityPropNotify, new EntityPropNotify { EntityId = monster.EntityId, PropMap = { { 4001u, new PropValue { Type = 4001, Ival = monster.Level, Val = monster.Level } } } }); } public async ValueTask SyncPendingMonsterLevelsAsync() { foreach (GameMonster monster in _player.Monsters.Where(m => m.IsAlive && m.LevelSyncCount < LevelSyncAttempts).ToList()) { monster.LevelSyncCount++; await SyncMonsterLevelAsync(monster); } } public async ValueTask ClearMonstersAsync() { if (_player.Monsters.Count == 0) return; SceneEntityDisappearNotify notify = new() { DisappearType = VisionType.VisionRemove }; foreach (GameMonster monster in _player.Monsters) notify.EntityList.Add(monster.EntityId); _player.Monsters.Clear(); await _session.NotifyAsync(CmdType.SceneEntityDisappearNotify, notify); } public async ValueTask TeleportAsync(Vector pos) { _state.UpdatePosition(pos); await _session.NotifyAsync(CmdType.PlayerEnterSceneNotify, new PlayerEnterSceneNotify { SceneId = GameState.SceneId, Pos = pos, SceneBeginTime = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), Type = EnterType.EnterGoto, TargetUid = _player.Uid, EnterSceneToken = _state.NextEnterSceneToken(), WorldLevel = 0, EnterReason = 1 }); } public async ValueTask HealTeamAsync() { foreach (GameAvatar avatar in _player.TeamAvatars) await _session.NotifyAsync(CmdType.EntityFightPropUpdateNotify, new EntityFightPropUpdateNotify { EntityId = avatar.EntityId, FightPropMap = { { 1010u, 999999f }, { 2000u, 999999f } } }); } public async ValueTask RefreshAvatarDataAsync() { AvatarDataNotify data = new() { CurAvatarTeamId = _player.CurrentTeamId, ChooseAvatarGuid = _player.CurrentAvatarGuid }; foreach (uint flycloak in Player.Flycloaks) data.OwnedFlycloakList.Add(flycloak); foreach (GameAvatar avatar in _player.Avatars) data.AvatarList.Add(avatar.AsAvatarInfo()); AvatarTeam team = new() { TeamName = "Team " + _player.CurrentTeamId }; team.AvatarGuidList.AddRange(_player.TeamAvatarGuids); data.AvatarTeamMap.Add(_player.CurrentTeamId, team); await _session.NotifyAsync(CmdType.AvatarDataNotify, data); } public async ValueTask RefreshTeamAsync() => await _session.NotifyAsync(CmdType.SceneTeamUpdateNotify, BuildSceneTeamUpdate()); public async ValueTask SetupTeamAsync(uint teamId, IReadOnlyList guids, ulong curAvatarGuid) { ulong previousActiveGuid = _player.CurrentAvatarGuid; _player.CurrentTeamId = teamId; _player.TeamAvatarGuids.Clear(); _player.TeamAvatarGuids.AddRange(guids); _player.CurrentAvatarGuid = curAvatarGuid != 0 ? curAvatarGuid : guids[0]; AvatarTeam team = new() { TeamName = "Team " + teamId }; team.AvatarGuidList.AddRange(guids); await _session.NotifyAsync(CmdType.AvatarTeamUpdateNotify, new AvatarTeamUpdateNotify { AvatarTeamMap = { { teamId, team } } }); if (previousActiveGuid != _player.CurrentAvatarGuid) await DisappearAsync(previousActiveGuid, VisionType.VisionReplace); await _session.NotifyAsync(CmdType.SceneTeamUpdateNotify, BuildSceneTeamUpdate()); if (previousActiveGuid != _player.CurrentAvatarGuid) await SpawnActiveAvatarAsync(VisionType.VisionReplace); } public async ValueTask ChangeAvatarAsync(ulong newGuid) { if (_player.CurrentAvatarGuid == newGuid) return; await DisappearAsync(_player.CurrentAvatarGuid, VisionType.VisionReplace); _player.CurrentAvatarGuid = newGuid; await SpawnActiveAvatarAsync(VisionType.VisionReplace); } private IEnumerable EquippedReliquaries(GameAvatar avatar) => avatar.ReliquaryGuids.Values.Select(_player.FindReliquary).OfType(); private async ValueTask DisappearAsync(ulong avatarGuid, VisionType visionType) { GameAvatar? avatar = _player.FindAvatar(avatarGuid); if (avatar is null) return; await _session.NotifyAsync(CmdType.SceneEntityDisappearNotify, new SceneEntityDisappearNotify { DisappearType = visionType, EntityList = { avatar.EntityId } }); } }