diff --git a/GameServer/Controllers/Attributes/GameEventAttribute.cs b/GameServer/Controllers/Attributes/GameEventAttribute.cs index 4ba6ff3..e596d9c 100644 --- a/GameServer/Controllers/Attributes/GameEventAttribute.cs +++ b/GameServer/Controllers/Attributes/GameEventAttribute.cs @@ -1,4 +1,4 @@ -using GameServer.Controllers.Event; +using GameServer.Systems.Event; namespace GameServer.Controllers.Attributes; diff --git a/GameServer/Controllers/CreatureController.cs b/GameServer/Controllers/CreatureController.cs new file mode 100644 index 0000000..fa4049d --- /dev/null +++ b/GameServer/Controllers/CreatureController.cs @@ -0,0 +1,147 @@ +using GameServer.Controllers.Attributes; +using GameServer.Models; +using GameServer.Network; +using GameServer.Network.Messages; +using GameServer.Systems.Entity; +using GameServer.Systems.Entity.Component; +using GameServer.Systems.Event; +using Protocol; + +namespace GameServer.Controllers; +internal class CreatureController : Controller +{ + private readonly EntitySystem _entitySystem; + private readonly EntityFactory _entityFactory; + private readonly ModelManager _modelManager; + + public CreatureController(PlayerSession session, EntitySystem entitySystem, EntityFactory entityFactory, ModelManager modelManager) : base(session) + { + _entitySystem = entitySystem; + _entityFactory = entityFactory; + _modelManager = modelManager; + } + + public async Task JoinScene(int instanceId) + { + _modelManager.Creature.SetSceneLoadingData(instanceId); + CreateInitialPlayer(); + + await Session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify + { + MaxEntityId = 10000, + TransitionOption = new TransitionOptionPb + { + TransitionType = (int)TransitionType.Empty + }, + SceneInfo = CreateSceneInfo() + }); + } + + [NetEvent(MessageId.EntityActiveRequest)] + public async Task OnEntityActiveRequest(EntityActiveRequest request) + { + EntityActiveResponse response; + + EntityBase? entity = _entitySystem.Get(request.EntityId); + if (entity != null) + { + _entitySystem.Activate(entity); + response = new EntityActiveResponse + { + ErrorCode = (int)ErrorCode.Success + }; + + response.ComponentPbs.AddRange(entity.ComponentSystem.Pb); + } + else + { + response = new EntityActiveResponse { ErrorCode = (int)ErrorCode.ErrActionEntityNoExist }; + } + + await OnVisionSkillChanged(); + return Response(MessageId.EntityActiveResponse, response); + } + + [NetEvent(MessageId.SceneLoadingFinishRequest)] + public ResponseMessage OnSceneLoadingFinishRequest() + { + _modelManager.Creature.OnWorldDone(); + + return Response(MessageId.SceneLoadingFinishResponse, new SceneLoadingFinishResponse()); + } + + [GameEvent(GameEventType.VisionSkillChanged)] + public async Task OnVisionSkillChanged() + { + PlayerEntity? playerEntity = GetPlayerEntity(_modelManager.Player.Id); + if (playerEntity == null) return; + + EntityVisionSkillComponent visionSkillComponent = playerEntity.ComponentSystem.Get(); + + VisionSkillChangeNotify skillChangeNotify = new() { EntityId = playerEntity.Id }; + skillChangeNotify.VisionSkillInfos.AddRange(visionSkillComponent.Skills); + + await Session.Push(MessageId.VisionSkillChangeNotify, skillChangeNotify); + } + + public PlayerEntity? GetPlayerEntity(int playerId) + { + return _entitySystem.EnumerateEntities().FirstOrDefault(entity => entity is PlayerEntity p && p.PlayerId == playerId) as PlayerEntity; + } + + private SceneInformation CreateSceneInfo() + { + SceneInformation scene = new() + { + InstanceId = _modelManager.Creature.InstanceId, + OwnerId = _modelManager.Creature.OwnerId, + CurContextId = _modelManager.Player.Id, + TimeInfo = new(), + AoiData = new(), + PlayerInfos = + { + new ScenePlayerInformation + { + PlayerId = _modelManager.Player.Id, + Level = 1, + IsOffline = false, + Location = new() + { + X = 4000, + Y = -2000, + Z = 260 + }, + FightRoleInfos = + { + new FightRoleInformation + { + EntityId = 1, + CurHp = 1000, + MaxHp = 1000, + IsControl = true, + RoleId = _modelManager.Player.CharacterId, + RoleLevel = 1, + } + }, + PlayerName = _modelManager.Player.Name + } + } + }; + + scene.AoiData.Entities.AddRange(_entitySystem.Pb); + return scene; + } + + private void CreateInitialPlayer() + { + PlayerEntity entity = _entityFactory.CreatePlayer(1601, _modelManager.Player.Id); + entity.Pos = new() + { + X = 4000, + Y = -2000, + Z = 260 + }; + + _entitySystem.Create(entity); + } +} diff --git a/GameServer/Controllers/Event/GameEventType.cs b/GameServer/Controllers/Event/GameEventType.cs deleted file mode 100644 index 63594c6..0000000 --- a/GameServer/Controllers/Event/GameEventType.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace GameServer.Controllers.Event; -internal enum GameEventType -{ - Login = 1, - EnterGame -} diff --git a/GameServer/Controllers/Factory/EventHandlerFactory.cs b/GameServer/Controllers/Factory/EventHandlerFactory.cs index 9aff994..cd8984b 100644 --- a/GameServer/Controllers/Factory/EventHandlerFactory.cs +++ b/GameServer/Controllers/Factory/EventHandlerFactory.cs @@ -2,8 +2,8 @@ using System.Linq.Expressions; using System.Reflection; using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; using GameServer.Network.Messages; +using GameServer.Systems.Event; using Google.Protobuf; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/GameServer/Controllers/FunctionalController.cs b/GameServer/Controllers/FunctionalController.cs index 2174bc3..e40997e 100644 --- a/GameServer/Controllers/FunctionalController.cs +++ b/GameServer/Controllers/FunctionalController.cs @@ -1,6 +1,6 @@ using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; using GameServer.Network; +using GameServer.Systems.Event; using Protocol; namespace GameServer.Controllers; diff --git a/GameServer/Controllers/LoginController.cs b/GameServer/Controllers/LoginController.cs index d4fcb4a..2fff24c 100644 --- a/GameServer/Controllers/LoginController.cs +++ b/GameServer/Controllers/LoginController.cs @@ -1,7 +1,7 @@ using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; using GameServer.Network; using GameServer.Network.Messages; +using GameServer.Systems.Event; using Microsoft.Extensions.Logging; using Protocol; diff --git a/GameServer/Controllers/Manager/ControllerManager.cs b/GameServer/Controllers/Manager/ControllerManager.cs index 34ea41c..0bd697b 100644 --- a/GameServer/Controllers/Manager/ControllerManager.cs +++ b/GameServer/Controllers/Manager/ControllerManager.cs @@ -1,6 +1,5 @@ -using GameServer.Controllers.Event; -using GameServer.Controllers.Factory; -using GameServer.Network; +using GameServer.Controllers.Factory; +using GameServer.Systems.Event; using Microsoft.Extensions.DependencyInjection; namespace GameServer.Controllers.Manager; diff --git a/GameServer/Controllers/PlayerInfoController.cs b/GameServer/Controllers/PlayerInfoController.cs index 7626377..5fd34a1 100644 --- a/GameServer/Controllers/PlayerInfoController.cs +++ b/GameServer/Controllers/PlayerInfoController.cs @@ -1,7 +1,7 @@ using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; using GameServer.Models; using GameServer.Network; +using GameServer.Systems.Event; using Protocol; namespace GameServer.Controllers; diff --git a/GameServer/Controllers/RoleController.cs b/GameServer/Controllers/RoleController.cs index af068c4..d488ef6 100644 --- a/GameServer/Controllers/RoleController.cs +++ b/GameServer/Controllers/RoleController.cs @@ -1,8 +1,8 @@ using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; using GameServer.Models; using GameServer.Network; using GameServer.Network.Messages; +using GameServer.Systems.Event; using Protocol; namespace GameServer.Controllers; diff --git a/GameServer/Controllers/RouletteController.cs b/GameServer/Controllers/RouletteController.cs index 0bab6a6..41698c5 100644 --- a/GameServer/Controllers/RouletteController.cs +++ b/GameServer/Controllers/RouletteController.cs @@ -1,7 +1,10 @@ using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; +using GameServer.Models; using GameServer.Network; using GameServer.Network.Messages; +using GameServer.Systems.Entity; +using GameServer.Systems.Entity.Component; +using GameServer.Systems.Event; using Protocol; namespace GameServer.Controllers; @@ -38,19 +41,15 @@ internal class RouletteController : Controller } [NetEvent(MessageId.VisionExploreSkillSetRequest)] - public async Task OnVisionExploreSkillSetRequest(VisionExploreSkillSetRequest request) + public async Task OnVisionExploreSkillSetRequest(VisionExploreSkillSetRequest request, CreatureController creatureController, ModelManager modelManager, EventSystem eventSystem) { - await Session.Push(MessageId.VisionSkillChangeNotify, new VisionSkillChangeNotify - { - EntityId = 1, - VisionSkillInfos = - { - new VisionSkillInformation - { - SkillId = request.SkillId - } - } - }); + PlayerEntity? playerEntity = creatureController.GetPlayerEntity(modelManager.Player.Id); + if (playerEntity == null) return Response(MessageId.VisionExploreSkillSetResponse, new VisionExploreSkillSetResponse { ErrCode = (int)ErrorCode.PlayerNotInAnyScene }); + + EntityVisionSkillComponent visionSkillComponent = playerEntity.ComponentSystem.Get(); + visionSkillComponent.SetExploreTool(request.SkillId); + + await eventSystem.Emit(GameEventType.VisionSkillChanged); return Response(MessageId.VisionExploreSkillSetResponse, new VisionExploreSkillSetResponse { diff --git a/GameServer/Controllers/WorldController.cs b/GameServer/Controllers/WorldController.cs index 8cde8c1..7953c50 100644 --- a/GameServer/Controllers/WorldController.cs +++ b/GameServer/Controllers/WorldController.cs @@ -1,8 +1,7 @@ using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; -using GameServer.Models; using GameServer.Network; using GameServer.Network.Messages; +using GameServer.Systems.Event; using Protocol; namespace GameServer.Controllers; @@ -14,187 +13,11 @@ internal class WorldController : Controller } [GameEvent(GameEventType.EnterGame)] - public async Task OnEnterGame(ModelManager modelManager) + public async Task OnEnterGame(CreatureController creatureController) { - PlayerModel player = modelManager.Player; - - await Session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify - { - MaxEntityId = 2, - TransitionOption = new TransitionOptionPb - { - TransitionType = (int)TransitionType.Empty - }, - SceneInfo = new SceneInformation - { - OwnerId = player.Id, - Mode = (int)SceneMode.Single, - InstanceId = 8, - AoiData = new PlayerSceneAoiData - { - GenIds = { 1 }, - Entities = - { - new EntityPb - { - EntityState = (int)EntityState.Born, - EntityType = (int)EEntityType.Player, - PlayerId = player.Id, - LivingStatus = (int)LivingStatus.Alive, - ConfigId = player.CharacterId, - ConfigType = (int)EntityConfigType.Character, - Id = 1, - IsVisible = true, - Pos = new Vector - { - X = 4000, - Y = -2000, - Z = 260 - }, - Rot = new(), - InitLinearVelocity = new(), - PrefabIncId = 0, - InitPos = new Vector - { - X = 4000, - Y = -2000, - Z = 260 - }, - ComponentPbs = - { - new EntityComponentPb - { - VisionSkillComponent = new VisionSkillComponentPb - { - VisionSkillInfos = - { - new VisionSkillInformation - { - SkillId = 1001 - } - } - } - }, - new EntityComponentPb - { - AttributeComponent = new AttributeComponentPb - { - GameAttributes = - { - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.Life, - BaseValue = 1000, - CurrentValue = 1000 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.LifeMax, - BaseValue = 1000, - CurrentValue = 1000 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.EnergyMax, - BaseValue = 10, - CurrentValue = 10 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.Energy, - BaseValue = 10, - CurrentValue = 10 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.SpecialEnergy3, - BaseValue = 10, - CurrentValue = 10 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.SpecialEnergy3Max, - BaseValue = 10, - CurrentValue = 10 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.AutoAttackSpeed, - BaseValue = 10000, - CurrentValue = 10000 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.CastAttackSpeed, - BaseValue = 10000, - CurrentValue = 10000 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.Atk, - BaseValue = 1, - CurrentValue = 1 - }, - new GameplayAttributeData - { - AttributeType = (int)EAttributeType.Lv, - BaseValue = 1, - CurrentValue = 1 - }, - }, - } - }, - new EntityComponentPb - { - ConcomitantsComponentPb = new ConcomitantsComponentPb - { - CustomEntityIds = {1}, - }, - } - }, - } - } - }, - TimeInfo = new SceneTimeInfo - { - Hour = 23 - }, - PlayerInfos = - { - new ScenePlayerInformation - { - PlayerId = player.Id, - Level = 1, - IsOffline = false, - Location = new() - { - X = 4000, - Y = -2000, - Z = 260 - }, - FightRoleInfos = - { - new FightRoleInformation - { - EntityId = 1, - CurHp = 1000, - MaxHp = 1000, - IsControl = true, - RoleId = player.CharacterId, - RoleLevel = 1, - } - }, - PlayerName = player.Name - } - }, - CurContextId = player.Id - } - }); + await creatureController.JoinScene(8); } - [NetEvent(MessageId.EntityActiveRequest)] - public ResponseMessage OnEntityActiveRequest() => Response(MessageId.EntityActiveResponse, new EntityActiveResponse()); - [NetEvent(MessageId.EntityOnLandedRequest)] public ResponseMessage OnEntityOnLandedRequest() => Response(MessageId.EntityOnLandedResponse, new EntityOnLandedResponse()); @@ -204,9 +27,6 @@ internal class WorldController : Controller [NetEvent(MessageId.EntityLoadCompleteRequest)] public ResponseMessage OnEntityLoadCompleteRequest() => Response(MessageId.EntityLoadCompleteResponse, new EntityLoadCompleteResponse()); - [NetEvent(MessageId.SceneLoadingFinishRequest)] - public ResponseMessage OnSceneLoadingFinishRequest() => Response(MessageId.SceneLoadingFinishResponse, new SceneLoadingFinishResponse()); - [NetEvent(MessageId.UpdateSceneDateRequest)] public ResponseMessage OnUpdateSceneDateRequest() => Response(MessageId.UpdateSceneDateResponse, new UpdateSceneDateResponse()); } diff --git a/GameServer/Models/CreatureModel.cs b/GameServer/Models/CreatureModel.cs new file mode 100644 index 0000000..ba92891 --- /dev/null +++ b/GameServer/Models/CreatureModel.cs @@ -0,0 +1,26 @@ +namespace GameServer.Models; +internal class CreatureModel +{ + public int OwnerId { get; } + + public int InstanceId { get; private set; } + public bool LoadingWorld { get; private set; } + + public long PlayerEntityId { get; set; } + + public CreatureModel(int ownerId) + { + OwnerId = ownerId; + } + + public void SetSceneLoadingData(int instanceId) + { + InstanceId = instanceId; + LoadingWorld = true; + } + + public void OnWorldDone() + { + LoadingWorld = false; + } +} diff --git a/GameServer/Models/ModelManager.cs b/GameServer/Models/ModelManager.cs index ae87dd1..ad135d5 100644 --- a/GameServer/Models/ModelManager.cs +++ b/GameServer/Models/ModelManager.cs @@ -1,16 +1,20 @@ using GameServer.Controllers.Attributes; -using GameServer.Controllers.Event; +using GameServer.Systems.Event; namespace GameServer.Models; internal class ModelManager { private PlayerModel? _playerModel; + private CreatureModel? _creatureModel; [GameEvent(GameEventType.Login)] public void OnLogin() { _playerModel = PlayerModel.CreateDefaultPlayer(); + _creatureModel = new CreatureModel(_playerModel.Id); } public PlayerModel Player => _playerModel ?? throw new InvalidOperationException($"Trying to access {nameof(PlayerModel)} instance before initialization!"); + + public CreatureModel Creature => _creatureModel ?? throw new InvalidOperationException($"Trying to access {nameof(CreatureModel)} instance before initialization!"); } diff --git a/GameServer/Program.cs b/GameServer/Program.cs index 1ff65ed..bc99585 100644 --- a/GameServer/Program.cs +++ b/GameServer/Program.cs @@ -1,5 +1,4 @@ -using GameServer.Controllers.Event; -using GameServer.Controllers.Factory; +using GameServer.Controllers.Factory; using GameServer.Controllers.Manager; using GameServer.Extensions; using GameServer.Models; @@ -7,6 +6,8 @@ using GameServer.Network; using GameServer.Network.Kcp; using GameServer.Network.Messages; using GameServer.Network.Rpc; +using GameServer.Systems.Entity; +using GameServer.Systems.Event; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -25,7 +26,8 @@ internal static class Program .AddScoped().AddSingleton() .AddScoped().AddScoped() .AddSingleton() - .AddScoped().AddScoped().AddScoped() + .AddScoped().AddScoped().AddScoped() + .AddScoped().AddScoped() .AddHostedService(); await builder.Build().RunAsync(); diff --git a/GameServer/Systems/Entity/Component/EntityAttributeComponent.cs b/GameServer/Systems/Entity/Component/EntityAttributeComponent.cs new file mode 100644 index 0000000..fac9ade --- /dev/null +++ b/GameServer/Systems/Entity/Component/EntityAttributeComponent.cs @@ -0,0 +1,72 @@ +using Protocol; + +namespace GameServer.Systems.Entity.Component; +internal class EntityAttributeComponent : EntityComponentBase +{ + public override EntityComponentType Type => EntityComponentType.Attribute; + + private readonly Dictionary _gameplayAttributes; + + public EntityAttributeComponent() + { + _gameplayAttributes = []; + } + + public void SetAttribute(EAttributeType type, int currentValue, int baseValue) + { + if (!_gameplayAttributes.TryGetValue(type, out GameplayAttributeData? attribute)) + { + attribute = new GameplayAttributeData + { + AttributeType = (int)type + }; + + _gameplayAttributes.Add(type, attribute); + } + + attribute.CurrentValue = currentValue; + attribute.BaseValue = baseValue; + } + + public void SetAttribute(EAttributeType type, int currentValue) + { + if (!_gameplayAttributes.TryGetValue(type, out GameplayAttributeData? attribute)) + { + SetAttribute(type, currentValue, currentValue); + return; + } + + attribute.CurrentValue = currentValue; + } + + public int GetAttribute(EAttributeType type) + { + if (_gameplayAttributes.TryGetValue(type, out GameplayAttributeData? attribute)) + return attribute.CurrentValue; + + return 0; + } + + public int GetAttributeBase(EAttributeType type) + { + if (_gameplayAttributes.TryGetValue(type, out GameplayAttributeData? attribute)) + return attribute.BaseValue; + + return 0; + } + + public override EntityComponentPb Pb + { + get + { + EntityComponentPb pb = new() + { + AttributeComponent = new() + }; + + pb.AttributeComponent.GameAttributes.AddRange(_gameplayAttributes.Values); + + return pb; + } + } +} diff --git a/GameServer/Systems/Entity/Component/EntityComponentBase.cs b/GameServer/Systems/Entity/Component/EntityComponentBase.cs new file mode 100644 index 0000000..df1dedf --- /dev/null +++ b/GameServer/Systems/Entity/Component/EntityComponentBase.cs @@ -0,0 +1,8 @@ +using Protocol; + +namespace GameServer.Systems.Entity.Component; +internal abstract class EntityComponentBase +{ + public abstract EntityComponentType Type { get; } + public abstract EntityComponentPb Pb { get; } +} diff --git a/GameServer/Systems/Entity/Component/EntityComponentSystem.cs b/GameServer/Systems/Entity/Component/EntityComponentSystem.cs new file mode 100644 index 0000000..f60090b --- /dev/null +++ b/GameServer/Systems/Entity/Component/EntityComponentSystem.cs @@ -0,0 +1,34 @@ +using Protocol; + +namespace GameServer.Systems.Entity.Component; +internal class EntityComponentSystem +{ + private readonly List _components; + + public EntityComponentSystem() + { + _components = []; + } + + public TEntityComponent Create() where TEntityComponent : EntityComponentBase, new() + { + if (_components.Any(component => component is TEntityComponent)) throw new InvalidOperationException($"Component of type {nameof(TEntityComponent)} already exists"); + + TEntityComponent component = new(); + _components.Add(component); + + return component; + } + + public TEntityComponent Get() where TEntityComponent : EntityComponentBase + { + return (_components.Single(component => component is TEntityComponent) as TEntityComponent)!; + } + + public bool TryGet(out TEntityComponent? component) where TEntityComponent : EntityComponentBase + { + return (component = _components.SingleOrDefault(component => component is TEntityComponent) as TEntityComponent) != null; + } + + public IEnumerable Pb => _components.Select(component => component.Pb); +} diff --git a/GameServer/Systems/Entity/Component/EntityComponentType.cs b/GameServer/Systems/Entity/Component/EntityComponentType.cs new file mode 100644 index 0000000..2eead36 --- /dev/null +++ b/GameServer/Systems/Entity/Component/EntityComponentType.cs @@ -0,0 +1,37 @@ +namespace GameServer.Systems.Entity.Component; +internal enum EntityComponentType +{ + Attribute, + Tag, + Trigger, + Summoner, + Part, + VisionSkill, + AnimationState, + BlackboardParam, + SysBuff, + ClientData, + MonsterWeapon, + MonsterAi, + FightBuff, + NearbyTracking, + Drop, + MonsterCapture, + LogicState, + Advice, + Lift, + Interact, + Equip, + BeControlled, + Concomitants, + TimelineTrack, + Summons, + EntityFsm, + Board, + PlacementItem, + StateTag, + MonsterGachaData, + Fan, + Npc, + Bubble +} diff --git a/GameServer/Systems/Entity/Component/EntityConcomitantsComponent.cs b/GameServer/Systems/Entity/Component/EntityConcomitantsComponent.cs new file mode 100644 index 0000000..2e966fe --- /dev/null +++ b/GameServer/Systems/Entity/Component/EntityConcomitantsComponent.cs @@ -0,0 +1,28 @@ +using Protocol; + +namespace GameServer.Systems.Entity.Component; +internal class EntityConcomitantsComponent : EntityComponentBase +{ + public List CustomEntityIds { get; } + + public EntityConcomitantsComponent() + { + CustomEntityIds = []; + } + + public override EntityComponentType Type => EntityComponentType.Concomitants; + + public override EntityComponentPb Pb + { + get + { + EntityComponentPb pb = new() + { + ConcomitantsComponentPb = new() + }; + + pb.ConcomitantsComponentPb.CustomEntityIds.AddRange(CustomEntityIds); + return pb; + } + } +} diff --git a/GameServer/Systems/Entity/Component/EntityVisionSkillComponent.cs b/GameServer/Systems/Entity/Component/EntityVisionSkillComponent.cs new file mode 100644 index 0000000..a352b23 --- /dev/null +++ b/GameServer/Systems/Entity/Component/EntityVisionSkillComponent.cs @@ -0,0 +1,37 @@ +using Protocol; + +namespace GameServer.Systems.Entity.Component; +internal class EntityVisionSkillComponent : EntityComponentBase +{ + public List Skills { get; } + + public EntityVisionSkillComponent() + { + Skills = []; + } + + public void SetExploreTool(int toolId) + { + Skills.Clear(); + Skills.Add(new VisionSkillInformation + { + SkillId = toolId + }); + } + + public override EntityComponentType Type => EntityComponentType.VisionSkill; + + public override EntityComponentPb Pb + { + get + { + EntityComponentPb pb = new() + { + VisionSkillComponent = new VisionSkillComponentPb() + }; + + pb.VisionSkillComponent.VisionSkillInfos.AddRange(Skills); + return pb; + } + } +} diff --git a/GameServer/Systems/Entity/EntityBase.cs b/GameServer/Systems/Entity/EntityBase.cs new file mode 100644 index 0000000..47af8c6 --- /dev/null +++ b/GameServer/Systems/Entity/EntityBase.cs @@ -0,0 +1,49 @@ +using GameServer.Systems.Entity.Component; +using Protocol; + +namespace GameServer.Systems.Entity; +internal abstract class EntityBase +{ + public long Id { get; } + public EntityComponentSystem ComponentSystem { get; } + + public Vector Pos { get; set; } + public Rotator Rot { get; set; } + + public bool Active { get; set; } + + public EntityState State { get; protected set; } + + public EntityBase(long id) + { + Id = id; + + Pos = new Vector(); + Rot = new Rotator(); + + ComponentSystem = new EntityComponentSystem(); + } + + public virtual void OnCreate() + { + State = EntityState.Born; + } + + public void Activate() + { + AddComponents(); + } + + public virtual void AddComponents() + { + // AddComponents. + } + + public virtual LivingStatus LivingStatus => LivingStatus.Alive; + public virtual bool IsVisible => true; + + public abstract EEntityType Type { get; } + public abstract EntityConfigType ConfigType { get; } + + public abstract EntityPb Pb { get; } +} diff --git a/GameServer/Systems/Entity/EntityFactory.cs b/GameServer/Systems/Entity/EntityFactory.cs new file mode 100644 index 0000000..563f107 --- /dev/null +++ b/GameServer/Systems/Entity/EntityFactory.cs @@ -0,0 +1,10 @@ +namespace GameServer.Systems.Entity; +internal class EntityFactory +{ + private long _entityIdCounter; + + public PlayerEntity CreatePlayer(int characterConfigId, int playerId) + => new(NextId(), characterConfigId, playerId); + + private long NextId() => Interlocked.Increment(ref _entityIdCounter); +} diff --git a/GameServer/Systems/Entity/EntitySystem.cs b/GameServer/Systems/Entity/EntitySystem.cs new file mode 100644 index 0000000..a6fea55 --- /dev/null +++ b/GameServer/Systems/Entity/EntitySystem.cs @@ -0,0 +1,38 @@ +using Protocol; + +namespace GameServer.Systems.Entity; +internal class EntitySystem +{ + private readonly List _entities; + + public EntitySystem() + { + _entities = []; + } + + public IEnumerable EnumerateEntities() + { + return _entities; + } + + public void Create(EntityBase entity) + { + if (_entities.Any(e => e.Id == entity.Id)) + throw new InvalidOperationException($"EntitySystem::Create - entity with id {entity.Id} already exists"); + + entity.OnCreate(); + _entities.Add(entity); + } + + public void Activate(EntityBase entity) + { + entity.Activate(); + } + + public TEntity? Get(long id) where TEntity : EntityBase + { + return _entities.SingleOrDefault(e => e.Id == id) as TEntity; + } + + public IEnumerable Pb => _entities.Select(e => e.Pb); +} diff --git a/GameServer/Systems/Entity/PlayerEntity.cs b/GameServer/Systems/Entity/PlayerEntity.cs new file mode 100644 index 0000000..63d628a --- /dev/null +++ b/GameServer/Systems/Entity/PlayerEntity.cs @@ -0,0 +1,66 @@ +using GameServer.Systems.Entity.Component; +using Protocol; + +namespace GameServer.Systems.Entity; +internal class PlayerEntity : EntityBase +{ + public PlayerEntity(long id, int configId, int playerId) : base(id) + { + ConfigId = configId; + PlayerId = playerId; + } + + public int ConfigId { get; } + public int PlayerId { get; } + + public override void AddComponents() + { + base.AddComponents(); + + EntityConcomitantsComponent concomitantsComponent = ComponentSystem.Create(); + concomitantsComponent.CustomEntityIds.Add(Id); + + EntityVisionSkillComponent visionSkillComponent = ComponentSystem.Create(); + visionSkillComponent.SetExploreTool(1001); + + _ = ComponentSystem.Create(); + InitAttributes(); + } + + private void InitAttributes() + { + EntityAttributeComponent attributeComponent = ComponentSystem.Get(); + attributeComponent.SetAttribute(EAttributeType.Life, 1000); + attributeComponent.SetAttribute(EAttributeType.LifeMax, 1000); + attributeComponent.SetAttribute(EAttributeType.Lv, 1); + } + + public override EEntityType Type => EEntityType.Player; + public override EntityConfigType ConfigType => EntityConfigType.Character; + + public override EntityPb Pb + { + get + { + EntityPb pb = new() + { + Id = Id, + EntityType = (int)Type, + ConfigType = (int)ConfigType, + EntityState = (int)State, + ConfigId = ConfigId, + Pos = Pos, + Rot = Rot, + PlayerId = PlayerId, + LivingStatus = (int)LivingStatus, + IsVisible = IsVisible, + InitLinearVelocity = new(), + InitPos = new() + }; + + pb.ComponentPbs.AddRange(ComponentSystem.Pb); + + return pb; + } + } +} diff --git a/GameServer/Controllers/Event/EventSystem.cs b/GameServer/Systems/Event/EventSystem.cs similarity index 98% rename from GameServer/Controllers/Event/EventSystem.cs rename to GameServer/Systems/Event/EventSystem.cs index 27b2eb0..6dececb 100644 --- a/GameServer/Controllers/Event/EventSystem.cs +++ b/GameServer/Systems/Event/EventSystem.cs @@ -6,7 +6,7 @@ using GameServer.Controllers.Manager; using GameServer.Models; using Microsoft.Extensions.Logging; -namespace GameServer.Controllers.Event; +namespace GameServer.Systems.Event; internal class EventSystem { private static readonly ImmutableDictionary> s_modelManagerEventHandlers = RegisterModelManagerEvents(); diff --git a/GameServer/Systems/Event/GameEventType.cs b/GameServer/Systems/Event/GameEventType.cs new file mode 100644 index 0000000..e328c0c --- /dev/null +++ b/GameServer/Systems/Event/GameEventType.cs @@ -0,0 +1,9 @@ +namespace GameServer.Systems.Event; +internal enum GameEventType +{ + Login = 1, + EnterGame, + + // Actions + VisionSkillChanged +}