Compare commits

...

24 commits

Author SHA1 Message Date
a1736317a1 WIP: Dynamic entity spawn (only monsters rn) 2024-02-25 23:29:35 +03:00
5d58821be4 cleanup 'using' directives 2024-02-25 00:54:49 +03:00
5fce82048f avoid explicit notify send in logic parts, implement callbacks for that 2024-02-25 00:49:57 +03:00
06692b4be9 Implement getpos and teleport commands 2024-02-24 20:38:45 +03:00
3ecc265f4d Use float for spawn command (no need to multiply game coordinates by 100 anymore) 2024-02-24 15:48:33 +03:00
d6d7ca6e76 Handle movement (fixes position reset after formation change) 2024-02-24 15:37:52 +03:00
e20c69f465 More combat handlers 2024-02-24 15:23:00 +03:00
32b7c987bf Set EnergyMax to 0 when UnlimitedEnergy enabled, more fields in Concomitants 2024-02-23 16:00:08 +03:00
832f4ab3eb Unlimited energy option in gameplay.json (unlimited ultimate) 2024-02-23 15:50:16 +03:00
thexeondev
97bbbbeae9
Merge pull request #4 from ryozm/feat-/resonantChainUnlock
Support unlocking Resonance Chains.
2024-02-23 13:24:16 +03:00
thexeondev
3cf22f41d9
Update RoleController.cs 2024-02-23 13:24:02 +03:00
Ryo
32349137d3 Unlock resonant chain 2024-02-23 14:43:11 +08:00
934ef7d74a Implement PlayerAttrs as intended 2024-02-22 23:09:07 +03:00
aa2bab9645 Unlock all items by default 2024-02-22 22:32:57 +03:00
thexeondev
6fc374af32
Merge pull request #3 from ryozm/fix/roleIds-bug
Fix roleIds IndexOutOfRangeException
2024-02-22 20:59:35 +03:00
thexeondev
c292baf580
Merge pull request #2 from alt3ri/patch-1
Add HeadFrame (Namecard)
2024-02-22 20:59:19 +03:00
thexeondev
e0cd311bfc
Merge branch 'master' into patch-1 2024-02-22 20:58:36 +03:00
Ryo
092fd8df28 Fix roleIds IndexOutOfRangeException 2024-02-22 22:15:26 +08:00
ec7db3384d Glider and wall run 2024-02-22 01:51:39 +03:00
a46dc434d8 Set entity equipment properly 2024-02-22 01:00:48 +03:00
3e7ec6f8ad Weapon stats + correct take on/off 2024-02-22 00:14:56 +03:00
1bd1931807 Weapon inventory (unlock all + switching) 2024-02-21 17:47:37 +03:00
e70c6aa946 Auto-fixer for multiple spaces in commands 2024-02-21 00:32:17 +03:00
alt
4e5f873003
Add HeadFrame (Namecard) 2024-02-11 16:36:04 +07:00
59 changed files with 24225 additions and 392 deletions

View file

@ -16,7 +16,12 @@ public class ConfigManager
logger.LogInformation("Loaded {count} config collections", _collectionsByEnum.Count); logger.LogInformation("Loaded {count} config collections", _collectionsByEnum.Count);
} }
public ConfigCollection GetCollection<TConfigType>() public IEnumerable<TConfig> Enumerate<TConfig>() where TConfig : IConfig
{
return GetCollection<TConfig>().Enumerate<TConfig>();
}
public ConfigCollection GetCollection<TConfigType>() where TConfigType : IConfig
{ {
return _collectionsByType[typeof(TConfigType)]; return _collectionsByType[typeof(TConfigType)];
} }

View file

@ -5,5 +5,6 @@ public enum ConfigType
Weapon, Weapon,
BaseProperty, BaseProperty,
LevelEntity, LevelEntity,
Blueprint Blueprint,
ItemInfo
} }

View file

@ -0,0 +1,38 @@
using Core.Config.Attributes;
namespace Core.Config;
[ConfigCollection("item/iteminfo.json")]
public class ItemInfoConfig : IConfig
{
public ConfigType Type => ConfigType.ItemInfo;
public int Identifier => Id;
public int Id { get; set; }
public int ItemType { get; set; }
public string Name { get; set; } = string.Empty;
public List<int> ShowTypes { get; set; } = [];
public string AttributesDescription { get; set; } = string.Empty;
public string BgDescription { get; set; } = string.Empty;
public string Mesh { get; set; } = string.Empty;
public int QualityId { get; set; }
public int MainTypeId { get; set; }
public int RedDotDisableRule { get; set; }
public int UseCountLimit { get; set; }
public int SortIndex { get; set; }
public int MaxCapcity { get; set; }
public int MaxStackableNum { get; set; }
public int UseLevel { get; set; }
public int BeginTimeStamp { get; set; }
public int DurationStamp { get; set; }
public bool ShowUseButton { get; set; }
public int ObtainedShow { get; set; }
public string ObtainedShowDescription { get; set; } = string.Empty;
public int EntityConfig { get; set; }
public int NumLimit { get; set; }
public bool ShowInBag { get; set; }
public bool Destructible { get; set; }
public int ItemBuffType { get; set; }
public bool SpecialItem { get; set; }
public bool UiPlayItem { get; set; }
}

View file

@ -14,6 +14,7 @@
<ItemGroup> <ItemGroup>
<None Remove="data\config\blueprint\blueprintconfig.json" /> <None Remove="data\config\blueprint\blueprintconfig.json" />
<None Remove="data\config\item\iteminfo.json" />
<None Remove="data\config\property\baseproperty.json" /> <None Remove="data\config\property\baseproperty.json" />
<None Remove="data\config\weapon\weaponconf.json" /> <None Remove="data\config\weapon\weaponconf.json" />
</ItemGroup> </ItemGroup>

File diff suppressed because it is too large Load diff

View file

