Entity system

This commit is contained in:
xeon 2024-02-10 19:04:03 +03:00
parent 13cfd2468f
commit 7392b2d20a
26 changed files with 595 additions and 216 deletions

View file

@ -1,4 +1,4 @@
using GameServer.Controllers.Event;
using GameServer.Systems.Event;
namespace GameServer.Controllers.Attributes;

View file

@ -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<ResponseMessage> OnEntityActiveRequest(EntityActiveRequest request)
{
EntityActiveResponse response;
EntityBase? entity = _entitySystem.Get<EntityBase>(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<EntityVisionSkillComponent>();
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);
}
}

View file

@ -1,6 +0,0 @@
namespace GameServer.Controllers.Event;
internal enum GameEventType
{
Login = 1,
EnterGame
}

View file

@ -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;

View file

@ -1,6 +1,6 @@
using GameServer.Controllers.Attributes;
using GameServer.Controllers.Event;
using GameServer.Network;
using GameServer.Systems.Event;
using Protocol;
namespace GameServer.Controllers;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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<ResponseMessage> OnVisionExploreSkillSetRequest(VisionExploreSkillSetRequest request)
public async Task<ResponseMessage> 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<EntityVisionSkillComponent>();
visionSkillComponent.SetExploreTool(request.SkillId);
await eventSystem.Emit(GameEventType.VisionSkillChanged);
return Response(MessageId.VisionExploreSkillSetResponse, new VisionExploreSkillSetResponse
{

View file

@ -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());
}

View file

@ -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;
}
}

View file

@ -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!");
}

View file

@ -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<MessageManager>().AddSingleton<EventHandlerFactory>()
.AddScoped<RpcManager>().AddScoped<IRpcEndPoint, RpcSessionEndPoint>()
.AddSingleton<SessionManager>()
.AddScoped<EventSystem>().AddScoped<ModelManager>().AddScoped<ControllerManager>()
.AddScoped<EventSystem>().AddScoped<EntitySystem>().AddScoped<EntityFactory>()
.AddScoped<ModelManager>().AddScoped<ControllerManager>()
.AddHostedService<WWGameServer>();
await builder.Build().RunAsync();

View file

@ -0,0 +1,72 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityAttributeComponent : EntityComponentBase
{
public override EntityComponentType Type => EntityComponentType.Attribute;
private readonly Dictionary<EAttributeType, GameplayAttributeData> _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;
}
}
}

View file

@ -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; }
}

View file

@ -0,0 +1,34 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityComponentSystem
{
private readonly List<EntityComponentBase> _components;
public EntityComponentSystem()
{
_components = [];
}
public TEntityComponent Create<TEntityComponent>() 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<TEntityComponent>() where TEntityComponent : EntityComponentBase
{
return (_components.Single(component => component is TEntityComponent) as TEntityComponent)!;
}
public bool TryGet<TEntityComponent>(out TEntityComponent? component) where TEntityComponent : EntityComponentBase
{
return (component = _components.SingleOrDefault(component => component is TEntityComponent) as TEntityComponent) != null;
}
public IEnumerable<EntityComponentPb> Pb => _components.Select(component => component.Pb);
}

View file

@ -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
}

View file

@ -0,0 +1,28 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityConcomitantsComponent : EntityComponentBase
{
public List<long> 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;
}
}
}

View file

@ -0,0 +1,37 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityVisionSkillComponent : EntityComponentBase
{
public List<VisionSkillInformation> 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;
}
}
}

View file

@ -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; }
}

View file

@ -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);
}

View file

@ -0,0 +1,38 @@
using Protocol;
namespace GameServer.Systems.Entity;
internal class EntitySystem
{
private readonly List<EntityBase> _entities;
public EntitySystem()
{
_entities = [];
}
public IEnumerable<EntityBase> 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<TEntity>(long id) where TEntity : EntityBase
{
return _entities.SingleOrDefault(e => e.Id == id) as TEntity;
}
public IEnumerable<EntityPb> Pb => _entities.Select(e => e.Pb);
}

View file

@ -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<EntityConcomitantsComponent>();
concomitantsComponent.CustomEntityIds.Add(Id);
EntityVisionSkillComponent visionSkillComponent = ComponentSystem.Create<EntityVisionSkillComponent>();
visionSkillComponent.SetExploreTool(1001);
_ = ComponentSystem.Create<EntityAttributeComponent>();
InitAttributes();
}
private void InitAttributes()
{
EntityAttributeComponent attributeComponent = ComponentSystem.Get<EntityAttributeComponent>();
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;
}
}
}

View file

@ -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<GameEventType, Func<ModelManager, Task>> s_modelManagerEventHandlers = RegisterModelManagerEvents();

View file

@ -0,0 +1,9 @@
namespace GameServer.Systems.Event;
internal enum GameEventType
{
Login = 1,
EnterGame,
// Actions
VisionSkillChanged
}