Compare commits

...

3 commits

38 changed files with 338 additions and 176 deletions

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,5 +1,4 @@
using Core.Config; using GameServer.Controllers.Attributes;
using GameServer.Controllers.Attributes;
using GameServer.Models; using GameServer.Models;
using GameServer.Models.Chat; using GameServer.Models.Chat;
using GameServer.Network; using GameServer.Network;

View file

@ -4,7 +4,6 @@ using GameServer.Models;
using GameServer.Models.Chat; using GameServer.Models.Chat;
using GameServer.Network; using GameServer.Network;
using GameServer.Systems.Entity; using GameServer.Systems.Entity;
using Protocol;
namespace GameServer.Controllers.ChatCommands; namespace GameServer.Controllers.ChatCommands;
@ -50,14 +49,8 @@ internal class ChatSpawnCommandHandler
Z = z * 100 Z = z * 100
}; };
_entitySystem.Create(monster);
monster.InitProps(_configManager.GetConfig<BasePropertyConfig>(600000100)!); // TODO: monster property config monster.InitProps(_configManager.GetConfig<BasePropertyConfig>(600000100)!); // TODO: monster property config
_entitySystem.Add([monster]);
await _session.Push(MessageId.EntityAddNotify, new EntityAddNotify
{
IsAdd = true,
EntityPbs = { monster.Pb }
});
await _creatureController.UpdateAiHate(); await _creatureController.UpdateAiHate();

View file