@ -10,14 +10,14 @@
"ResonLevelLimit": 5, "ResonLevelLimit": 5,
"FirstPropId": { "FirstPropId": {
"Id": 7, "Id": 7,
"Value": 54, "Value": 47,
"IsRatio": false "IsRatio": false
}, },
"FirstCurve": 1, "FirstCurve": 1,
"SecondPropId": { "SecondPropId": {
"Id": 11, "Id": 10007,
"Value": 432, "Value": 0.08100000023841858,
"IsRatio": false "IsRatio": true
}, },
"SecondCurve": 2, "SecondCurve": 2,
"ResonId": 21010015, "ResonId": 21010015,
@ -26,7 +26,7 @@
"Desc": "WeaponConf_21010015_Desc", "Desc": "WeaponConf_21010015_Desc",
"DescParams": [ "DescParams": [
{ {
"ArrayString": [ "12%", "15%", "18%", "21%", "24%" ] "ArrayString": [ "12.8%", "16%", "19.2%", "22.4%", "25.6%" ]
}, },
{ {
"ArrayString": [ "8%", "10%", "12%", "14%", "16%" ] "ArrayString": [ "8%", "10%", "12%", "14%", "16%" ]
@ -35,7 +35,7 @@
"ArrayString": [ "2", "2", "2", "2", "2" ] "ArrayString": [ "2", "2", "2", "2", "2" ]
}, },
{ {
"ArrayString": [ "8", "8", "8", "8", "8" ] "ArrayString": [ "12", "12", "12", "12", "12" ]
} }
], ],
"TypeDescription": "WeaponConf_21010015_TypeDescription", "TypeDescription": "WeaponConf_21010015_TypeDescription",
@ -84,16 +84,16 @@
"Desc": "WeaponConf_21020015_Desc", "Desc": "WeaponConf_21020015_Desc",
"DescParams": [ "DescParams": [
{ {
"ArrayString": [ "16%", "20%", "24%", "28%", "32%" ] "ArrayString": [ "12.8%", "16%", "19.2%", "22.4%", "25.6%" ]
}, },
{ {
"ArrayString": [ "8%", "10%", "12%", "14%", "16%" ] "ArrayString": [ "6%", "7.5%", "9%", "10.5%", "12%" ]
}, },
{ {
"ArrayString": [ "2", "2", "2", "2", "2" ] "ArrayString": [ "2", "2", "2", "2", "2" ]
}, },
{ {
"ArrayString": [ "8", "8", "8", "8", "8" ] "ArrayString": [ "10", "10", "10", "10", "10" ]
} }
], ],
"TypeDescription": "WeaponConf_21020015_TypeDescription", "TypeDescription": "WeaponConf_21020015_TypeDescription",
@ -142,16 +142,16 @@
"Desc": "WeaponConf_21030015_Desc", "Desc": "WeaponConf_21030015_Desc",
"DescParams": [ "DescParams": [
{ {
"ArrayString": [ "12%", "15%", "18%", "21%", "24%" ] "ArrayString": [ "12.8%", "16%", "19.2%", "22.4%", "25.6%" ]
}, },
{ {
"ArrayString": [ "2.4%", "3%", "3.6%", "4.2%", "4.8%" ] "ArrayString": [ "10%", "12.5%", "15%", "17.5%", "20%" ]
}, },
{ {
"ArrayString": [ "5", "5", "5", "5", "5" ] "ArrayString": [ "1", "1", "1", "1", "1" ]
}, },
{ {
"ArrayString": [ "8", "8", "8", "8", "8" ] "ArrayString": [ "14", "14", "14", "14", "14" ]
} }
], ],
"TypeDescription": "WeaponConf_21030015_TypeDescription", "TypeDescription": "WeaponConf_21030015_TypeDescription",
@ -189,9 +189,9 @@
}, },
"FirstCurve": 1, "FirstCurve": 1,
"SecondPropId": { "SecondPropId": {
"Id": 11, "Id": 10007,
"Value": 864, "Value": 0.08100000023841858,
"IsRatio": false "IsRatio": true
}, },
"SecondCurve": 2, "SecondCurve": 2,
"ResonId": 21040015, "ResonId": 21040015,
@ -200,7 +200,7 @@
"Desc": "WeaponConf_21040015_Desc", "Desc": "WeaponConf_21040015_Desc",
"DescParams": [ "DescParams": [
{ {
"ArrayString": [ "8%", "10%", "12%", "14%", "16%" ] "ArrayString": [ "12.8%", "16%", "19.2%", "22.4%", "25.6%" ]
}, },
{ {
"ArrayString": [ "10%", "12.5%", "15%", "17.5%", "20%" ] "ArrayString": [ "10%", "12.5%", "15%", "17.5%", "20%" ]
@ -250,9 +250,9 @@
}, },
"FirstCurve": 1, "FirstCurve": 1,
"SecondPropId": { "SecondPropId": {
"Id": 11, "Id": 10007,
"Value": 1280, "Value": 0.11999999731779099,
"IsRatio": false "IsRatio": true
}, },
"SecondCurve": 2, "SecondCurve": 2,
"ResonId": 21050015, "ResonId": 21050015,
@ -261,7 +261,7 @@
"Desc": "WeaponConf_21050015_Desc", "Desc": "WeaponConf_21050015_Desc",
"DescParams": [ "DescParams": [
{ {
"ArrayString": [ "16%", "20%", "24%", "28%", "32%" ] "ArrayString": [ "12.8%", "16%", "19.2%", "22.4%", "25.6%" ]
}, },
{ {
"ArrayString": [ "3.2%", "4%", "4.8%", "5.6%", "6.4%" ] "ArrayString": [ "3.2%", "4%", "4.8%", "5.6%", "6.4%" ]
@ -1670,13 +1670,13 @@
"ResonLevelLimit": 5, "ResonLevelLimit": 5,
"FirstPropId": { "FirstPropId": {
"Id": 7, "Id": 7,
"Value": 31, "Value": 27,
"IsRatio": false "IsRatio": false
}, },
"FirstCurve": 1, "FirstCurve": 1,
"SecondPropId": { "SecondPropId": {
"Id": 10010, "Id": 10010,
"Value": 0.10260000079870224, "Value": 0.13680000603199005,
"IsRatio": true "IsRatio": true
}, },
"SecondCurve": 2, "SecondCurve": 2,
@ -1834,8 +1834,8 @@
}, },
"FirstCurve": 1, "FirstCurve": 1,
"SecondPropId": { "SecondPropId": {
"Id": 10007, "Id": 10010,
"Value": 0.1080000028014183, "Value": 0.13680000603199005,
"IsRatio": true "IsRatio": true
}, },
"SecondCurve": 2, "SecondCurve": 2,
@ -1886,7 +1886,7 @@
}, },
"FirstCurve": 1, "FirstCurve": 1,
"SecondPropId": { "SecondPropId": {
"Id": 10002, "Id": 10007,
"Value": 0.08100000023841858, "Value": 0.08100000023841858,
"IsRatio": true "IsRatio": true
}, },
@ -2248,13 +2248,13 @@
"ResonLevelLimit": 5, "ResonLevelLimit": 5,
"FirstPropId": { "FirstPropId": {
"Id": 7, "Id": 7,
"Value": 26, "Value": 24,
"IsRatio": false "IsRatio": false
}, },
"FirstCurve": 1, "FirstCurve": 1,
"SecondPropId": { "SecondPropId": {
"Id": 10010, "Id": 10010,
"Value": 0.06840000301599503, "Value": 0.08550000190734863,
"IsRatio": true "IsRatio": true
}, },
"SecondCurve": 2, "SecondCurve": 2,
@ -3128,7 +3128,7 @@
"ArrayString": [ "2", "2", "2", "2", "2" ] "ArrayString": [ "2", "2", "2", "2", "2" ]
}, },
{ {
"ArrayString": [ "10", "10", "10", "10", "10" ] "ArrayString": [ "14", "14", "14", "14", "14" ]
} }
], ],
"TypeDescription": "WeaponConf_21010016_TypeDescription", "TypeDescription": "WeaponConf_21010016_TypeDescription",
@ -3180,13 +3180,16 @@
"ArrayString": [ "12%", "15%", "18%", "21%", "24%" ] "ArrayString": [ "12%", "15%", "18%", "21%", "24%" ]
}, },
{ {
"ArrayString": [ "20%", "25%", "30%", "35%", "40%" ] "ArrayString": [ "12%", "15%", "18%", "21%", "24%" ]
},
{
"ArrayString": [ "12%", "15%", "18%", "21%", "24%" ]
}, },
{ {
"ArrayString": [ "2", "2", "2", "2", "2" ] "ArrayString": [ "2", "2", "2", "2", "2" ]
}, },
{ {
"ArrayString": [ "18", "18", "18", "18", "18" ] "ArrayString": [ "5", "5", "5", "5", "5" ]
} }
], ],
"TypeDescription": "WeaponConf_21050016_TypeDescription", "TypeDescription": "WeaponConf_21050016_TypeDescription",

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

@ -0,0 +1,67 @@
using GameServer.Controllers.Attributes;
using GameServer.Models;
using GameServer.Models.Chat;
using GameServer.Network;
using GameServer.Systems.Entity;
using Protocol;
namespace GameServer.Controllers.ChatCommands;
[ChatCommandCategory("player")]
internal class ChatPlayerCommandHandler
{
private readonly ChatRoom _helperRoom;
private readonly PlayerSession _session;
private readonly CreatureController _creatureController;
public ChatPlayerCommandHandler(ModelManager modelManager, PlayerSession session, CreatureController creatureController)
{
_helperRoom = modelManager.Chat.GetChatRoom(1338);
_session = session;
_creatureController = creatureController;
}
[ChatCommand("getpos")]
[ChatCommandDesc("/player getpos - shows current player coordinates")]
public void OnPlayerGetPosCommand(string[] _)
{
PlayerEntity? entity = _creatureController.GetPlayerEntity();
if (entity == null) return;
_helperRoom.AddMessage(1338, 0, $"Your current position: ({entity.Pos.X / 100}, {entity.Pos.Y / 100}, {entity.Pos.Z / 100})");
}
[ChatCommand("teleport")]
[ChatCommandDesc("/player teleport [x] [y] [z] - performing fast travel to specified position")]
public async Task OnPlayerTeleportCommand(string[] args)
{
if (args.Length != 3 || !float.TryParse(args[0], out float x)
|| !float.TryParse(args[1], out float y)
|| !float.TryParse(args[2], out float z))
{
_helperRoom.AddMessage(1338, 0, $"Usage: /player teleport [x] [y] [z]");
return;
}
PlayerEntity? entity = _creatureController.GetPlayerEntity();
if (entity != null)
{
await _session.Push(MessageId.TeleportNotify, new TeleportNotify
{
PosX = x * 100,
PosY = y * 100,
PosZ = z * 100,
PosA = 0,
MapId = 8,
Reason = (int)TeleportReason.Gm,
TransitionOption = new TransitionOptionPb
{
TransitionType = (int)TransitionType.Empty
}
});
}
_helperRoom.AddMessage(1338, 0, $"Successfully performed fast travel to ({x}, {y}, {z})");
}
}

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;
@ -34,9 +33,9 @@ internal class ChatSpawnCommandHandler
{ {
if (args.Length != 4 || if (args.Length != 4 ||
!(int.TryParse(args[0], out int levelEntityId) && !(int.TryParse(args[0], out int levelEntityId) &&
int.TryParse(args[1], out int x) && float.TryParse(args[1], out float x) &&
int.TryParse(args[2], out int y) && float.TryParse(args[2], out float y) &&
int.TryParse(args[3], out int z))) float.TryParse(args[3], out float z)))
{ {
_helperRoom.AddMessage(1338, 0, "Usage: /spawn monster [id] [x] [y] [z]"); _helperRoom.AddMessage(1338, 0, "Usage: /spawn monster [id] [x] [y] [z]");
return; return;
@ -45,19 +44,13 @@ internal class ChatSpawnCommandHandler
MonsterEntity monster = _entityFactory.CreateMonster(levelEntityId); MonsterEntity monster = _entityFactory.CreateMonster(levelEntityId);
monster.Pos = new() monster.Pos = new()
{ {
X = x, X = x * 100,
Y = y, Y = y * 100,
Z = z 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

@ -1,14 +1,14 @@
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Controllers.ChatCommands; 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;
internal class ChatController : Controller internal partial class ChatController : Controller
{ {
private readonly ModelManager _modelManager; private readonly ModelManager _modelManager;
@ -42,7 +42,8 @@ internal class ChatController : Controller
} }
else else
{ {
string[] split = request.Content[1..].Split(' '); string content = MultipleSpacesRegex().Replace(request.Content, " ");
string[] split = content[1..].Split(' ');
if (split.Length >= 2) if (split.Length >= 2)
{ {
await chatCommandManager.InvokeCommandAsync(split[0], split[1], split[2..]); await chatCommandManager.InvokeCommandAsync(split[0], split[1], split[2..]);
@ -85,4 +86,7 @@ internal class ChatController : Controller
return builder.ToString(); return builder.ToString();
} }
[GeneratedRegex(@"\s+")]
private static partial Regex MultipleSpacesRegex();
} }

View file

@ -1,7 +1,6 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography;
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Systems.Entity; using GameServer.Systems.Entity;
@ -33,8 +32,57 @@ internal class CombatManager
_creatureController = creatureController; _creatureController = creatureController;
} }
[CombatRequest(CombatRequestData.MessageOneofCase.RTimeStopRequest)]
public CombatResponseData OnRTimeStopRequest(CombatRequestContext context) => new()
{
CombatCommon = context.Request.CombatCommon,
RTimeStopResponse = new()
};
[CombatRequest(CombatRequestData.MessageOneofCase.ActivateBuffRequest)]
public CombatResponseData OnActivateBuffRequest(CombatRequestContext context) => new()
{
CombatCommon = context.Request.CombatCommon,
ActivateBuffResponse = new()
};
[CombatRequest(CombatRequestData.MessageOneofCase.UseSkillRequest)]
public CombatResponseData OnUseSkillRequest(CombatRequestContext context) => new()
{
CombatCommon = context.Request.CombatCommon,
UseSkillResponse = new()
{
SkillSingleId = context.Request.UseSkillRequest.SkillSingleId,
UseSkillInfo = context.Request.UseSkillRequest.UseSkillInfo
}
};
[CombatRequest(CombatRequestData.MessageOneofCase.ApplyGameplayEffectRequest)]
public CombatResponseData OnApplyGameplayEffectRequest(CombatRequestContext context) => new()
{
CombatCommon = context.Request.CombatCommon,
ApplyGameplayEffectResponse = new ApplyGameplayEffectResponse()
};
[CombatRequest(CombatRequestData.MessageOneofCase.RemoveGameplayEffectRequest)]
public CombatResponseData OnRemoveGameplayEffectRequest(CombatRequestContext context) => new()
{
CombatCommon = context.Request.CombatCommon,
RemoveGameplayEffectResponse = new RemoveGameplayEffectResponse()
};
[CombatRequest(CombatRequestData.MessageOneofCase.CreateBulletRequest)]
public CombatResponseData OnCreateBulletRequest(CombatRequestContext context)
{
return new CombatResponseData
{
CombatCommon = context.Request.CombatCommon,
CreateBulletResponse = new()
};
}
[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;
@ -57,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
@ -199,13 +235,19 @@ internal class CombatManager
if (entity.ComponentSystem.TryGet(out EntityFsmComponent? fsmComponent)) if (entity.ComponentSystem.TryGet(out EntityFsmComponent? fsmComponent))
{ {
DFsm? dfsm = fsmComponent.Fsms.FirstOrDefault(fsm => fsm.FsmId == request.FsmId); DFsm? dfsm = fsmComponent.Fsms.FirstOrDefault(fsm => fsm.FsmId == request.FsmId);
dfsm ??= new()
if (dfsm == null)
{
dfsm = new DFsm
{ {
FsmId = request.FsmId, FsmId = request.FsmId,
Status = 1, Status = 1,
Flag = (int)EFsmStateFlag.Confirmed Flag = (int)EFsmStateFlag.Confirmed
}; };
fsmComponent.Fsms.Add(dfsm);
}
dfsm.CurrentState = request.State; dfsm.CurrentState = request.State;
context.Notifies.Add(new CombatNotifyData context.Notifies.Add(new CombatNotifyData
{ {

View file

@ -1,18 +1,44 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Controllers.Combat; using GameServer.Controllers.Combat;
using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages; using GameServer.Systems.Entity;
using GameServer.Systems.Event;
using Microsoft.Extensions.Logging;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
internal class CombatMessageController : Controller internal class CombatMessageController : Controller
{ {
public CombatMessageController(PlayerSession session) : base(session) private readonly ILogger _logger;
public CombatMessageController(PlayerSession session, ILogger<CombatMessageController> logger) : base(session)
{ {
// CombatMessageController. _logger = logger;
} }
[NetEvent(MessageId.CombatSendPackRequest)] // TODO: CombatSendPackRequest is important [NetEvent(MessageId.MovePackagePush)]
public async Task OnMovePackagePush(MovePackagePush push, EntitySystem entitySystem, EventSystem eventSystem, ModelManager modelManager)
{
foreach (MovingEntityData movingEntityData in push.MovingEntities)
{
EntityBase? entity = entitySystem.Get<EntityBase>(movingEntityData.EntityId);
if (entity == null)
{
_logger.LogWarning("OnMovePackagePush: moving entity not found! Id: {entityId}", movingEntityData.EntityId);
continue;
}
MoveReplaySample lastMoveReplay = movingEntityData.MoveInfos.Last();
entity.Pos.MergeFrom(lastMoveReplay.Location);
entity.Rot.MergeFrom(lastMoveReplay.Rotation);
if (entity.Id == modelManager.Creature.PlayerEntityId)
await eventSystem.Emit(GameEventType.PlayerPositionChanged);
}
}
[NetEvent(MessageId.CombatSendPackRequest)]
public async Task<RpcResult> OnCombatSendPackRequest(CombatSendPackRequest request, CombatManager combatManager) public async Task<RpcResult> OnCombatSendPackRequest(CombatSendPackRequest request, CombatManager combatManager)
{ {
CombatReceivePackNotify combatPackNotify = new(); CombatReceivePackNotify combatPackNotify = new();

View file

@ -1,27 +1,41 @@
using Core.Config; using Core.Config;
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
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.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 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;
public CreatureController(PlayerSession session, EntitySystem entitySystem, EntityFactory entityFactory, ModelManager modelManager, ConfigManager configManager) : base(session) private readonly GameplayFeatureSettings _gameplayFeatures;
private readonly Vector _lastDynamicSpawnPos;
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;
_lastDynamicSpawnPos = new();
} }
public async Task JoinScene(int instanceId) public async Task JoinScene(int instanceId)
@ -30,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)]
@ -79,51 +85,29 @@ 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();
} }
[GameEvent(GameEventType.PlayerPositionChanged)]
public void OnPlayerPositionChanged()
{
_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)]
public async Task OnVisionSkillChanged() public async Task OnVisionSkillChanged()
{ {
@ -168,6 +152,9 @@ internal class CreatureController : Controller
_modelManager.Creature.PlayerEntityId = newEntity.Id; _modelManager.Creature.PlayerEntityId = newEntity.Id;
newEntity.IsCurrentRole = true; newEntity.IsCurrentRole = true;
newEntity.Pos.MergeFrom(prevEntity.Pos);
newEntity.Rot.MergeFrom(prevEntity.Rot);
await UpdateAiHate(); await UpdateAiHate();
} }
@ -241,24 +228,37 @@ 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++)
{ {
PlayerEntity entity = _entityFactory.CreatePlayer(_modelManager.Formation.RoleIds[i], _modelManager.Player.Id); int roleId = _modelManager.Formation.RoleIds[i];
PlayerEntity entity = _entityFactory.CreatePlayer(roleId, _modelManager.Player.Id);
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.InitProps(_configManager.GetConfig<BasePropertyConfig>(entity.ConfigId)!);
CreateConcomitants(entity); CreateConcomitants(entity);
entity.WeaponId = _modelManager.Inventory.GetEquippedWeapon(roleId)?.Id ?? 0;
// Give weapon to entity
RoleInfoConfig roleConfig = _configManager.GetConfig<RoleInfoConfig>(entity.ConfigId)!;
WeaponConfig weaponConfig = _configManager.GetConfig<WeaponConfig>(roleConfig.InitWeaponItemId)!;
entity.WeaponId = weaponConfig.ItemId;
if (i == 0) _modelManager.Creature.PlayerEntityId = entity.Id; if (i == 0) _modelManager.Creature.PlayerEntityId = entity.Id;
if (_gameplayFeatures.UnlimitedEnergy)
{
EntityAttributeComponent attr = entity.ComponentSystem.Get<EntityAttributeComponent>();
attr.SetAttribute(EAttributeType.EnergyMax, 0);
attr.SetAttribute(EAttributeType.SpecialEnergy1Max, 0);
attr.SetAttribute(EAttributeType.SpecialEnergy2Max, 0);
attr.SetAttribute(EAttributeType.SpecialEnergy3Max, 0);
attr.SetAttribute(EAttributeType.SpecialEnergy4Max, 0);
} }
playerEntities[i] = entity;
}
_entitySystem.Add(playerEntities);
} }
private void CreateConcomitants(PlayerEntity entity) private void CreateConcomitants(PlayerEntity entity)
@ -272,10 +272,10 @@ 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.Get<EntityConcomitantsComponent>(); EntityConcomitantsComponent concomitants = entity.ComponentSystem.Create<EntityConcomitantsComponent>();
concomitants.CustomEntityIds.Clear(); concomitants.CustomEntityIds.Clear();
concomitants.VisionEntityId = concomitant.Id;
concomitants.CustomEntityIds.Add(concomitant.Id); concomitants.CustomEntityIds.Add(concomitant.Id);
EntitySummonerComponent summoner = concomitant.ComponentSystem.Create<EntitySummonerComponent>(); EntitySummonerComponent summoner = concomitant.ComponentSystem.Create<EntitySummonerComponent>();
@ -283,24 +283,51 @@ internal class CreatureController : Controller
summoner.SummonConfigId = summonConfigId; summoner.SummonConfigId = summonConfigId;
summoner.SummonType = ESummonType.ConcomitantCustom; summoner.SummonType = ESummonType.ConcomitantCustom;
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

@ -1,6 +1,10 @@
using GameServer.Controllers.Attributes; using Core.Config;
using GameServer.Controllers.Attributes;
using GameServer.Extensions.Logic;
using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages; using GameServer.Systems.Entity;
using GameServer.Systems.Event;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
@ -12,14 +16,101 @@ internal class InventoryController : Controller
} }
[NetEvent(MessageId.NormalItemRequest)] [NetEvent(MessageId.NormalItemRequest)]
public RpcResult OnNormalItemRequest() => Response(MessageId.NormalItemResponse, new NormalItemResponse()); public RpcResult OnNormalItemRequest(ModelManager modelManager) => Response(MessageId.NormalItemResponse, new NormalItemResponse
{
NormalItemList = { modelManager.Inventory.ItemList }
});
[NetEvent(MessageId.WeaponItemRequest)] [NetEvent(MessageId.WeaponItemRequest)]
public RpcResult OnWeaponItemRequest() => Response(MessageId.WeaponItemResponse, new WeaponItemResponse()); public RpcResult OnWeaponItemRequest(ModelManager modelManager) => Response(MessageId.WeaponItemResponse, new WeaponItemResponse
{
WeaponItemList =
{
modelManager.Inventory.WeaponList
}
});
[NetEvent(MessageId.PhantomItemRequest)] [NetEvent(MessageId.PhantomItemRequest)]
public RpcResult OnPhantomItemRequest() => Response(MessageId.PhantomItemResponse, new PhantomItemResponse()); public RpcResult OnPhantomItemRequest() => Response(MessageId.PhantomItemResponse, new PhantomItemResponse());
[NetEvent(MessageId.ItemExchangeInfoRequest)] [NetEvent(MessageId.ItemExchangeInfoRequest)]
public RpcResult OnItemExchangeInfoRequest() => Response(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse()); public RpcResult OnItemExchangeInfoRequest() => Response(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse());
[NetEvent(MessageId.EquipTakeOnRequest)]
public RpcResult OnEquipTakeOnRequest(EquipTakeOnRequest request, ModelManager modelManager, CreatureController creatureController, RoleController roleController, ConfigManager configManager)
{
WeaponItem? weapon = modelManager.Inventory.GetWeaponById(request.Data.EquipIncId);
if (weapon == null) return Response(MessageId.EquipTakeOnResponse, new EquipTakeOnResponse
{
ErrorCode = (int)ErrorCode.ErrItemIdInvaild
});
WeaponConfig weaponConf = configManager.GetConfig<WeaponConfig>(weapon.Id)!;
roleInfo? role = modelManager.Roles.GetRoleById(request.Data.RoleId);
if (role == null) return Response(MessageId.EquipTakeOnResponse, new EquipTakeOnResponse
{
ErrorCode = (int)ErrorCode.NotValidRole
});
// Take off previous weapon
WeaponItem? prevWeapon = modelManager.Inventory.WeaponList.SingleOrDefault(weapon => weapon.RoleId == role.RoleId);
if (prevWeapon != null) prevWeapon.RoleId = 0;
// Set new weapon
weapon.RoleId = role.RoleId;
roleController.ApplyWeaponPropertiesToRole(role.RoleId, weaponConf);
// Update entity (if this role is currently active)
PlayerEntity? entity = creatureController.GetPlayerEntityByRoleId(request.Data.RoleId);
entity?.ChangeEquipment(weapon.Id);
entity?.ChangeGameplayAttributes(role.GetAttributeList());
// Response
EquipTakeOnResponse response = new()
{
DataList =
{
new RoleLoadEquipData
{
RoleId = request.Data.RoleId,
Pos = request.Data.Pos,
EquipIncId = request.Data.EquipIncId
}
}
};
if (prevWeapon != null)
{
response.DataList.Add(new RoleLoadEquipData
{
EquipIncId = prevWeapon.IncrId
});
}
return Response(MessageId.EquipTakeOnResponse, response);
}
[GameEvent(GameEventType.EnterGame)]
public async Task OnEnterGame()
{
await Session.Push(MessageId.ItemPkgOpenNotify, new ItemPkgOpenNotify
{
OpenPkg = { 0, 2, 1, 3, 4, 5, 6, 7 }
});
}
[GameEvent(GameEventType.DebugUnlockAllItems)]
public void DebugUnlockAllWeapons(ConfigManager configManager, ModelManager modelManager)
{
foreach (WeaponConfig weaponConf in configManager.Enumerate<WeaponConfig>())
{
modelManager.Inventory.AddNewWeapon(weaponConf.ItemId);
}
foreach (ItemInfoConfig itemInfo in configManager.Enumerate<ItemInfoConfig>())
{
modelManager.Inventory.AddItem(itemInfo.Id, itemInfo.MaxStackableNum);
}
}
} }

View file

@ -16,6 +16,10 @@ internal class LoginController : Controller
{ {
await eventSystem.Emit(GameEventType.Login); await eventSystem.Emit(GameEventType.Login);
// Debug
await eventSystem.Emit(GameEventType.DebugUnlockAllRoles);
await eventSystem.Emit(GameEventType.DebugUnlockAllItems);
return Response(MessageId.LoginResponse, new LoginResponse return Response(MessageId.LoginResponse, new LoginResponse
{ {
Code = 0, Code = 0,

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,7 +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.Event; using GameServer.Systems.Event;
using Protocol; using Protocol;
@ -22,34 +21,8 @@ internal class PlayerInfoController : Controller
{ {
RandomSeed = 1337, RandomSeed = 1337,
Id = player.Id, Id = player.Id,
Birthday = 0, Birthday = player.BirthDay,
Attributes = Attributes = { player.Attributes },
{
new PlayerAttr
{
Key = (int)PlayerAttrKey.Name,
ValueType = (int)PlayerAttrType.String,
StringValue = player.Name
},
new PlayerAttr
{
Key = (int)PlayerAttrKey.Level,
ValueType = (int)PlayerAttrType.Int32,
Int32Value = 10
},
new PlayerAttr
{
Key = (int)PlayerAttrKey.HeadPhoto,
ValueType = (int)PlayerAttrType.Int32,
Int32Value = 1402
},
new PlayerAttr
{
Key = (int)PlayerAttrKey.Sex,
ValueType = (int)PlayerAttrType.Int32,
Int32Value = 1
}
},
RoleShowList = RoleShowList =
{ {
new RoleShowEntry new RoleShowEntry
@ -72,6 +45,47 @@ internal class PlayerInfoController : Controller
await Session.Push(MessageId.BasicInfoNotify, basicInfo); await Session.Push(MessageId.BasicInfoNotify, basicInfo);
} }
[NetEvent(MessageId.ChangeHeadPhotoRequest)]
public RpcResult OnChangeHeadPhotoRequest(ChangeHeadPhotoRequest request, ModelManager modelManager)
{
modelManager.Player.SetAttribute(PlayerAttrKey.HeadPhoto, request.HeadPhotoId);
return Response(MessageId.ChangeHeadPhotoResponse, new ChangeHeadPhotoResponse
{
HeadPhotoId = request.HeadPhotoId
});
}
[NetEvent(MessageId.BirthdayInitRequest)]
public RpcResult OnBirthdayInitRequest(BirthdayInitRequest request, ModelManager modelManager)
{
modelManager.Player.BirthDay = request.Birthday;
return Response(MessageId.BirthdayInitResponse, new BirthdayInitResponse());
}
[NetEvent(MessageId.ModifySignatureRequest)]
public RpcResult OnModifySignatureRequest(ModifySignatureRequest request, ModelManager modelManager)
{
modelManager.Player.SetAttribute(PlayerAttrKey.Sign, request.Signature);
return Response(MessageId.ModifySignatureResponse, new ModifySignatureResponse
{
Signature = request.Signature
});
}
[NetEvent(MessageId.ModifyNameRequest)]
public RpcResult OnModifyNameRequest(ModifyNameRequest request, ModelManager modelManager)
{
modelManager.Player.SetAttribute(PlayerAttrKey.Name, request.Name);
return Response(MessageId.ModifyNameResponse, new ModifyNameResponse
{
Name = request.Name
});
}
[NetEvent(MessageId.PlayerBasicInfoGetRequest)] [NetEvent(MessageId.PlayerBasicInfoGetRequest)]
public RpcResult OnPlayerBasicInfoGetRequest() public RpcResult OnPlayerBasicInfoGetRequest()
{ {

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,36 +1,55 @@
using Core.Config; using Core.Config;
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Extensions.Logic;
using GameServer.Models; using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages;
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)]
public void UnlockAllRoles(ConfigManager configManager)
{
foreach (RoleInfoConfig roleConfig in configManager.Enumerate<RoleInfoConfig>())
{
roleInfo role = _modelManager.Roles.Create(roleConfig.Id);
role.BaseProp.AddRange(CreateBasePropList(configManager.GetConfig<BasePropertyConfig>(roleConfig.Id)));
WeaponItem weapon = _modelManager.Inventory.AddNewWeapon(roleConfig.InitWeaponItemId);
weapon.RoleId = role.RoleId;
role.ApplyWeaponProperties(configManager.GetConfig<WeaponConfig>(weapon.Id)!);
}
} }
[GameEvent(GameEventType.EnterGame)] [GameEvent(GameEventType.EnterGame)]
public async Task OnEnterGame(ModelManager modelManager, ConfigManager configManager) public async Task OnEnterGame(ModelManager modelManager)
{ {
PlayerModel player = modelManager.Player;
await Session.Push(MessageId.PbGetRoleListNotify, new PbGetRoleListNotify await Session.Push(MessageId.PbGetRoleListNotify, new PbGetRoleListNotify
{ {
RoleList = RoleList =
{ {
configManager.GetCollection(ConfigType.RoleInfo) modelManager.Roles.Roles
.Enumerate<RoleInfoConfig>()
.Select(config => new roleInfo
{
RoleId = config.Id,
Level = 1,
BaseProp = { CreateBasePropList(configManager.GetConfig<BasePropertyConfig>(config.Id)) }
})
} }
}); });
} }
@ -48,147 +67,177 @@ internal class RoleController : Controller
[NetEvent(MessageId.RoleFavorListRequest)] [NetEvent(MessageId.RoleFavorListRequest)]
public RpcResult OnRoleFavorListRequest() => Response(MessageId.RoleFavorListResponse, new RoleFavorListResponse()); public RpcResult OnRoleFavorListRequest() => Response(MessageId.RoleFavorListResponse, new RoleFavorListResponse());
[NetEvent(MessageId.ResonantChainUnlockRequest)]
public RpcResult OnResonantChainUnlockRequest(ResonantChainUnlockRequest request, ModelManager modelManager, ConfigManager configManager)
{
roleInfo? role = modelManager.Roles.Roles.Find(r => r.RoleId == request.RoleId)!;
if (role != null)
{
RoleInfoConfig roleConfig = configManager.GetConfig<RoleInfoConfig>(request.RoleId)!;
int resonantChainGroupId = roleConfig.ResonantChainGroupId;
// Todo: add buff by _resonantChainGroupId
int curr = role.ResonantChainGroupIndex;
int next = Math.Min(curr + 1, 6);
role.ResonantChainGroupIndex = next;
return Response(MessageId.ResonantChainUnlockResponse, new ResonantChainUnlockResponse
{
RoleId = request.RoleId,
ResonantChainGroupIndex = next
});
}
return Response(MessageId.ResonantChainUnlockResponse, new ResonantChainUnlockResponse
{
ErrCode = (int)ErrorCode.ErrRoleResonNotActive
});
}
private static List<ArrayIntInt> CreateBasePropList(BasePropertyConfig? config) private static List<ArrayIntInt> CreateBasePropList(BasePropertyConfig? config)
{ {
List<ArrayIntInt> baseProp = []; List<ArrayIntInt> baseProp = [];
if (config == null) return baseProp; if (config == null) return baseProp;
baseProp.Add(new() { Key = (int)EAttributeType.Lv, Value = config.Lv}); baseProp.Add(new() { Key = (int)EAttributeType.Lv, Value = config.Lv });
baseProp.Add(new() { Key = (int)EAttributeType.LifeMax, Value = config.LifeMax}); baseProp.Add(new() { Key = (int)EAttributeType.LifeMax, Value = config.LifeMax });
baseProp.Add(new() { Key = (int)EAttributeType.Life, Value = config.Life}); baseProp.Add(new() { Key = (int)EAttributeType.Life, Value = config.Life });
baseProp.Add(new() { Key = (int)EAttributeType.Sheild, Value = config.Sheild}); baseProp.Add(new() { Key = (int)EAttributeType.Sheild, Value = config.Sheild });
baseProp.Add(new() { Key = (int)EAttributeType.SheildDamageChange, Value = config.SheildDamageChange}); baseProp.Add(new() { Key = (int)EAttributeType.SheildDamageChange, Value = config.SheildDamageChange });
baseProp.Add(new() { Key = (int)EAttributeType.SheildDamageReduce, Value = config.SheildDamageReduce}); baseProp.Add(new() { Key = (int)EAttributeType.SheildDamageReduce, Value = config.SheildDamageReduce });
baseProp.Add(new() { Key = (int)EAttributeType.Atk, Value = config.Atk}); baseProp.Add(new() { Key = (int)EAttributeType.Atk, Value = config.Atk });
baseProp.Add(new() { Key = (int)EAttributeType.Crit, Value = config.Crit}); baseProp.Add(new() { Key = (int)EAttributeType.Crit, Value = config.Crit });
baseProp.Add(new() { Key = (int)EAttributeType.CritDamage, Value = config.CritDamage}); baseProp.Add(new() { Key = (int)EAttributeType.CritDamage, Value = config.CritDamage });
baseProp.Add(new() { Key = (int)EAttributeType.Def, Value = config.Def}); baseProp.Add(new() { Key = (int)EAttributeType.Def, Value = config.Def });
baseProp.Add(new() { Key = (int)EAttributeType.EnergyEfficiency, Value = config.EnergyEfficiency}); baseProp.Add(new() { Key = (int)EAttributeType.EnergyEfficiency, Value = config.EnergyEfficiency });
baseProp.Add(new() { Key = (int)EAttributeType.CdReduse, Value = config.CdReduse}); baseProp.Add(new() { Key = (int)EAttributeType.CdReduse, Value = config.CdReduse });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionEfficiency, Value = config.ReactionEfficiency}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionEfficiency, Value = config.ReactionEfficiency });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeNormalSkill, Value = config.DamageChangeNormalSkill}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeNormalSkill, Value = config.DamageChangeNormalSkill });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChange, Value = config.DamageChange}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChange, Value = config.DamageChange });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReduce, Value = config.DamageReduce}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReduce, Value = config.DamageReduce });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeAuto, Value = config.DamageChangeAuto}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeAuto, Value = config.DamageChangeAuto });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeCast, Value = config.DamageChangeCast}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeCast, Value = config.DamageChangeCast });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeUltra, Value = config.DamageChangeUltra}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeUltra, Value = config.DamageChangeUltra });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeQte, Value = config.DamageChangeQte}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeQte, Value = config.DamageChangeQte });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangePhys, Value = config.DamageChangePhys}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangePhys, Value = config.DamageChangePhys });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement1, Value = config.DamageChangeElement1}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement1, Value = config.DamageChangeElement1 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement2, Value = config.DamageChangeElement2}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement2, Value = config.DamageChangeElement2 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement3, Value = config.DamageChangeElement3}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement3, Value = config.DamageChangeElement3 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement4, Value = config.DamageChangeElement4}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement4, Value = config.DamageChangeElement4 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement5, Value = config.DamageChangeElement5}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement5, Value = config.DamageChangeElement5 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement6, Value = config.DamageChangeElement6}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangeElement6, Value = config.DamageChangeElement6 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageResistancePhys, Value = config.DamageResistancePhys}); baseProp.Add(new() { Key = (int)EAttributeType.DamageResistancePhys, Value = config.DamageResistancePhys });
baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement1, Value = config.DamageResistanceElement1}); baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement1, Value = config.DamageResistanceElement1 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement2, Value = config.DamageResistanceElement2}); baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement2, Value = config.DamageResistanceElement2 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement3, Value = config.DamageResistanceElement3}); baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement3, Value = config.DamageResistanceElement3 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement4, Value = config.DamageResistanceElement4}); baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement4, Value = config.DamageResistanceElement4 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement5, Value = config.DamageResistanceElement5}); baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement5, Value = config.DamageResistanceElement5 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement6, Value = config.DamageResistanceElement6}); baseProp.Add(new() { Key = (int)EAttributeType.DamageResistanceElement6, Value = config.DamageResistanceElement6 });
baseProp.Add(new() { Key = (int)EAttributeType.HealChange, Value = config.HealChange}); baseProp.Add(new() { Key = (int)EAttributeType.HealChange, Value = config.HealChange });
baseProp.Add(new() { Key = (int)EAttributeType.HealedChange, Value = config.HealedChange}); baseProp.Add(new() { Key = (int)EAttributeType.HealedChange, Value = config.HealedChange });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReducePhys, Value = config.DamageReducePhys}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReducePhys, Value = config.DamageReducePhys });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement1, Value = config.DamageReduceElement1}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement1, Value = config.DamageReduceElement1 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement2, Value = config.DamageReduceElement2}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement2, Value = config.DamageReduceElement2 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement3, Value = config.DamageReduceElement3}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement3, Value = config.DamageReduceElement3 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement4, Value = config.DamageReduceElement4}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement4, Value = config.DamageReduceElement4 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement5, Value = config.DamageReduceElement5}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement5, Value = config.DamageReduceElement5 });
baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement6, Value = config.DamageReduceElement6}); baseProp.Add(new() { Key = (int)EAttributeType.DamageReduceElement6, Value = config.DamageReduceElement6 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange1, Value = config.ReactionChange1}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange1, Value = config.ReactionChange1 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange2, Value = config.ReactionChange2}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange2, Value = config.ReactionChange2 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange3, Value = config.ReactionChange3}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange3, Value = config.ReactionChange3 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange4, Value = config.ReactionChange4}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange4, Value = config.ReactionChange4 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange5, Value = config.ReactionChange5}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange5, Value = config.ReactionChange5 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange6, Value = config.ReactionChange6}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange6, Value = config.ReactionChange6 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange7, Value = config.ReactionChange7}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange7, Value = config.ReactionChange7 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange8, Value = config.ReactionChange8}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange8, Value = config.ReactionChange8 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange9, Value = config.ReactionChange9}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange9, Value = config.ReactionChange9 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange10, Value = config.ReactionChange10}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange10, Value = config.ReactionChange10 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange11, Value = config.ReactionChange11}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange11, Value = config.ReactionChange11 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange12, Value = config.ReactionChange12}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange12, Value = config.ReactionChange12 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange13, Value = config.ReactionChange13}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange13, Value = config.ReactionChange13 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange14, Value = config.ReactionChange14}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange14, Value = config.ReactionChange14 });
baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange15, Value = config.ReactionChange15}); baseProp.Add(new() { Key = (int)EAttributeType.ReactionChange15, Value = config.ReactionChange15 });
baseProp.Add(new() { Key = (int)EAttributeType.EnergyMax, Value = config.EnergyMax}); baseProp.Add(new() { Key = (int)EAttributeType.EnergyMax, Value = config.EnergyMax });
baseProp.Add(new() { Key = (int)EAttributeType.Energy, Value = config.Energy}); baseProp.Add(new() { Key = (int)EAttributeType.Energy, Value = config.Energy });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy1Max, Value = config.SpecialEnergy1Max}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy1Max, Value = config.SpecialEnergy1Max });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy1, Value = config.SpecialEnergy1}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy1, Value = config.SpecialEnergy1 });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy2Max, Value = config.SpecialEnergy2Max}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy2Max, Value = config.SpecialEnergy2Max });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy2, Value = config.SpecialEnergy2}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy2, Value = config.SpecialEnergy2 });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy3Max, Value = config.SpecialEnergy3Max}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy3Max, Value = config.SpecialEnergy3Max });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy3, Value = config.SpecialEnergy3}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy3, Value = config.SpecialEnergy3 });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy4Max, Value = config.SpecialEnergy4Max}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy4Max, Value = config.SpecialEnergy4Max });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy4, Value = config.SpecialEnergy4}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialEnergy4, Value = config.SpecialEnergy4 });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthMax, Value = config.StrengthMax}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthMax, Value = config.StrengthMax });
baseProp.Add(new() { Key = (int)EAttributeType.Strength, Value = config.Strength}); baseProp.Add(new() { Key = (int)EAttributeType.Strength, Value = config.Strength });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthRecover, Value = config.StrengthRecover}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthRecover, Value = config.StrengthRecover });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthPunishTime, Value = config.StrengthPunishTime}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthPunishTime, Value = config.StrengthPunishTime });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthRun, Value = config.StrengthRun}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthRun, Value = config.StrengthRun });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthSwim, Value = config.StrengthSwim}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthSwim, Value = config.StrengthSwim });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthFastSwim, Value = config.StrengthFastSwim}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthFastSwim, Value = config.StrengthFastSwim });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthClimb, Value = config.StrengthClimb}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthClimb, Value = config.StrengthClimb });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthFastClimb, Value = config.StrengthFastClimb}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthFastClimb, Value = config.StrengthFastClimb });
baseProp.Add(new() { Key = (int)EAttributeType.HardnessMax, Value = config.HardnessMax}); baseProp.Add(new() { Key = (int)EAttributeType.HardnessMax, Value = config.HardnessMax });
baseProp.Add(new() { Key = (int)EAttributeType.Hardness, Value = config.Hardness}); baseProp.Add(new() { Key = (int)EAttributeType.Hardness, Value = config.Hardness });
baseProp.Add(new() { Key = (int)EAttributeType.HardnessRecover, Value = config.HardnessRecover}); baseProp.Add(new() { Key = (int)EAttributeType.HardnessRecover, Value = config.HardnessRecover });
baseProp.Add(new() { Key = (int)EAttributeType.HardnessPunishTime, Value = config.HardnessPunishTime}); baseProp.Add(new() { Key = (int)EAttributeType.HardnessPunishTime, Value = config.HardnessPunishTime });
baseProp.Add(new() { Key = (int)EAttributeType.HardnessChange, Value = config.HardnessChange}); baseProp.Add(new() { Key = (int)EAttributeType.HardnessChange, Value = config.HardnessChange });
baseProp.Add(new() { Key = (int)EAttributeType.HardnessReduce, Value = config.HardnessReduce}); baseProp.Add(new() { Key = (int)EAttributeType.HardnessReduce, Value = config.HardnessReduce });
baseProp.Add(new() { Key = (int)EAttributeType.RageMax, Value = config.RageMax}); baseProp.Add(new() { Key = (int)EAttributeType.RageMax, Value = config.RageMax });
baseProp.Add(new() { Key = (int)EAttributeType.Rage, Value = config.Rage}); baseProp.Add(new() { Key = (int)EAttributeType.Rage, Value = config.Rage });
baseProp.Add(new() { Key = (int)EAttributeType.RageRecover, Value = config.RageRecover}); baseProp.Add(new() { Key = (int)EAttributeType.RageRecover, Value = config.RageRecover });
baseProp.Add(new() { Key = (int)EAttributeType.RagePunishTime, Value = config.RagePunishTime}); baseProp.Add(new() { Key = (int)EAttributeType.RagePunishTime, Value = config.RagePunishTime });
baseProp.Add(new() { Key = (int)EAttributeType.RageChange, Value = config.RageChange}); baseProp.Add(new() { Key = (int)EAttributeType.RageChange, Value = config.RageChange });
baseProp.Add(new() { Key = (int)EAttributeType.RageReduce, Value = config.RageReduce}); baseProp.Add(new() { Key = (int)EAttributeType.RageReduce, Value = config.RageReduce });
baseProp.Add(new() { Key = (int)EAttributeType.ToughMax, Value = config.ToughMax}); baseProp.Add(new() { Key = (int)EAttributeType.ToughMax, Value = config.ToughMax });
baseProp.Add(new() { Key = (int)EAttributeType.Tough, Value = config.Tough}); baseProp.Add(new() { Key = (int)EAttributeType.Tough, Value = config.Tough });
baseProp.Add(new() { Key = (int)EAttributeType.ToughRecover, Value = config.ToughRecover}); baseProp.Add(new() { Key = (int)EAttributeType.ToughRecover, Value = config.ToughRecover });
baseProp.Add(new() { Key = (int)EAttributeType.ToughChange, Value = config.ToughChange}); baseProp.Add(new() { Key = (int)EAttributeType.ToughChange, Value = config.ToughChange });
baseProp.Add(new() { Key = (int)EAttributeType.ToughReduce, Value = config.ToughReduce}); baseProp.Add(new() { Key = (int)EAttributeType.ToughReduce, Value = config.ToughReduce });
baseProp.Add(new() { Key = (int)EAttributeType.ToughRecoverDelayTime, Value = config.ToughRecoverDelayTime}); baseProp.Add(new() { Key = (int)EAttributeType.ToughRecoverDelayTime, Value = config.ToughRecoverDelayTime });
baseProp.Add(new() { Key = (int)EAttributeType.ElementPower1, Value = config.ElementPower1}); baseProp.Add(new() { Key = (int)EAttributeType.ElementPower1, Value = config.ElementPower1 });
baseProp.Add(new() { Key = (int)EAttributeType.ElementPower2, Value = config.ElementPower2}); baseProp.Add(new() { Key = (int)EAttributeType.ElementPower2, Value = config.ElementPower2 });
baseProp.Add(new() { Key = (int)EAttributeType.ElementPower3, Value = config.ElementPower3}); baseProp.Add(new() { Key = (int)EAttributeType.ElementPower3, Value = config.ElementPower3 });
baseProp.Add(new() { Key = (int)EAttributeType.ElementPower4, Value = config.ElementPower4}); baseProp.Add(new() { Key = (int)EAttributeType.ElementPower4, Value = config.ElementPower4 });
baseProp.Add(new() { Key = (int)EAttributeType.ElementPower5, Value = config.ElementPower5}); baseProp.Add(new() { Key = (int)EAttributeType.ElementPower5, Value = config.ElementPower5 });
baseProp.Add(new() { Key = (int)EAttributeType.ElementPower6, Value = config.ElementPower6}); baseProp.Add(new() { Key = (int)EAttributeType.ElementPower6, Value = config.ElementPower6 });
baseProp.Add(new() { Key = (int)EAttributeType.SpecialDamageChange, Value = config.SpecialDamageChange}); baseProp.Add(new() { Key = (int)EAttributeType.SpecialDamageChange, Value = config.SpecialDamageChange });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthFastClimbCost, Value = config.StrengthFastClimbCost}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthFastClimbCost, Value = config.StrengthFastClimbCost });
baseProp.Add(new() { Key = (int)EAttributeType.ElementPropertyType, Value = config.ElementPropertyType}); baseProp.Add(new() { Key = (int)EAttributeType.ElementPropertyType, Value = config.ElementPropertyType });
baseProp.Add(new() { Key = (int)EAttributeType.WeakTime, Value = config.WeakTime}); baseProp.Add(new() { Key = (int)EAttributeType.WeakTime, Value = config.WeakTime });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDefRate, Value = config.IgnoreDefRate}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDefRate, Value = config.IgnoreDefRate });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistancePhys, Value = config.IgnoreDamageResistancePhys}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistancePhys, Value = config.IgnoreDamageResistancePhys });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement1, Value = config.IgnoreDamageResistanceElement1}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement1, Value = config.IgnoreDamageResistanceElement1 });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement2, Value = config.IgnoreDamageResistanceElement2}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement2, Value = config.IgnoreDamageResistanceElement2 });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement3, Value = config.IgnoreDamageResistanceElement3}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement3, Value = config.IgnoreDamageResistanceElement3 });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement4, Value = config.IgnoreDamageResistanceElement4}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement4, Value = config.IgnoreDamageResistanceElement4 });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement5, Value = config.IgnoreDamageResistanceElement5}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement5, Value = config.IgnoreDamageResistanceElement5 });
baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement6, Value = config.IgnoreDamageResistanceElement6}); baseProp.Add(new() { Key = (int)EAttributeType.IgnoreDamageResistanceElement6, Value = config.IgnoreDamageResistanceElement6 });
baseProp.Add(new() { Key = (int)EAttributeType.SkillToughRatio, Value = config.SkillToughRatio}); baseProp.Add(new() { Key = (int)EAttributeType.SkillToughRatio, Value = config.SkillToughRatio });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthClimbJump, Value = config.StrengthClimbJump}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthClimbJump, Value = config.StrengthClimbJump });
baseProp.Add(new() { Key = (int)EAttributeType.StrengthGliding, Value = config.StrengthGliding}); baseProp.Add(new() { Key = (int)EAttributeType.StrengthGliding, Value = config.StrengthGliding });
baseProp.Add(new() { Key = (int)EAttributeType.Mass, Value = config.Mass}); baseProp.Add(new() { Key = (int)EAttributeType.Mass, Value = config.Mass });
baseProp.Add(new() { Key = (int)EAttributeType.BrakingFrictionFactor, Value = config.BrakingFrictionFactor}); baseProp.Add(new() { Key = (int)EAttributeType.BrakingFrictionFactor, Value = config.BrakingFrictionFactor });
baseProp.Add(new() { Key = (int)EAttributeType.GravityScale, Value = config.GravityScale}); baseProp.Add(new() { Key = (int)EAttributeType.GravityScale, Value = config.GravityScale });
baseProp.Add(new() { Key = (int)EAttributeType.SpeedRatio, Value = config.SpeedRatio}); baseProp.Add(new() { Key = (int)EAttributeType.SpeedRatio, Value = config.SpeedRatio });
baseProp.Add(new() { Key = (int)EAttributeType.DamageChangePhantom, Value = config.DamageChangePhantom}); baseProp.Add(new() { Key = (int)EAttributeType.DamageChangePhantom, Value = config.DamageChangePhantom });
baseProp.Add(new() { Key = (int)EAttributeType.AutoAttackSpeed, Value = config.AutoAttackSpeed}); baseProp.Add(new() { Key = (int)EAttributeType.AutoAttackSpeed, Value = config.AutoAttackSpeed });
baseProp.Add(new() { Key = (int)EAttributeType.CastAttackSpeed, Value = config.CastAttackSpeed}); baseProp.Add(new() { Key = (int)EAttributeType.CastAttackSpeed, Value = config.CastAttackSpeed });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp1Max, Value = config.StatusBuildUp1Max}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp1Max, Value = config.StatusBuildUp1Max });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp1, Value = config.StatusBuildUp1}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp1, Value = config.StatusBuildUp1 });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp2Max, Value = config.StatusBuildUp2Max}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp2Max, Value = config.StatusBuildUp2Max });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp2, Value = config.StatusBuildUp2}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp2, Value = config.StatusBuildUp2 });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp3Max, Value = config.StatusBuildUp3Max}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp3Max, Value = config.StatusBuildUp3Max });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp3, Value = config.StatusBuildUp3}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp3, Value = config.StatusBuildUp3 });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp4Max, Value = config.StatusBuildUp4Max}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp4Max, Value = config.StatusBuildUp4Max });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp4, Value = config.StatusBuildUp4}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp4, Value = config.StatusBuildUp4 });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp5Max, Value = config.StatusBuildUp5Max}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp5Max, Value = config.StatusBuildUp5Max });
baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp5, Value = config.StatusBuildUp5}); baseProp.Add(new() { Key = (int)EAttributeType.StatusBuildUp5, Value = config.StatusBuildUp5 });
baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTimeMax, Value = config.ParalysisTimeMax}); baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTimeMax, Value = config.ParalysisTimeMax });
baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTime, Value = config.ParalysisTime}); baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTime, Value = config.ParalysisTime });
baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTimeRecover, Value = config.ParalysisTimeRecover}); baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTimeRecover, Value = config.ParalysisTimeRecover });
return baseProp; return baseProp;
} }

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