@ -5,7 +5,6 @@ using GameServer.Controllers.ChatCommands;
using GameServer.Models; using GameServer.Models;
using GameServer.Models.Chat; using GameServer.Models.Chat;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -82,7 +82,7 @@ internal class CombatManager
} }
[CombatRequest(CombatRequestData.MessageOneofCase.DamageExecuteRequest)] [CombatRequest(CombatRequestData.MessageOneofCase.DamageExecuteRequest)]
public async Task<CombatResponseData> OnDamageExecuteRequest(CombatRequestContext context) public CombatResponseData OnDamageExecuteRequest(CombatRequestContext context)
{ {
DamageExecuteRequest request = context.Request.DamageExecuteRequest; DamageExecuteRequest request = context.Request.DamageExecuteRequest;
@ -105,19 +105,7 @@ internal class CombatManager
if (request.DamageId <= 0 && entity.Type != EEntityType.Player) // Player death not implemented if (request.DamageId <= 0 && entity.Type != EEntityType.Player) // Player death not implemented
{ {
_entitySystem.Destroy(entity); _entitySystem.Destroy([entity]);
await _session.Push(MessageId.EntityRemoveNotify, new EntityRemoveNotify
{
IsRemove = true,
RemoveInfos =
{
new EntityRemoveInfo
{
EntityId = entity.Id,
Type = (int)ERemoveEntityType.RemoveTypeNormal
}
}
});
} }
return new CombatResponseData return new CombatResponseData

View file

@ -3,31 +3,39 @@ using GameServer.Controllers.Attributes;
using GameServer.Extensions.Logic; using GameServer.Extensions.Logic;
using GameServer.Models; using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using GameServer.Settings; using GameServer.Settings;
using GameServer.Systems.Entity; using GameServer.Systems.Entity;
using GameServer.Systems.Entity.Component; using GameServer.Systems.Entity.Component;
using GameServer.Systems.Event; using GameServer.Systems.Event;
using GameServer.Systems.Notify;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
internal class CreatureController : Controller internal class CreatureController : Controller
{ {
private const float DynamicSpawnRadius = 5000;
private const float DynamicSpawnPositionDelta = 2500;
private readonly EntitySystem _entitySystem; private readonly EntitySystem _entitySystem;
private readonly EntityFactory _entityFactory; private readonly EntityFactory _entityFactory;
private readonly ModelManager _modelManager; private readonly ModelManager _modelManager;
private readonly ConfigManager _configManager; private readonly ConfigManager _configManager;
private readonly IGameActionListener _listener;
private readonly GameplayFeatureSettings _gameplayFeatures; private readonly GameplayFeatureSettings _gameplayFeatures;
private readonly Vector _lastDynamicSpawnPos;
public CreatureController(PlayerSession session, EntitySystem entitySystem, EntityFactory entityFactory, ModelManager modelManager, ConfigManager configManager, IOptions<GameplayFeatureSettings> gameplayFeatures) : base(session) public CreatureController(PlayerSession session, EntitySystem entitySystem, EntityFactory entityFactory, ModelManager modelManager, ConfigManager configManager, IOptions<GameplayFeatureSettings> gameplayFeatures, IGameActionListener listener) : base(session)
{ {
_entitySystem = entitySystem; _entitySystem = entitySystem;
_entityFactory = entityFactory; _entityFactory = entityFactory;
_modelManager = modelManager; _modelManager = modelManager;
_configManager = configManager; _configManager = configManager;
_listener = listener;
_gameplayFeatures = gameplayFeatures.Value; _gameplayFeatures = gameplayFeatures.Value;
_lastDynamicSpawnPos = new();
} }
public async Task JoinScene(int instanceId) public async Task JoinScene(int instanceId)
@ -36,15 +44,7 @@ internal class CreatureController : Controller
CreateTeamPlayerEntities(); CreateTeamPlayerEntities();
CreateWorldEntities(); CreateWorldEntities();
await Session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify await _listener.OnJoinedScene(CreateSceneInfo(), TransitionType.Empty);
{
MaxEntityId = 10000000,
TransitionOption = new TransitionOptionPb
{
TransitionType = (int)TransitionType.Empty
},
SceneInfo = CreateSceneInfo()
});
} }
[NetEvent(MessageId.EntityActiveRequest)] [NetEvent(MessageId.EntityActiveRequest)]
@ -85,47 +85,11 @@ internal class CreatureController : Controller
[GameEvent(GameEventType.FormationUpdated)] [GameEvent(GameEventType.FormationUpdated)]
public async Task OnFormationUpdated() public async Task OnFormationUpdated()
{ {
// Remove old entities _entitySystem.Destroy(GetPlayerEntities().ToArray());
IEnumerable<PlayerEntity> oldEntities = GetPlayerEntities().ToArray();
foreach (PlayerEntity oldEntity in oldEntities)
{
_entitySystem.Destroy(oldEntity);
}
await Session.Push(MessageId.EntityRemoveNotify, new EntityRemoveNotify
{
IsRemove = true,
RemoveInfos =
{
oldEntities.Select(entity => new EntityRemoveInfo
{
EntityId = entity.Id,
Type = (int)entity.Type
})
}
});
// Spawn new entities
CreateTeamPlayerEntities(); CreateTeamPlayerEntities();
IEnumerable<PlayerEntity> newEntities = GetPlayerEntities(); _modelManager.Creature.PlayerEntityId = GetPlayerEntities().First().Id;
await Session.Push(MessageId.EntityAddNotify, new EntityAddNotify await _listener.OnPlayerFightRoleInfoUpdated(_modelManager.Player.Id, GetFightRoleInfos());
{
IsAdd = true,
EntityPbs =
{
newEntities.Select(entity => entity.Pb)
}
});
_modelManager.Creature.PlayerEntityId = newEntities.First().Id;
await Session.Push(MessageId.UpdatePlayerAllFightRoleNotify, new UpdatePlayerAllFightRoleNotify
{
PlayerId = _modelManager.Player.Id,
FightRoleInfos = { GetFightRoleInfos() }
});
await UpdateAiHate(); await UpdateAiHate();
} }
@ -134,6 +98,14 @@ internal class CreatureController : Controller
public void OnPlayerPositionChanged() public void OnPlayerPositionChanged()
{ {
_modelManager.Player.Position.MergeFrom(GetPlayerEntity()!.Pos); _modelManager.Player.Position.MergeFrom(GetPlayerEntity()!.Pos);
if (_lastDynamicSpawnPos.GetDistance(_modelManager.Player.Position) >= DynamicSpawnPositionDelta)
{
_lastDynamicSpawnPos.MergeFrom(_modelManager.Player.Position);
ClearInactiveEntities();
SpawnDynamicEntities();
}
} }
[GameEvent(GameEventType.VisionSkillChanged)] [GameEvent(GameEventType.VisionSkillChanged)]
@ -256,6 +228,8 @@ internal class CreatureController : Controller
private void CreateTeamPlayerEntities() private void CreateTeamPlayerEntities()
{ {
PlayerEntity[] playerEntities = new PlayerEntity[_modelManager.Formation.RoleIds.Length];
for (int i = 0; i < _modelManager.Formation.RoleIds.Length; i++) for (int i = 0; i < _modelManager.Formation.RoleIds.Length; i++)
{ {
int roleId = _modelManager.Formation.RoleIds[i]; int roleId = _modelManager.Formation.RoleIds[i];
@ -264,7 +238,6 @@ internal class CreatureController : Controller
entity.Pos = _modelManager.Player.Position.Clone(); entity.Pos = _modelManager.Player.Position.Clone();
entity.IsCurrentRole = i == 0; entity.IsCurrentRole = i == 0;
_entitySystem.Create(entity);
entity.ComponentSystem.Get<EntityAttributeComponent>().SetAll(_modelManager.Roles.GetRoleById(roleId)!.GetAttributeList()); entity.ComponentSystem.Get<EntityAttributeComponent>().SetAll(_modelManager.Roles.GetRoleById(roleId)!.GetAttributeList());
CreateConcomitants(entity); CreateConcomitants(entity);
@ -281,7 +254,11 @@ internal class CreatureController : Controller
attr.SetAttribute(EAttributeType.SpecialEnergy3Max, 0); attr.SetAttribute(EAttributeType.SpecialEnergy3Max, 0);
attr.SetAttribute(EAttributeType.SpecialEnergy4Max, 0); attr.SetAttribute(EAttributeType.SpecialEnergy4Max, 0);
} }
playerEntities[i] = entity;
} }
_entitySystem.Add(playerEntities);
} }
private void CreateConcomitants(PlayerEntity entity) private void CreateConcomitants(PlayerEntity entity)
@ -295,7 +272,6 @@ internal class CreatureController : Controller
if (roleId != -1) if (roleId != -1)
{ {
PlayerEntity concomitant = _entityFactory.CreatePlayer(roleId, 0); PlayerEntity concomitant = _entityFactory.CreatePlayer(roleId, 0);
_entitySystem.Create(concomitant);
EntityConcomitantsComponent concomitants = entity.ComponentSystem.Create<EntityConcomitantsComponent>(); EntityConcomitantsComponent concomitants = entity.ComponentSystem.Create<EntityConcomitantsComponent>();
concomitants.CustomEntityIds.Clear(); concomitants.CustomEntityIds.Clear();
@ -309,23 +285,49 @@ internal class CreatureController : Controller
summoner.PlayerId = _modelManager.Player.Id; summoner.PlayerId = _modelManager.Player.Id;
concomitant.InitProps(_configManager.GetConfig<BasePropertyConfig>(roleId)!); concomitant.InitProps(_configManager.GetConfig<BasePropertyConfig>(roleId)!);
_entitySystem.Add([concomitant]);
} }
} }
private void CreateWorldEntities() private void CreateWorldEntities()
{
_lastDynamicSpawnPos.MergeFrom(_modelManager.Player.Position.Clone());
SpawnDynamicEntities();
}
private void ClearInactiveEntities()
{
_entitySystem.Destroy(_entitySystem.EnumerateEntities()
.Where(e => e is MonsterEntity && e.DynamicId != 0 &&
e.Pos.GetDistance(_modelManager.Player.Position) > DynamicSpawnRadius).ToArray());
}
private void SpawnDynamicEntities()
{ {
Vector playerPos = _modelManager.Player.Position; Vector playerPos = _modelManager.Player.Position;
// Test monster // Currently only monsters
MonsterEntity monster = _entityFactory.CreateMonster(106003002); // Turtle. IEnumerable<LevelEntityConfig> entitiesToSpawn = _configManager.Enumerate<LevelEntityConfig>()
.Where(config => config.MapId == 8 && Math.Abs(config.Transform[0].X / 100 - playerPos.X) < DynamicSpawnRadius && Math.Abs(config.Transform[0].Y / 100 - playerPos.Y) < DynamicSpawnRadius &&
config.BlueprintType.StartsWith("Monster"));
List<MonsterEntity> spawnMonsters = [];
foreach (LevelEntityConfig levelEntity in entitiesToSpawn)
{
if (_entitySystem.HasDynamicEntity(levelEntity.EntityId)) continue;
MonsterEntity monster = _entityFactory.CreateMonster(levelEntity.EntityId);
monster.Pos = new() monster.Pos = new()
{ {
X = playerPos.X + 250, X = levelEntity.Transform[0].X / 100,
Y = playerPos.Y + 250, Y = levelEntity.Transform[0].Y / 100,
Z = playerPos.Z Z = levelEntity.Transform[0].Z / 100
}; };
_entitySystem.Create(monster);
monster.InitProps(_configManager.GetConfig<BasePropertyConfig>(600000100)!); monster.InitProps(_configManager.GetConfig<BasePropertyConfig>(600000100)!);
spawnMonsters.Add(monster);
}
_entitySystem.Add(spawnMonsters);
} }
} }

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,8 +1,6 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Models; using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using GameServer.Systems.Entity;
using GameServer.Systems.Event; using GameServer.Systems.Event;
using Protocol; using Protocol;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -4,7 +4,6 @@ using GameServer.Extensions.Logic;
using GameServer.Models; using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Systems.Entity; using GameServer.Systems.Entity;
using GameServer.Systems.Entity.Component;
using GameServer.Systems.Event; using GameServer.Systems.Event;
using Protocol; using Protocol;
@ -38,7 +37,7 @@ internal class InventoryController : Controller
public RpcResult OnItemExchangeInfoRequest() => Response(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse()); public RpcResult OnItemExchangeInfoRequest() => Response(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse());
[NetEvent(MessageId.EquipTakeOnRequest)] [NetEvent(MessageId.EquipTakeOnRequest)]
public async Task<RpcResult> OnEquipTakeOnRequest(EquipTakeOnRequest request, ModelManager modelManager, CreatureController creatureController, ConfigManager configManager) public RpcResult OnEquipTakeOnRequest(EquipTakeOnRequest request, ModelManager modelManager, CreatureController creatureController, RoleController roleController, ConfigManager configManager)
{ {
WeaponItem? weapon = modelManager.Inventory.GetWeaponById(request.Data.EquipIncId); WeaponItem? weapon = modelManager.Inventory.GetWeaponById(request.Data.EquipIncId);
if (weapon == null) return Response(MessageId.EquipTakeOnResponse, new EquipTakeOnResponse if (weapon == null) return Response(MessageId.EquipTakeOnResponse, new EquipTakeOnResponse
@ -60,39 +59,12 @@ internal class InventoryController : Controller
// Set new weapon // Set new weapon
weapon.RoleId = role.RoleId; weapon.RoleId = role.RoleId;
role.ApplyWeaponProperties(weaponConf); roleController.ApplyWeaponPropertiesToRole(role.RoleId, weaponConf);
// Update role prop data on client
await Session.Push(MessageId.PbRolePropsNotify, new PbRolePropsNotify
{
RoleId = role.RoleId,
BaseProp = { role.BaseProp },
AddProp = { role.AddProp }
});
// Update entity (if this role is currently active)
PlayerEntity? entity = creatureController.GetPlayerEntityByRoleId(request.Data.RoleId); PlayerEntity? entity = creatureController.GetPlayerEntityByRoleId(request.Data.RoleId);
if (entity != null) entity?.ChangeEquipment(weapon.Id);
{ entity?.ChangeGameplayAttributes(role.GetAttributeList());
// Update entity equipment
EntityEquipComponent equipComponent = entity.ComponentSystem.Get<EntityEquipComponent>();
equipComponent.WeaponId = weapon.Id;
await Session.Push(MessageId.EntityEquipChangeNotify, new EntityEquipChangeNotify
{
EntityId = entity.Id,
EquipComponent = equipComponent.Pb.EquipComponent
});
// Update entity gameplay attributes
EntityAttributeComponent attrComponent = entity.ComponentSystem.Get<EntityAttributeComponent>();
attrComponent.SetAll(role.GetAttributeList());
await Session.Push(MessageId.AttributeChangedNotify, new AttributeChangedNotify
{
Id = entity.Id,
Attributes = { attrComponent.Pb.AttributeComponent.GameAttributes }
});
}
// Response // Response
EquipTakeOnResponse response = new() EquipTakeOnResponse response = new()

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -4,25 +4,38 @@ using GameServer.Extensions.Logic;
using GameServer.Models; using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Systems.Event; using GameServer.Systems.Event;
using GameServer.Systems.Notify;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
internal class RoleController : Controller internal class RoleController : Controller
{ {
public RoleController(PlayerSession session) : base(session) private readonly ModelManager _modelManager;
private readonly IGameActionListener _listener;
public RoleController(PlayerSession session, ModelManager modelManager, IGameActionListener listener) : base(session)
{ {
// RoleController. _modelManager = modelManager;
_listener = listener;
}
public void ApplyWeaponPropertiesToRole(int roleId, WeaponConfig weaponConfiguration)
{
roleInfo? role = _modelManager.Roles.GetRoleById(roleId) ?? throw new ArgumentException($"Role with id {roleId} doesn't exist");
role.ApplyWeaponProperties(weaponConfiguration);
_ = _listener.OnRolePropertiesUpdated(roleId, role.BaseProp, role.AddProp);
} }
[GameEvent(GameEventType.DebugUnlockAllRoles)] [GameEvent(GameEventType.DebugUnlockAllRoles)]
public void UnlockAllRoles(ConfigManager configManager, ModelManager modelManager) public void UnlockAllRoles(ConfigManager configManager)
{ {
foreach (RoleInfoConfig roleConfig in configManager.Enumerate<RoleInfoConfig>()) foreach (RoleInfoConfig roleConfig in configManager.Enumerate<RoleInfoConfig>())
{ {
roleInfo role = modelManager.Roles.Create(roleConfig.Id); roleInfo role = _modelManager.Roles.Create(roleConfig.Id);
role.BaseProp.AddRange(CreateBasePropList(configManager.GetConfig<BasePropertyConfig>(roleConfig.Id))); role.BaseProp.AddRange(CreateBasePropList(configManager.GetConfig<BasePropertyConfig>(roleConfig.Id)));
WeaponItem weapon = modelManager.Inventory.AddNewWeapon(roleConfig.InitWeaponItemId); WeaponItem weapon = _modelManager.Inventory.AddNewWeapon(roleConfig.InitWeaponItemId);
weapon.RoleId = role.RoleId; weapon.RoleId = role.RoleId;
role.ApplyWeaponProperties(configManager.GetConfig<WeaponConfig>(weapon.Id)!); role.ApplyWeaponProperties(configManager.GetConfig<WeaponConfig>(weapon.Id)!);

View file

@ -1,7 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using GameServer.Systems.Entity; using GameServer.Systems.Entity;
using GameServer.Systems.Entity.Component; using GameServer.Systems.Entity.Component;
using GameServer.Systems.Event; using GameServer.Systems.Event;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;

View file

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

View file

@ -1,6 +1,5 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
using GameServer.Settings; using GameServer.Settings;
using GameServer.Systems.Entity; using GameServer.Systems.Entity;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;

View file

@ -0,0 +1,13 @@
using Protocol;
namespace GameServer.Extensions.Logic;
internal static class MathExtensions
{
public static float GetDistance(this Vector self, Vector other)
{
float x = self.X - other.X;
float y = self.Y - other.Y;
return (float)Math.Sqrt(x * x + y * y);
}
}

View file

@ -1,5 +1,4 @@
using Google.Protobuf; using Protocol;
using Protocol;
namespace GameServer.Models.Chat; namespace GameServer.Models.Chat;
internal class ChatRoom internal class ChatRoom

View file

@ -1,5 +1,4 @@
using Core.Config; using Protocol;
using Protocol;
namespace GameServer.Models; namespace GameServer.Models;
internal class RoleModel internal class RoleModel

View file

@ -8,6 +8,7 @@ internal class KcpConnection : IConnection
{ {
private readonly byte[] _recvBuffer; private readonly byte[] _recvBuffer;
private readonly KcpConversation _conv; private readonly KcpConversation _conv;
private readonly ManualResetEvent _sendEvent;
private uint _upStreamSeqNo; private uint _upStreamSeqNo;
private uint _downStreamSeqNo; private uint _downStreamSeqNo;
@ -15,6 +16,7 @@ internal class KcpConnection : IConnection
{ {
_conv = conv; _conv = conv;
_recvBuffer = GC.AllocateUninitializedArray<byte>(8192); _recvBuffer = GC.AllocateUninitializedArray<byte>(8192);
_sendEvent = new ManualResetEvent(true);
} }
public bool Active => !_conv.TransportClosed; public bool Active => !_conv.TransportClosed;
@ -51,7 +53,16 @@ internal class KcpConnection : IConnection
MessageManager.EncodeMessage(memory[BaseMessage.LengthFieldSize..], message); MessageManager.EncodeMessage(memory[BaseMessage.LengthFieldSize..], message);
if (_conv == null) throw new InvalidOperationException("Trying to send message when conv is null"); if (_conv == null) throw new InvalidOperationException("Trying to send message when conv is null");
if (!_sendEvent.WaitOne(0))
{
await Task.Yield();
_ = _sendEvent.WaitOne();
}
_sendEvent.Reset();
await _conv.SendAsync(memoryOwner.Memory[..networkSize]); await _conv.SendAsync(memoryOwner.Memory[..networkSize]);
_sendEvent.Set();
} }
private uint NextUpStreamSeqNo() private uint NextUpStreamSeqNo()

View file

@ -13,6 +13,7 @@ using GameServer.Network.Rpc;
using GameServer.Settings; using GameServer.Settings;
using GameServer.Systems.Entity; using GameServer.Systems.Entity;
using GameServer.Systems.Event; using GameServer.Systems.Event;
using GameServer.Systems.Notify;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@ -39,7 +40,8 @@ internal static class Program
.AddScoped<MessageManager>().AddSingleton<EventHandlerFactory>() .AddScoped<MessageManager>().AddSingleton<EventHandlerFactory>()
.AddScoped<RpcManager>().AddScoped<IRpcEndPoint, RpcSessionEndPoint>() .AddScoped<RpcManager>().AddScoped<IRpcEndPoint, RpcSessionEndPoint>()
.AddSingleton<SessionManager>() .AddSingleton<SessionManager>()
.AddScoped<EventSystem>().AddScoped<EntitySystem>().AddScoped<EntityFactory>() .AddScoped<EventSystem>().AddScoped<EntitySystem>().AddScoped<IGameActionListener, NotifySystem>()
.AddScoped<EntityFactory>()
.AddScoped<ModelManager>().AddScoped<ControllerManager>() .AddScoped<ModelManager>().AddScoped<ControllerManager>()
.AddScoped<CombatManager>().AddScoped<ChatCommandManager>() .AddScoped<CombatManager>().AddScoped<ChatCommandManager>()
.AddHostedService<WWGameServer>(); .AddHostedService<WWGameServer>();

View file

@ -1,5 +1,6 @@
using Core.Config; using Core.Config;
using GameServer.Systems.Entity.Component; using GameServer.Systems.Entity.Component;
using GameServer.Systems.Notify;
using Protocol; using Protocol;
namespace GameServer.Systems.Entity; namespace GameServer.Systems.Entity;
@ -12,12 +13,15 @@ internal abstract class EntityBase
public Rotator Rot { get; set; } public Rotator Rot { get; set; }
public bool Active { get; set; } public bool Active { get; set; }
public int DynamicId { get; protected set; }
public EntityState State { get; protected set; } public EntityState State { get; protected set; }
public bool IsConcomitant => ComponentSystem.TryGet<EntitySummonerComponent>(out _); public bool IsConcomitant => ComponentSystem.TryGet<EntitySummonerComponent>(out _);
public EntityBase(long id) protected IGameActionListener ActionListener { get; }
public EntityBase(long id, IGameActionListener listener)
{ {
Id = id; Id = id;
@ -25,6 +29,7 @@ internal abstract class EntityBase
Rot = new Rotator(); Rot = new Rotator();
ComponentSystem = new EntityComponentSystem(); ComponentSystem = new EntityComponentSystem();
ActionListener = listener;
} }
public virtual void OnCreate() public virtual void OnCreate()
@ -40,6 +45,24 @@ internal abstract class EntityBase
// Activate. // Activate.
} }
public void ChangeEquipment(int weaponId)
{
if (ComponentSystem.TryGet(out EntityEquipComponent? equipComponent))
{
equipComponent.WeaponId = weaponId;
_ = ActionListener.OnEntityEquipmentChanged(Id, equipComponent.Pb.EquipComponent);
}
}
public void ChangeGameplayAttributes(IEnumerable<GameplayAttributeData> attributes)
{
if (ComponentSystem.TryGet(out EntityAttributeComponent? attrComponent))
{
attrComponent.SetAll(attributes);
_ = ActionListener.OnEntityAttributesChanged(Id, attrComponent.Pb.AttributeComponent.GameAttributes);
}
}
public virtual LivingStatus LivingStatus => LivingStatus.Alive; public virtual LivingStatus LivingStatus => LivingStatus.Alive;
public virtual bool IsVisible => true; public virtual bool IsVisible => true;

View file

@ -1,12 +1,40 @@
namespace GameServer.Systems.Entity; using Microsoft.Extensions.DependencyInjection;
namespace GameServer.Systems.Entity;
internal class EntityFactory internal class EntityFactory
{ {
private static readonly ObjectFactory<PlayerEntity> s_createPlayerEntity;
private static readonly ObjectFactory<MonsterEntity> s_createMonsterEntity;
private long _entityIdCounter; private long _entityIdCounter;
private readonly IServiceProvider _serviceProvider;
static EntityFactory()
{
s_createPlayerEntity = ActivatorUtilities.CreateFactory<PlayerEntity>([typeof(long), typeof(int), typeof(int)]);
s_createMonsterEntity = ActivatorUtilities.CreateFactory<MonsterEntity>([typeof(long), typeof(int)]);
}
public EntityFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public PlayerEntity CreatePlayer(int characterConfigId, int playerId) public PlayerEntity CreatePlayer(int characterConfigId, int playerId)
=> new(NextId(), characterConfigId, playerId); {
PlayerEntity entity = s_createPlayerEntity(_serviceProvider, [NextId(), characterConfigId, playerId]);
entity.OnCreate();
public MonsterEntity CreateMonster(int levelEntityId) => new(NextId(), levelEntityId); return entity;
}
public MonsterEntity CreateMonster(int levelEntityId)
{
MonsterEntity monsterEntity = s_createMonsterEntity(_serviceProvider, [NextId(), levelEntityId]);
monsterEntity.OnCreate();
return monsterEntity;
}
private long NextId() => Interlocked.Increment(ref _entityIdCounter); private long NextId() => Interlocked.Increment(ref _entityIdCounter);
} }

View file

@ -1,13 +1,19 @@
using Protocol; using GameServer.Systems.Notify;
using Protocol;
namespace GameServer.Systems.Entity; namespace GameServer.Systems.Entity;
internal class EntitySystem internal class EntitySystem
{ {
private readonly List<EntityBase> _entities; private readonly List<EntityBase> _entities;
private readonly List<int> _dynamicEntityIds;
public EntitySystem() private readonly IGameActionListener _listener;
public EntitySystem(IGameActionListener listener)
{ {
_entities = []; _entities = [];
_dynamicEntityIds = [];
_listener = listener;
} }
public IEnumerable<EntityBase> EnumerateEntities() public IEnumerable<EntityBase> EnumerateEntities()
@ -15,18 +21,36 @@ internal class EntitySystem
return _entities; return _entities;
} }
public void Create(EntityBase entity) public void Add(IEnumerable<EntityBase> entities)
{
foreach (EntityBase entity in entities)
{ {
if (_entities.Any(e => e.Id == entity.Id)) if (_entities.Any(e => e.Id == entity.Id))
throw new InvalidOperationException($"EntitySystem::Create - entity with id {entity.Id} already exists"); throw new InvalidOperationException($"EntitySystem::Create - entity with id {entity.Id} already exists");
entity.OnCreate();
_entities.Add(entity); _entities.Add(entity);
if (entity.DynamicId != 0)
_dynamicEntityIds.Add(entity.DynamicId);
} }
public void Destroy(EntityBase entity) _ = _listener.OnEntitiesAdded(entities);
}
public bool HasDynamicEntity(int dynamicId)
{
return _dynamicEntityIds.Contains(dynamicId);
}
public void Destroy(IEnumerable<EntityBase> entities)
{
foreach (EntityBase entity in entities)
{ {
_ = _entities.Remove(entity); _ = _entities.Remove(entity);
_ = _dynamicEntityIds.Remove(entity.DynamicId);
}
_ = _listener.OnEntitiesRemoved(entities);
} }
public void Activate(EntityBase entity) public void Activate(EntityBase entity)

View file

@ -1,12 +1,14 @@
using GameServer.Systems.Entity.Component; using GameServer.Systems.Entity.Component;
using GameServer.Systems.Notify;
using Protocol; using Protocol;
namespace GameServer.Systems.Entity; namespace GameServer.Systems.Entity;
internal class MonsterEntity : EntityBase internal class MonsterEntity : EntityBase
{ {
public MonsterEntity(long id, int configId) : base(id) public MonsterEntity(long id, int configId, IGameActionListener listener) : base(id, listener)
{ {
ConfigId = configId; ConfigId = configId;
DynamicId = configId;
} }
public int ConfigId { get; } public int ConfigId { get; }
@ -32,17 +34,20 @@ internal class MonsterEntity : EntityBase
fsm.Fsms.Add(new DFsm fsm.Fsms.Add(new DFsm
{ {
FsmId = 10007, // Main State Machine FsmId = 10007, // Main State Machine
CurrentState = 10013, // Battle Branching CurrentState = 10013 // Battle Branching
Status = 1, // ??
Flag = (int)EFsmStateFlag.Confirmed
}); });
fsm.Fsms.Add(new DFsm fsm.Fsms.Add(new DFsm
{ {
FsmId = 10007, // Main State Machine FsmId = 10007, // Main State Machine
CurrentState = 10015, // Moving Combat CurrentState = 10015 // Moving Combat
Status = 1, // ?? });
Flag = (int)EFsmStateFlag.Confirmed
// Some monsters need weapon
fsm.Fsms.Add(new DFsm
{
FsmId = 100,
CurrentState = 9 // [9 - Empty hand, 10 - Crowbar, 11 - flamethrower, 12 - chainsaw, 13 - electric blade, 14 - sniper rifle]
}); });
} }

View file

@ -1,10 +1,11 @@
using GameServer.Systems.Entity.Component; using GameServer.Systems.Entity.Component;
using GameServer.Systems.Notify;
using Protocol; using Protocol;
namespace GameServer.Systems.Entity; namespace GameServer.Systems.Entity;
internal class PlayerEntity : EntityBase internal class PlayerEntity : EntityBase
{ {
public PlayerEntity(long id, int configId, int playerId) : base(id) public PlayerEntity(long id, int configId, int playerId, IGameActionListener listener) : base(id, listener)
{ {
ConfigId = configId; ConfigId = configId;
PlayerId = playerId; PlayerId = playerId;

View file

@ -0,0 +1,14 @@
using GameServer.Systems.Entity;
using Protocol;
namespace GameServer.Systems.Notify;
internal interface IGameActionListener
{
Task OnJoinedScene(SceneInformation sceneInformation, TransitionType transitionType);
Task OnEntitiesAdded(IEnumerable<EntityBase> entities);
Task OnEntitiesRemoved(IEnumerable<EntityBase> entities);
Task OnPlayerFightRoleInfoUpdated(int playerId, IEnumerable<FightRoleInformation> fightRoles);
Task OnRolePropertiesUpdated(int roleId, IEnumerable<ArrayIntInt> baseProp, IEnumerable<ArrayIntInt> addProp);
Task OnEntityEquipmentChanged(long entityId, EquipComponentPb componentPb);
Task OnEntityAttributesChanged(long entityId, IEnumerable<GameplayAttributeData> attributes);
}

View file

@ -0,0 +1,97 @@
using GameServer.Models;
using GameServer.Network;
using GameServer.Systems.Entity;
using Protocol;
namespace GameServer.Systems.Notify;
internal class NotifySystem : IGameActionListener
{
private readonly PlayerSession _session;
private readonly ModelManager _modelManager;
public NotifySystem(PlayerSession session, ModelManager modelManager)
{
_session = session;
_modelManager = modelManager;
}
public Task OnJoinedScene(SceneInformation sceneInformation, TransitionType transitionType)
{
return _session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify
{
SceneInfo = sceneInformation,
TransitionOption = new TransitionOptionPb
{
TransitionType = (int)transitionType
}
});
}
public Task OnEntitiesAdded(IEnumerable<EntityBase> entities)
{
if (_modelManager.Creature.LoadingWorld) return Task.CompletedTask;
return _session.Push(MessageId.EntityAddNotify, new EntityAddNotify
{
IsAdd = true,
EntityPbs = { entities.Select(e => e.Pb) }
});
}
public Task OnEntitiesRemoved(IEnumerable<EntityBase> entities)
{
if (_modelManager.Creature.LoadingWorld) return Task.CompletedTask;
return _session.Push(MessageId.EntityRemoveNotify, new EntityRemoveNotify
{
IsRemove = true,
RemoveInfos =
{
entities.Select(e => new EntityRemoveInfo
{
EntityId = e.Id,
Type = (int)ERemoveEntityType.RemoveTypeNormal
})
}
});
}
public Task OnPlayerFightRoleInfoUpdated(int playerId, IEnumerable<FightRoleInformation> fightRoles)
{
return _session.Push(MessageId.UpdatePlayerAllFightRoleNotify, new UpdatePlayerAllFightRoleNotify
{
PlayerId = playerId,
FightRoleInfos = { fightRoles }
});
}
public Task OnRolePropertiesUpdated(int roleId, IEnumerable<ArrayIntInt> baseProp, IEnumerable<ArrayIntInt> addProp)
{
if (_modelManager.Creature.LoadingWorld) return Task.CompletedTask;
return _session.Push(MessageId.PbRolePropsNotify, new PbRolePropsNotify
{
RoleId = roleId,
BaseProp = { baseProp },
AddProp = { addProp }
});
}
public Task OnEntityEquipmentChanged(long entityId, EquipComponentPb componentPb)
{
return _session.Push(MessageId.EntityEquipChangeNotify, new EntityEquipChangeNotify
{
EntityId = entityId,
EquipComponent = componentPb
});
}
public Task OnEntityAttributesChanged(long entityId, IEnumerable<GameplayAttributeData> attributes)
{
return _session.Push(MessageId.AttributeChangedNotify, new AttributeChangedNotify
{
Id = entityId,
Attributes = { attributes }
});
}
}

View file

@ -6,13 +6,13 @@
"HeadFrame": 80060009, "HeadFrame": 80060009,
"Characters": [ 1402, 1302, 1203 ], "Characters": [ 1402, 1302, 1203 ],
"Position": { "Position": {
"X": -35823, "X": -45000,
"Y": 67132, "Y": 67800,
"Z": 4067 "Z": 2600
} }
}, },
"Features": { "Features": {
"TeleportByMapMark": true, "TeleportByMapMark": true,
"UnlimitedEnergy": true "UnlimitedEnergy": false
} }
} }