@ -0,0 +1,39 @@
using Core.Config;
using Protocol;
namespace GameServer.Extensions.Logic;
internal static class RoleInfoExtensions
{
public static IEnumerable<GameplayAttributeData> GetAttributeList(this roleInfo role)
{
return role.BaseProp.Select(prop => new GameplayAttributeData
{
AttributeType = prop.Key,
BaseValue = prop.Value,
CurrentValue = prop.Value + ((role.AddProp.SingleOrDefault(p => p.Key == prop.Key)?.Value) ?? 0),
});
}
public static void ApplyWeaponProperties(this roleInfo role, WeaponConfig weaponConf)
{
role.AddProp.Clear();
if (weaponConf.FirstPropId != null)
{
role.AddProp.Add(new ArrayIntInt
{
Key = weaponConf.FirstPropId.Id,
Value = (int)weaponConf.FirstPropId.Value
});
}
if (weaponConf.SecondPropId != null)
{
role.AddProp.Add(new ArrayIntInt
{
Key = weaponConf.SecondPropId.Id,
Value = (int)weaponConf.SecondPropId.Value
});
}
}
}

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

@ -10,7 +10,7 @@ internal class FormationModel
public void Set(int[] roleIds) public void Set(int[] roleIds)
{ {
for (int i = 0; i < RoleIds.Length; i++) for (int i = 0; i < roleIds.Length; i++)
{ {
RoleIds[i] = roleIds[i]; RoleIds[i] = roleIds[i];
} }

View file

@ -0,0 +1,55 @@
using Protocol;
namespace GameServer.Models;
internal class InventoryModel
{
private int _itemIncrId;
public List<NormalItem> ItemList { get; } = [];
public List<WeaponItem> WeaponList { get; } = [];
public WeaponItem? GetEquippedWeapon(int roleId) => WeaponList.SingleOrDefault(weapon => weapon.RoleId == roleId);
public WeaponItem? GetWeaponById(int incrId) => WeaponList.SingleOrDefault(weapon => weapon.IncrId == incrId);
public int GetItemCount(int itemId) => ItemList.SingleOrDefault(item => item.Id == itemId)?.Count ?? 0;
public bool TryUseItem(int itemId, int amount)
{
int currentAmount = GetItemCount(itemId);
if (amount > currentAmount) return false;
AddItem(itemId, -amount);
return true;
}
public void AddItem(int itemId, int amount)
{
NormalItem? item = ItemList.SingleOrDefault(item => item.Id == itemId);
if (item != null)
{
item.Count += amount;
return;
}
ItemList.Add(new NormalItem
{
Id = itemId,
Count = amount
});
}
public WeaponItem AddNewWeapon(int weaponId)
{
WeaponItem weapon = new()
{
Id = weaponId,
IncrId = ++_itemIncrId,
WeaponLevel = 1,
WeaponResonLevel = 1
};
WeaponList.Add(weapon);
return weapon;
}
}

View file

@ -1,4 +1,5 @@
using GameServer.Controllers.Attributes; using Core.Config;
using GameServer.Controllers.Attributes;
using GameServer.Settings; using GameServer.Settings;
using GameServer.Systems.Event; using GameServer.Systems.Event;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -7,13 +8,15 @@ namespace GameServer.Models;
internal class ModelManager internal class ModelManager
{ {
private readonly IOptions<PlayerStartingValues> _playerStartingValues; private readonly IOptions<PlayerStartingValues> _playerStartingValues;
private readonly ConfigManager _configManager;
private PlayerModel? _playerModel; private PlayerModel? _playerModel;
private CreatureModel? _creatureModel; private CreatureModel? _creatureModel;
public ModelManager(IOptions<PlayerStartingValues> playerStartingValues) public ModelManager(IOptions<PlayerStartingValues> playerStartingValues, ConfigManager configManager)
{ {
_playerStartingValues = playerStartingValues; _playerStartingValues = playerStartingValues;
_configManager = configManager;
} }
[GameEvent(GameEventType.Login)] [GameEvent(GameEventType.Login)]
@ -29,6 +32,8 @@ internal class ModelManager
public CreatureModel Creature => _creatureModel ?? throw new InvalidOperationException($"Trying to access {nameof(CreatureModel)} instance before initialization!"); public CreatureModel Creature => _creatureModel ?? throw new InvalidOperationException($"Trying to access {nameof(CreatureModel)} instance before initialization!");
public RoleModel Roles { get; } = new();
public FormationModel Formation { get; } = new(); public FormationModel Formation { get; } = new();
public InventoryModel Inventory { get; } = new();
public ChatModel Chat { get; } = new(); public ChatModel Chat { get; } = new();
} }

View file

@ -1,29 +1,103 @@
using Protocol; using GameServer.Settings;
using GameServer.Settings; using Protocol;
namespace GameServer.Models; namespace GameServer.Models;
internal class PlayerModel internal class PlayerModel
{ {
private const int MaxPlayerLevel = 80;
public List<PlayerAttr> Attributes { get; } = [];
public int Id { get; private set; } public int Id { get; private set; }
public string Name { get; private set; } public int BirthDay { get; set; }
public int[] Characters { get; private set; } public int[] Characters { get; private set; }
public Vector Position { get; private set; } public Vector Position { get; private set; }
public string Name => GetStringAttribute(PlayerAttrKey.Name);
public PlayerModel() public PlayerModel()
{ {
Name = string.Empty;
Characters = []; Characters = [];
Position = new Vector(); Position = new Vector();
} }
public void SetAttribute(PlayerAttrKey key, int value)
{
SetAttribute(key, PlayerAttrType.Int32, value, null);
}
public void SetAttribute(PlayerAttrKey key, string value)
{
SetAttribute(key, PlayerAttrType.String, 0, value);
}
public int GetIntAttribute(PlayerAttrKey key)
{
return GetAttributeOfType(key, PlayerAttrType.Int32)?.Int32Value ?? 0;
}
public string GetStringAttribute(PlayerAttrKey key)
{
return GetAttributeOfType(key, PlayerAttrType.String)?.StringValue ?? string.Empty;
}
private void SetAttribute(PlayerAttrKey key, PlayerAttrType type, int intValue, string? stringValue)
{
PlayerAttr? attr = GetAttributeOfType(key, type);
if (attr == null)
{
attr = new PlayerAttr
{
Key = (int)key,
ValueType = (int)type,
};
Attributes.Add(attr);
}
switch (type)
{
case PlayerAttrType.Int32:
attr.Int32Value = intValue; break;
case PlayerAttrType.String:
attr.StringValue = stringValue; break;
}
}
private PlayerAttr? GetAttributeOfType(PlayerAttrKey key, PlayerAttrType type)
{
PlayerAttr? attr = Attributes.SingleOrDefault(attr => attr.Key == (int)key);
if (attr != null)
{
if (attr.ValueType != (int)type)
throw new ArgumentException($"PlayerAttr type mismatch! Key: {key}, type: {(PlayerAttrType)attr.ValueType}, argument type: {type}");
}
return attr;
}
public void LevelUp()
{
int level = GetIntAttribute(PlayerAttrKey.Level);
if (level == MaxPlayerLevel) return;
SetAttribute(PlayerAttrKey.Level, level + 1);
}
public static PlayerModel CreateDefaultPlayer(PlayerStartingValues startingValues) public static PlayerModel CreateDefaultPlayer(PlayerStartingValues startingValues)
{ {
return new PlayerModel PlayerModel playerModel = new()
{ {
Id = 1337, Id = 1337,
Name = startingValues.Name,
Characters = startingValues.Characters, Characters = startingValues.Characters,
Position = startingValues.Position.Clone() Position = startingValues.Position.Clone()
}; };
playerModel.SetAttribute(PlayerAttrKey.Name, startingValues.Name);
playerModel.SetAttribute(PlayerAttrKey.Level, startingValues.PlayerLevel);
playerModel.SetAttribute(PlayerAttrKey.HeadPhoto, startingValues.HeadPhoto);
playerModel.SetAttribute(PlayerAttrKey.HeadFrame, startingValues.HeadFrame);
return playerModel;
} }
} }

View file

@ -0,0 +1,24 @@
using Protocol;
namespace GameServer.Models;
internal class RoleModel
{
public List<roleInfo> Roles { get; } = [];
public roleInfo Create(int id)
{
roleInfo info = new()
{
RoleId = id,
Level = 1,
};
Roles.Add(info);
return info;
}
public roleInfo? GetRoleById(int roleId)
{
return Roles.SingleOrDefault(role => role.RoleId == roleId);
}
}

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

@ -2,4 +2,5 @@
internal class GameplayFeatureSettings internal class GameplayFeatureSettings
{ {
public bool TeleportByMapMark { get; set; } public bool TeleportByMapMark { get; set; }
public bool UnlimitedEnergy { get; set; }
} }

View file

@ -4,6 +4,9 @@ namespace GameServer.Settings;
internal class PlayerStartingValues internal class PlayerStartingValues
{ {
public required string Name { get; set; } public required string Name { get; set; }
public required int PlayerLevel { get; set; }
public required int HeadPhoto { get; set; }
public required int HeadFrame { get; set; }
public required int[] Characters { get; set; } public required int[] Characters { get; set; }
public required Vector Position { get; set; } public required Vector Position { get; set; }
} }

View file

@ -12,6 +12,14 @@ internal class EntityAttributeComponent : EntityComponentBase
_gameplayAttributes = []; _gameplayAttributes = [];
} }
public void SetAll(IEnumerable<GameplayAttributeData> attributes)
{
foreach (GameplayAttributeData attr in attributes)
{
SetAttribute((EAttributeType)attr.AttributeType, attr.CurrentValue, attr.BaseValue);
}
}
public void SetAttribute(EAttributeType type, int currentValue, int baseValue) public void SetAttribute(EAttributeType type, int currentValue, int baseValue)
{ {
if (!_gameplayAttributes.TryGetValue(type, out GameplayAttributeData? attribute)) if (!_gameplayAttributes.TryGetValue(type, out GameplayAttributeData? attribute))

View file

@ -4,6 +4,8 @@ namespace GameServer.Systems.Entity.Component;
internal class EntityConcomitantsComponent : EntityComponentBase internal class EntityConcomitantsComponent : EntityComponentBase
{ {
public List<long> CustomEntityIds { get; } public List<long> CustomEntityIds { get; }
public long PhantomRoleEntityId { get; set; }
public long VisionEntityId { get; set; }
public EntityConcomitantsComponent() public EntityConcomitantsComponent()
{ {
@ -19,6 +21,10 @@ internal class EntityConcomitantsComponent : EntityComponentBase
EntityComponentPb pb = new() EntityComponentPb pb = new()
{ {
ConcomitantsComponentPb = new() ConcomitantsComponentPb = new()
{
PhantomRoleEid = PhantomRoleEntityId,
VisionEntityId = VisionEntityId
}
}; };
pb.ConcomitantsComponentPb.CustomEntityIds.AddRange(CustomEntityIds); pb.ConcomitantsComponentPb.CustomEntityIds.AddRange(CustomEntityIds);

View file

@ -0,0 +1,20 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityFightBuffComponent : EntityComponentBase
{
public List<FightBuffInformation> BuffInfoList { get; } = [];
public override EntityComponentType Type => EntityComponentType.FightBuff;
public override EntityComponentPb Pb => new()
{
FightBuffComponent = new()
{
FightBuffInfos =
{
BuffInfoList
}
}
};
}

View file

@ -19,7 +19,7 @@ internal class EntitySummonerComponent : EntityComponentBase
Type = (int)SummonType, Type = (int)SummonType,
SummonerId = SummonerId, SummonerId = SummonerId,
PlayerId = PlayerId, PlayerId = PlayerId,
SummonSkillId = SummonSkillId SummonSkillId = SummonSkillId,
} }
}; };
} }

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,10 +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 EntityBase(long id) public bool IsConcomitant => ComponentSystem.TryGet<EntitySummonerComponent>(out _);
protected IGameActionListener ActionListener { get; }
public EntityBase(long id, IGameActionListener listener)
{ {
Id = id; Id = id;
@ -23,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()
@ -30,6 +37,7 @@ internal abstract class EntityBase
State = EntityState.Born; State = EntityState.Born;
_ = ComponentSystem.Create<EntityLogicStateComponent>(); _ = ComponentSystem.Create<EntityLogicStateComponent>();
_ = ComponentSystem.Create<EntityFightBuffComponent>();
} }
public virtual void Activate() public virtual void Activate()
@ -37,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;
@ -37,15 +38,37 @@ internal class PlayerEntity : EntityBase
{ {
base.OnCreate(); base.OnCreate();
// Should be created immediately
EntityConcomitantsComponent concomitantsComponent = ComponentSystem.Create<EntityConcomitantsComponent>();
concomitantsComponent.CustomEntityIds.Add(Id);
EntityVisionSkillComponent visionSkillComponent = ComponentSystem.Create<EntityVisionSkillComponent>(); EntityVisionSkillComponent visionSkillComponent = ComponentSystem.Create<EntityVisionSkillComponent>();
visionSkillComponent.SetExploreTool(1001); visionSkillComponent.SetExploreTool(1001);
_ = ComponentSystem.Create<EntityEquipComponent>(); _ = ComponentSystem.Create<EntityEquipComponent>();
_ = ComponentSystem.Create<EntityAttributeComponent>(); _ = ComponentSystem.Create<EntityAttributeComponent>();
// TODO: temporary solution to enable glider and wall run, should implement proper buff management.
EntityFightBuffComponent fightBuffComponent = ComponentSystem.Get<EntityFightBuffComponent>();
fightBuffComponent.BuffInfoList.Add(new FightBuffInformation
{
BuffId = 3004,
EntityId = Id,
InstigatorId = Id,
IsActive = true,
Duration = -1,
LeftDuration = -1,
Level = 1,
StackCount = 1
});
fightBuffComponent.BuffInfoList.Add(new FightBuffInformation
{
BuffId = 3003,
EntityId = Id,
InstigatorId = Id,
IsActive = true,
Duration = -1,
LeftDuration = -1,
Level = 1,
StackCount = 1
});
} }
public override void Activate() public override void Activate()
@ -56,7 +79,7 @@ internal class PlayerEntity : EntityBase
public override EEntityType Type => EEntityType.Player; public override EEntityType Type => EEntityType.Player;
public override EntityConfigType ConfigType => EntityConfigType.Character; public override EntityConfigType ConfigType => EntityConfigType.Character;
public override bool IsVisible => IsCurrentRole; public override bool IsVisible => IsCurrentRole || IsConcomitant;
public override EntityPb Pb public override EntityPb Pb
{ {

View file

@ -6,6 +6,11 @@ internal enum GameEventType
PushDataDone, PushDataDone,
// Actions // Actions
PlayerPositionChanged,
FormationUpdated, FormationUpdated,
VisionSkillChanged VisionSkillChanged,
// Debug
DebugUnlockAllRoles,
DebugUnlockAllItems
} }

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

@ -1,14 +1,18 @@
{ {
"StartingValues": { "StartingValues": {
"Name": "ReversedRooms", "Name": "ReversedRooms",
"PlayerLevel": 8,
"HeadPhoto": 1402,
"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": false
} }
} }