Merge branch 'master' into patch-1

This commit is contained in:
thexeondev 2024-02-22 20:58:36 +03:00 committed by GitHub
commit e0cd311bfc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
99 changed files with 1576671 additions and 357 deletions

View file

@ -0,0 +1,12 @@
namespace Core.Config.Attributes;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
internal class ConfigCollectionAttribute : Attribute
{
public string Path { get; }
public ConfigCollectionAttribute(string path)
{
Path = path;
}
}

View file

@ -0,0 +1,148 @@
using Core.Config.Attributes;
namespace Core.Config;
[ConfigCollection("property/baseproperty.json")]
public class BasePropertyConfig : IConfig
{
public ConfigType Type => ConfigType.BaseProperty;
public int Identifier => Id;
public int Id { get; set; }
public int Lv { get; set; }
public int LifeMax { get; set; }
public int Life { get; set; }
public int Sheild { get; set; }
public int SheildDamageChange { get; set; }
public int SheildDamageReduce { get; set; }
public int Atk { get; set; }
public int Crit { get; set; }
public int CritDamage { get; set; }
public int Def { get; set; }
public int EnergyEfficiency { get; set; }
public int CdReduse { get; set; }
public int ReactionEfficiency { get; set; }
public int DamageChangeNormalSkill { get; set; }
public int DamageChange { get; set; }
public int DamageReduce { get; set; }
public int DamageChangeAuto { get; set; }
public int DamageChangeCast { get; set; }
public int DamageChangeUltra { get; set; }
public int DamageChangeQte { get; set; }
public int DamageChangePhys { get; set; }
public int DamageChangeElement1 { get; set; }
public int DamageChangeElement2 { get; set; }
public int DamageChangeElement3 { get; set; }
public int DamageChangeElement4 { get; set; }
public int DamageChangeElement5 { get; set; }
public int DamageChangeElement6 { get; set; }
public int DamageResistancePhys { get; set; }
public int DamageResistanceElement1 { get; set; }
public int DamageResistanceElement2 { get; set; }
public int DamageResistanceElement3 { get; set; }
public int DamageResistanceElement4 { get; set; }
public int DamageResistanceElement5 { get; set; }
public int DamageResistanceElement6 { get; set; }
public int HealChange { get; set; }
public int HealedChange { get; set; }
public int DamageReducePhys { get; set; }
public int DamageReduceElement1 { get; set; }
public int DamageReduceElement2 { get; set; }
public int DamageReduceElement3 { get; set; }
public int DamageReduceElement4 { get; set; }
public int DamageReduceElement5 { get; set; }
public int DamageReduceElement6 { get; set; }
public int ReactionChange1 { get; set; }
public int ReactionChange2 { get; set; }
public int ReactionChange3 { get; set; }
public int ReactionChange4 { get; set; }
public int ReactionChange5 { get; set; }
public int ReactionChange6 { get; set; }
public int ReactionChange7 { get; set; }
public int ReactionChange8 { get; set; }
public int ReactionChange9 { get; set; }
public int ReactionChange10 { get; set; }
public int ReactionChange11 { get; set; }
public int ReactionChange12 { get; set; }
public int ReactionChange13 { get; set; }
public int ReactionChange14 { get; set; }
public int ReactionChange15 { get; set; }
public int EnergyMax { get; set; }
public int Energy { get; set; }
public int SpecialEnergy1Max { get; set; }
public int SpecialEnergy1 { get; set; }
public int SpecialEnergy2Max { get; set; }
public int SpecialEnergy2 { get; set; }
public int SpecialEnergy3Max { get; set; }
public int SpecialEnergy3 { get; set; }
public int SpecialEnergy4Max { get; set; }
public int SpecialEnergy4 { get; set; }
public int StrengthMax { get; set; }
public int Strength { get; set; }
public int StrengthRecover { get; set; }
public int StrengthPunishTime { get; set; }
public int StrengthRun { get; set; }
public int StrengthSwim { get; set; }
public int StrengthFastSwim { get; set; }
public int StrengthClimb { get; set; }
public int StrengthFastClimb { get; set; }
public int HardnessMax { get; set; }
public int Hardness { get; set; }
public int HardnessRecover { get; set; }
public int HardnessPunishTime { get; set; }
public int HardnessChange { get; set; }
public int HardnessReduce { get; set; }
public int RageMax { get; set; }
public int Rage { get; set; }
public int RageRecover { get; set; }
public int RagePunishTime { get; set; }
public int RageChange { get; set; }
public int RageReduce { get; set; }
public int ToughMax { get; set; }
public int Tough { get; set; }
public int ToughRecover { get; set; }
public int ToughChange { get; set; }
public int ToughReduce { get; set; }
public int ToughRecoverDelayTime { get; set; }
public int ElementPower1 { get; set; }
public int ElementPower2 { get; set; }
public int ElementPower3 { get; set; }
public int ElementPower4 { get; set; }
public int ElementPower5 { get; set; }
public int ElementPower6 { get; set; }
public int SpecialDamageChange { get; set; }
public int StrengthFastClimbCost { get; set; }
public int ElementPropertyType { get; set; }
public int WeakTime { get; set; }
public int IgnoreDefRate { get; set; }
public int IgnoreDamageResistancePhys { get; set; }
public int IgnoreDamageResistanceElement1 { get; set; }
public int IgnoreDamageResistanceElement2 { get; set; }
public int IgnoreDamageResistanceElement3 { get; set; }
public int IgnoreDamageResistanceElement4 { get; set; }
public int IgnoreDamageResistanceElement5 { get; set; }
public int IgnoreDamageResistanceElement6 { get; set; }
public int SkillToughRatio { get; set; }
public int StrengthClimbJump { get; set; }
public int StrengthGliding { get; set; }
public int Mass { get; set; }
public int BrakingFrictionFactor { get; set; }
public int GravityScale { get; set; }
public int SpeedRatio { get; set; }
public int DamageChangePhantom { get; set; }
public int AutoAttackSpeed { get; set; }
public int CastAttackSpeed { get; set; }
public int StatusBuildUp1Max { get; set; }
public int StatusBuildUp1 { get; set; }
public int StatusBuildUp2Max { get; set; }
public int StatusBuildUp2 { get; set; }
public int StatusBuildUp3Max { get; set; }
public int StatusBuildUp3 { get; set; }
public int StatusBuildUp4Max { get; set; }
public int StatusBuildUp4 { get; set; }
public int StatusBuildUp5Max { get; set; }
public int StatusBuildUp5 { get; set; }
public int ParalysisTimeMax { get; set; }
public int ParalysisTime { get; set; }
public int ParalysisTimeRecover { get; set; }
}

View file

@ -0,0 +1,17 @@
using Core.Config.Attributes;
namespace Core.Config;
[ConfigCollection("blueprint/blueprintconfig.json")]
public class BlueprintConfig : IConfig
{
public ConfigType Type => ConfigType.Blueprint;
public int Identifier => Id;
public int Id { get; set; }
public string BlueprintType { get; set; } = string.Empty;
public string EntityType { get; set; } = string.Empty;
public string EntityLogic { get; set; } = string.Empty;
public int ModelId { get; set; }
public int HalfHeight { get; set; }
}

View file

@ -0,0 +1,49 @@
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
namespace Core.Config;
public class ConfigCollection
{
private readonly ImmutableDictionary<int, IConfig> _configs;
public ConfigCollection(JsonDocument json, Type type)
{
_configs = LoadConfigs(json, type);
}
public int Count => _configs.Count;
public TConfig At<TConfig>(int index) where TConfig : IConfig
{
return (TConfig)_configs.Values.ElementAt(index);
}
public IEnumerable<TConfig> Enumerate<TConfig>() where TConfig : IConfig
{
return _configs.Values.Cast<TConfig>();
}
public bool TryGet<TConfig>(int identifier, [NotNullWhen(true)] out TConfig? config) where TConfig : IConfig
{
bool result = _configs.TryGetValue(identifier, out IConfig? cfg);
config = (TConfig?)cfg;
return result;
}
private static ImmutableDictionary<int, IConfig> LoadConfigs(JsonDocument json, Type type)
{
var builder = ImmutableDictionary.CreateBuilder<int, IConfig>();
foreach (JsonElement element in json.RootElement.EnumerateArray())
{
if (element.ValueKind != JsonValueKind.Object) throw new InvalidDataException($"LoadConfigs: expected array of {JsonValueKind.Object}, got array of {element.ValueKind}");
IConfig configItem = (element.Deserialize(type) as IConfig)!;
builder.Add(configItem.Identifier, configItem);
}
return builder.ToImmutable();
}
}

View file

@ -0,0 +1,62 @@
using System.Collections.Immutable;
using System.Reflection;
using Core.Config.Attributes;
using Core.Resources;
using Microsoft.Extensions.Logging;
namespace Core.Config;
public class ConfigManager
{
private readonly ImmutableDictionary<ConfigType, ConfigCollection> _collectionsByEnum;
private readonly ImmutableDictionary<Type, ConfigCollection> _collectionsByType;
public ConfigManager(ILogger<ConfigManager> logger, IResourceProvider resourceProvider)
{
(_collectionsByEnum, _collectionsByType) = LoadConfigCollections(resourceProvider);
logger.LogInformation("Loaded {count} config collections", _collectionsByEnum.Count);
}
public IEnumerable<TConfig> Enumerate<TConfig>() where TConfig : IConfig
{
return GetCollection<TConfig>().Enumerate<TConfig>();
}
public ConfigCollection GetCollection<TConfigType>() where TConfigType : IConfig
{
return _collectionsByType[typeof(TConfigType)];
}
public ConfigCollection GetCollection(ConfigType type)
{
return _collectionsByEnum[type];
}
public TConfig? GetConfig<TConfig>(int id) where TConfig : IConfig
{
if (_collectionsByType[typeof(TConfig)].TryGet(id, out TConfig? config))
return config;
return default;
}
private static (ImmutableDictionary<ConfigType, ConfigCollection>, ImmutableDictionary<Type, ConfigCollection>) LoadConfigCollections(IResourceProvider resourceProvider)
{
var builderByEnum = ImmutableDictionary.CreateBuilder<ConfigType, ConfigCollection>();
var builderByType = ImmutableDictionary.CreateBuilder<Type, ConfigCollection>();
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => type.IsAssignableTo(typeof(IConfig)) && !type.IsAbstract);
foreach (Type type in types)
{
ConfigCollectionAttribute? attribute = type.GetCustomAttribute<ConfigCollectionAttribute>();
if (attribute == null) continue;
ConfigCollection collection = new(resourceProvider.GetJsonResource("data/config/" + attribute.Path), type);
builderByEnum.Add(collection.At<IConfig>(0).Type, collection);
builderByType.Add(collection.At<IConfig>(0).GetType(), collection);
}
return (builderByEnum.ToImmutable(), builderByType.ToImmutable());
}
}

View file

@ -0,0 +1,9 @@
namespace Core.Config;
public enum ConfigType
{
RoleInfo,
Weapon,
BaseProperty,
LevelEntity,
Blueprint
}

6
Core/Config/IConfig.cs Normal file
View file

@ -0,0 +1,6 @@
namespace Core.Config;
public interface IConfig
{
ConfigType Type { get; }
int Identifier { get; }
}

View file

@ -0,0 +1,27 @@
using Core.Config.Attributes;
namespace Core.Config;
[ConfigCollection("level_entity/levelentityconfig.json")]
public class LevelEntityConfig : IConfig
{
public ConfigType Type => ConfigType.LevelEntity;
public int Identifier => Id;
public int Id { get; set; }
public int MapId { get; set; }
public int EntityId { get; set; }
public string BlueprintType { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public bool InSleep { get; set; }
public bool IsHidden { get; set; }
public int AreaId { get; set; }
public Transform[] Transform { get; set; } = [];
}
public class Transform
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}

View file

@ -0,0 +1,69 @@
using Core.Config.Attributes;
namespace Core.Config;
[ConfigCollection("role/roleinfo.json")]
public class RoleInfoConfig : IConfig
{
public ConfigType Type => ConfigType.RoleInfo;
public int Identifier => Id;
public int Id { get; set; }
public int QualityId { get; set; }
public int RoleType { get; set; }
public bool IsTrial { get; set; }
public string Name { get; set; } = string.Empty;
public string NickName { get; set; } = string.Empty;
public string Introduction { get; set; } = string.Empty;
public int ParentId { get; set; }
public int Priority { get; set; }
public int PropertyId { get; set; }
public List<int> ShowProperty { get; set; } = [];
public int ElementId { get; set; }
public string RoleHeadIconLarge { get; set; } = string.Empty;
public string RoleHeadIconBig { get; set; } = string.Empty;
public string Card { get; set; } = string.Empty;
public string RoleHeadIcon { get; set; } = string.Empty;
public string FormationRoleCard { get; set; } = string.Empty;
public string RoleStand { get; set; } = string.Empty;
public int MeshId { get; set; }
public int UiMeshId { get; set; }
public string RoleBody { get; set; } = string.Empty;
public int BreachModel { get; set; }
public int SpecialEnergyBarId { get; set; }
public string CameraConfig { get; set; } = string.Empty;
public int CameraFloatHeight { get; set; }
public int EntityProperty { get; set; }
public int MaxLevel { get; set; }
public int LevelConsumeId { get; set; }
public int BreachId { get; set; }
public int SkillId { get; set; }
public int SkillTreeGroupId { get; set; }
public int ResonanceId { get; set; }
public int ResonantChainGroupId { get; set; }
public bool IsShow { get; set; }
public int InitWeaponItemId { get; set; }
public int WeaponType { get; set; }
public string SkillDAPath { get; set; } = string.Empty;
public string SkillLockDAPath { get; set; } = string.Empty;
public string UiScenePerformanceABP { get; set; } = string.Empty;
public int LockOnDefaultId { get; set; }
public int LockOnLookOnId { get; set; }
public string SkillEffectDA { get; set; } = string.Empty;
public string FootStepState { get; set; } = string.Empty;
public int PartyId { get; set; }
public string AttributesDescription { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
public int ItemQualityId { get; set; }
public string ObtainedShowDescription { get; set; } = string.Empty;
public int NumLimit { get; set; }
public bool ShowInBag { get; set; }
public List<float> WeaponScale { get; set; } = [];
public bool Intervene { get; set; }
public string CharacterVoice { get; set; } = string.Empty;
public int TrialRole { get; set; }
public bool IsAim { get; set; }
public int RoleGuide { get; set; }
public int RedDotDisableRule { get; set; }
}

View file

@ -0,0 +1,7 @@
namespace Core.Config.Types;
public class PropConfig
{
public int Id { get; set; }
public float Value { get; set; }
public bool IsRatio { get; set; }
}

View file

@ -0,0 +1,47 @@
using Core.Config.Attributes;
using Core.Config.Types;
namespace Core.Config;
[ConfigCollection("weapon/weaponconf.json")]
public class WeaponConfig : IConfig
{
public ConfigType Type => ConfigType.Weapon;
public int Identifier => ItemId;
public int ItemId { get; set; }
public string WeaponName { get; set; } = string.Empty;
public int QualityId { get; set; }
public int WeaponType { get; set; }
public int ModelId { get; set; }
public int TransformId { get; set; }
public List<int> Models { get; set; } = [];
public int ResonLevelLimit { get; set; }
public PropConfig? FirstPropId { get; set; }
public int FirstCurve { get; set; }
public PropConfig? SecondPropId { get; set; }
public int SecondCurve { get; set; }
public int ResonId { get; set; }
public int LevelId { get; set; }
public int BreachId { get; set; }
public string Desc { get; set; } = string.Empty;
public string TypeDescription { get; set; } = string.Empty;
public string AttributesDescription { get; set; } = string.Empty;
public string BgDescription { get; set; } = string.Empty;
public string Icon { get; set; } = string.Empty;
public string IconMiddle { get; set; } = string.Empty;
public string IconSmall { get; set; } = string.Empty;
public string Mesh { get; set; } = string.Empty;
public int MaxCapcity { get; set; }
public List<object> ItemAccess { get; set; } = [];
public int ObtainedShow { get; set; }
public string ObtainedShowDescription { get; set; } = string.Empty;
public int NumLimit { get; set; }
public bool ShowInBag { get; set; }
public int SortIndex { get; set; }
public string ResonanceIcon { get; set; } = string.Empty;
public int HiddenTime { get; set; }
public bool Destructible { get; set; }
public int RedDotDisableRule { get; set; }
}

29
Core/Core.csproj Normal file
View file

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Content Include="data\config\*\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Remove="data\config\blueprint\blueprintconfig.json" />
<None Remove="data\config\property\baseproperty.json" />
<None Remove="data\config\weapon\weaponconf.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="data\config\level_entity\" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,11 @@
using Core.Resources;
using Microsoft.Extensions.DependencyInjection;
namespace Core.Extensions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection UseLocalResources(this IServiceCollection services)
{
return services.AddSingleton<IResourceProvider, LocalResourceProvider>();
}
}

View file

@ -0,0 +1,7 @@
using System.Text.Json;
namespace Core.Resources;
public interface IResourceProvider
{
JsonDocument GetJsonResource(string path);
}

View file

@ -0,0 +1,11 @@
using System.Text.Json;
namespace Core.Resources;
internal class LocalResourceProvider : IResourceProvider
{
public JsonDocument GetJsonResource(string path)
{
using FileStream fileStream = File.Open(path, FileMode.Open, FileAccess.Read);
return JsonDocument.Parse(fileStream);
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -12,5 +12,5 @@ internal class AchievementController : Controller
} }
[NetEvent(MessageId.AchievementInfoRequest)] [NetEvent(MessageId.AchievementInfoRequest)]
public ResponseMessage OnAchievementInfoRequest() => Response(MessageId.AchievementInfoResponse, new AchievementInfoResponse()); public RpcResult OnAchievementInfoRequest() => Response(MessageId.AchievementInfoResponse, new AchievementInfoResponse());
} }

View file

@ -0,0 +1,12 @@
namespace GameServer.Controllers.Attributes;
[AttributeUsage(AttributeTargets.Method)]
internal class ChatCommandAttribute : Attribute
{
public string Command { get; }
public ChatCommandAttribute(string command)
{
Command = command;
}
}

View file

@ -0,0 +1,12 @@
namespace GameServer.Controllers.Attributes;
[AttributeUsage(AttributeTargets.Class)]
internal class ChatCommandCategoryAttribute : Attribute
{
public string Category { get; }
public ChatCommandCategoryAttribute(string category)
{
Category = category;
}
}

View file

@ -0,0 +1,12 @@
namespace GameServer.Controllers.Attributes;
[AttributeUsage(AttributeTargets.Method)]
internal class ChatCommandDescAttribute : Attribute
{
public string Description { get; }
public ChatCommandDescAttribute(string description)
{
Description = description;
}
}

View file

@ -0,0 +1,14 @@
using Protocol;
namespace GameServer.Controllers.Attributes;
[AttributeUsage(AttributeTargets.Method)]
internal class CombatRequestAttribute : Attribute
{
public CombatRequestData.MessageOneofCase MessageCase { get; }
public CombatRequestAttribute(CombatRequestData.MessageOneofCase messageCase)
{
MessageCase = messageCase;
}
}

View file

@ -0,0 +1,77 @@
using System.Collections.Immutable;
using System.Linq.Expressions;
using System.Reflection;
using GameServer.Controllers.Attributes;
using Microsoft.Extensions.DependencyInjection;
namespace GameServer.Controllers.ChatCommands;
internal class ChatCommandManager
{
private delegate Task ChatCommandDelegate(IServiceProvider serviceProvider, string[] args);
private static readonly ImmutableDictionary<string, ImmutableDictionary<string, ChatCommandDelegate>> s_commandCategories;
private static readonly ImmutableArray<string> s_commandDescriptions;
private readonly IServiceProvider _serviceProvider;
static ChatCommandManager()
{
(s_commandCategories, s_commandDescriptions) = RegisterCommands();
}
public static IEnumerable<string> CommandDescriptions => s_commandDescriptions;
public ChatCommandManager(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task InvokeCommandAsync(string category, string command, string[] args)
{
if (s_commandCategories.TryGetValue(category, out var commands))
{
if (commands.TryGetValue(command, out ChatCommandDelegate? commandDelegate))
await commandDelegate(_serviceProvider, args);
}
}
private static (ImmutableDictionary<string, ImmutableDictionary<string, ChatCommandDelegate>>, ImmutableArray<string>) RegisterCommands()
{
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => type.GetCustomAttribute<ChatCommandCategoryAttribute>() != null);
MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod(nameof(ServiceProviderServiceExtensions.GetRequiredService), [typeof(IServiceProvider)])!;
var categories = ImmutableDictionary.CreateBuilder<string, ImmutableDictionary<string, ChatCommandDelegate>>();
var descriptions = ImmutableArray.CreateBuilder<string>();
foreach (Type type in types)
{
var commands = ImmutableDictionary.CreateBuilder<string, ChatCommandDelegate>();
foreach (MethodInfo method in type.GetMethods())
{
ChatCommandAttribute? cmdAttribute = method.GetCustomAttribute<ChatCommandAttribute>();
if (cmdAttribute == null) continue;
ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider));
ParameterExpression argsParam = Expression.Parameter(typeof(string[]));
MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam);
Expression handlerCall = Expression.Call(getServiceCall, method, argsParam);
if (method.ReturnType == typeof(void)) // Allow non-async methods as well
handlerCall = Expression.Block(handlerCall, Expression.Constant(Task.CompletedTask));
Expression<ChatCommandDelegate> lambda = Expression.Lambda<ChatCommandDelegate>(handlerCall, serviceProviderParam, argsParam);
commands.Add(cmdAttribute.Command, lambda.Compile());
ChatCommandDescAttribute? desc = method.GetCustomAttribute<ChatCommandDescAttribute>();
if (desc != null)
descriptions.Add(desc.Description);
}
ChatCommandCategoryAttribute categoryAttribute = type.GetCustomAttribute<ChatCommandCategoryAttribute>()!;
categories.Add(categoryAttribute.Category, commands.ToImmutable());
}
return (categories.ToImmutable(), descriptions.ToImmutable());
}
}

View file

@ -0,0 +1,66 @@
using Core.Config;
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("spawn")]
internal class ChatSpawnCommandHandler
{
private readonly ChatRoom _helperRoom;
private readonly EntitySystem _entitySystem;
private readonly EntityFactory _entityFactory;
private readonly PlayerSession _session;
private readonly ConfigManager _configManager;
private readonly CreatureController _creatureController;
public ChatSpawnCommandHandler(ModelManager modelManager, EntitySystem entitySystem, EntityFactory entityFactory, PlayerSession session, ConfigManager configManager, CreatureController creatureController)
{
_helperRoom = modelManager.Chat.GetChatRoom(1338);
_entitySystem = entitySystem;
_entityFactory = entityFactory;
_session = session;
_configManager = configManager;
_creatureController = creatureController;
}
[ChatCommand("monster")]
[ChatCommandDesc("/spawn monster [id] [x] [y] [z] - spawns monster with specified id and coordinates")]
public async Task OnSpawnMonsterCommand(string[] args)
{
if (args.Length != 4 ||
!(int.TryParse(args[0], out int levelEntityId) &&
int.TryParse(args[1], out int x) &&
int.TryParse(args[2], out int y) &&
int.TryParse(args[3], out int z)))
{
_helperRoom.AddMessage(1338, 0, "Usage: /spawn monster [id] [x] [y] [z]");
return;
}
MonsterEntity monster = _entityFactory.CreateMonster(levelEntityId);
monster.Pos = new()
{
X = x,
Y = y,
Z = z
};
_entitySystem.Create(monster);
monster.InitProps(_configManager.GetConfig<BasePropertyConfig>(600000100)!); // TODO: monster property config
await _session.Push(MessageId.EntityAddNotify, new EntityAddNotify
{
IsAdd = true,
EntityPbs = { monster.Pb }
});
await _creatureController.UpdateAiHate();
_helperRoom.AddMessage(1338, 0, $"Successfully spawned monster with id {levelEntityId} at ({x}, {y}, {z})");
}
}

View file

@ -0,0 +1,93 @@
using System.Text;
using System.Text.RegularExpressions;
using GameServer.Controllers.Attributes;
using GameServer.Controllers.ChatCommands;
using GameServer.Models;
using GameServer.Models.Chat;
using GameServer.Network;
using GameServer.Network.Messages;
using Protocol;
namespace GameServer.Controllers;
internal partial class ChatController : Controller
{
private readonly ModelManager _modelManager;
public ChatController(PlayerSession session, ModelManager modelManager) : base(session)
{
_modelManager = modelManager;
}
[NetEvent(MessageId.PrivateChatDataRequest)]
public async Task<RpcResult> OnPrivateChatDataRequest()
{
if (!_modelManager.Chat.AllChatRooms.Any())
{
ChatRoom chatRoom = _modelManager.Chat.GetChatRoom(1338); // Reversed Helper
chatRoom.AddMessage(1338, (int)ChatContentType.Text, BuildWelcomeMessage());
}
await PushPrivateChatHistory();
return Response(MessageId.PrivateChatDataResponse, new PrivateChatDataResponse()); // Response doesn't contain any useful info, everything is in notifies.
}
[NetEvent(MessageId.PrivateChatRequest)]
public async Task<RpcResult> OnPrivateChatRequest(PrivateChatRequest request, ChatCommandManager chatCommandManager)
{
ChatRoom chatRoom = _modelManager.Chat.GetChatRoom(1338);
chatRoom.AddMessage(_modelManager.Player.Id, request.ChatContentType, request.Content);
if (!request.Content.StartsWith('/'))
{
chatRoom.AddMessage(1338, 0, "huh?");
}
else
{
string content = MultipleSpacesRegex().Replace(request.Content, " ");
string[] split = content[1..].Split(' ');
if (split.Length >= 2)
{
await chatCommandManager.InvokeCommandAsync(split[0], split[1], split[2..]);
}
}
await PushPrivateChatHistory();
return Response(MessageId.PrivateChatResponse, new PrivateChatResponse());
}
[NetEvent(MessageId.PrivateChatOperateRequest)]
public RpcResult OnPrivateChatOperateRequest() => Response(MessageId.PrivateChatOperateResponse, new PrivateChatOperateResponse());
private async Task PushPrivateChatHistory()
{
await Session.Push(MessageId.PrivateChatHistoryNotify, new PrivateChatHistoryNotify
{
AllChats =
{
_modelManager.Chat.AllChatRooms
.Select(chatRoom => new PrivateChatHistoryContentProto
{
TargetUid = chatRoom.TargetUid,
Chats = { chatRoom.ChatHistory }
})
}
});
}
private static string BuildWelcomeMessage()
{
StringBuilder builder = new();
builder.AppendLine("Welcome to ReversedRooms WutheringWaves private server!\nTo get support, join:\ndiscord.gg/reversedrooms");
builder.AppendLine("List of all commands:");
foreach (string description in ChatCommandManager.CommandDescriptions)
{
builder.AppendLine(description);
}
return builder.ToString();
}
[GeneratedRegex(@"\s+")]
private static partial Regex MultipleSpacesRegex();
}

View file

@ -0,0 +1,306 @@
using System.Collections.Immutable;
using System.Linq.Expressions;
using System.Reflection;
using System.Security.Cryptography;
using GameServer.Controllers.Attributes;
using GameServer.Network;
using GameServer.Systems.Entity;
using GameServer.Systems.Entity.Component;
using Microsoft.Extensions.Logging;
using Protocol;
namespace GameServer.Controllers.Combat;
internal class CombatManager
{
private delegate Task<CombatResponseData> CombatRequestHandler(CombatManager combatManager, CombatRequestContext context);
private static readonly ImmutableDictionary<CombatRequestData.MessageOneofCase, CombatRequestHandler> s_requestHandlers;
private readonly ILogger _logger;
private readonly EntitySystem _entitySystem;
private readonly PlayerSession _session;
private readonly CreatureController _creatureController;
static CombatManager()
{
s_requestHandlers = MapRequestHandlers();
}
public CombatManager(ILogger<CombatManager> logger, EntitySystem entitySystem, PlayerSession session, CreatureController creatureController)
{
_logger = logger;
_entitySystem = entitySystem;
_session = session;
_creatureController = creatureController;
}
[CombatRequest(CombatRequestData.MessageOneofCase.DamageExecuteRequest)]
public async Task<CombatResponseData> OnDamageExecuteRequest(CombatRequestContext context)
{
DamageExecuteRequest request = context.Request.DamageExecuteRequest;
EntityBase? entity = _entitySystem.Get<EntityBase>(request.TargetEntityId);
if (entity == null)
{
return new CombatResponseData
{
RequestId = context.Request.RequestId,
CombatCommon = context.Request.CombatCommon,
DamageExecuteResponse = new()
{
ErrorCode = (int)ErrorCode.ErrThrowDamageReqEntityIsAlreadyDead
}
};
}
EntityAttributeComponent attr = entity.ComponentSystem.Get<EntityAttributeComponent>();
attr.SetAttribute(EAttributeType.Life, (int)request.DamageId); // Pakchunk patch! (cur hp instead of damageid)
if (request.DamageId <= 0 && entity.Type != EEntityType.Player) // Player death not implemented
{
_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
{
RequestId = context.Request.RequestId,
CombatCommon = context.Request.CombatCommon,
DamageExecuteResponse = new()
{
AttackerEntityId = request.AttackerEntityId,
TargetEntityId = request.TargetEntityId,
ShieldCoverDamage = 0,
Damage = (int)request.DamageId,
PartId = request.PartId
}
};
}
[CombatRequest(CombatRequestData.MessageOneofCase.HitRequest)]
public CombatResponseData OnHitRequest(CombatRequestContext context)
{
return new CombatResponseData
{
RequestId = context.Request.RequestId,
CombatCommon = context.Request.CombatCommon,
HitResponse = new()
{
HitInfo = context.Request.HitRequest.HitInfo
}
};
}
[CombatRequest(CombatRequestData.MessageOneofCase.LogicStateInitRequest)]
public CombatResponseData OnLogicStateInitRequest(CombatRequestContext context)
{
CombatResponseData rsp = new()
{
CombatCommon = context.Request.CombatCommon,
RequestId = context.Request.RequestId,
LogicStateInitResponse = new()
};
EntityBase? entity = _entitySystem.Get<EntityBase>(context.Request.CombatCommon.EntityId);
if (entity == null) return rsp;
EntityLogicStateComponent logicStateComponent = entity.ComponentSystem.Get<EntityLogicStateComponent>();
logicStateComponent.States = [.. context.Request.LogicStateInitRequest.InitData.States];
context.Notifies.Add(new CombatNotifyData
{
CombatCommon = context.Request.CombatCommon,
LogicStateInitNotify = new()
{
CombatCommon = context.Request.CombatCommon,
EntityId = entity.Id,
InitData = logicStateComponent.Pb.LogicStateComponentPb
}
});
return rsp;
}
[CombatRequest(CombatRequestData.MessageOneofCase.SwitchLogicStateRequest)]
public CombatResponseData OnSwitchLogicStateRequest(CombatRequestContext context)
{
CombatResponseData rsp = new()
{
CombatCommon = context.Request.CombatCommon,
RequestId = context.Request.RequestId,
SwitchLogicStateResponse = new()
};
EntityBase? entity = _entitySystem.Get<EntityBase>(context.Request.CombatCommon.EntityId);
if (entity == null)
{
rsp.SwitchLogicStateResponse.ErrorCode = (int)ErrorCode.ErrActionEntityNoExist;
return rsp;
}
EntityLogicStateComponent logicStateComponent = entity.ComponentSystem.Get<EntityLogicStateComponent>();
logicStateComponent.States = [.. context.Request.SwitchLogicStateRequest.States];
context.Notifies.Add(new CombatNotifyData
{
CombatCommon = context.Request.CombatCommon,
SwitchLogicStateNotify = new()
{
States = { logicStateComponent.States }
}
});
return rsp;
}
[CombatRequest(CombatRequestData.MessageOneofCase.BattleStateChangeRequest)]
public CombatResponseData OnBattleStateChangeRequest(CombatRequestContext context)
{
return new CombatResponseData
{
CombatCommon = context.Request.CombatCommon,
RequestId = context.Request.RequestId,
BattleStateChangeResponse = new()
};
}
[CombatRequest(CombatRequestData.MessageOneofCase.ChangeStateConfirmRequest)]
public CombatResponseData OnChangeStateConfirmRequest(CombatRequestContext context)
{
CombatResponseData rsp = new()
{
CombatCommon = context.Request.CombatCommon,
RequestId = context.Request.RequestId,
ChangeStateConfirmResponse = new()
{
Error = new()
}
};
EntityBase? entity = _entitySystem.Get<EntityBase>(context.Request.CombatCommon.EntityId);
if (entity == null)
{
rsp.ChangeStateConfirmResponse.Error.ErrorCode = (int)ErrorCode.ErrEntityIsNotAlive;
return rsp;
}
ChangeStateConfirmRequest request = context.Request.ChangeStateConfirmRequest;
if (entity.ComponentSystem.TryGet(out EntityFsmComponent? fsmComponent))
{
DFsm? dfsm = fsmComponent.Fsms.FirstOrDefault(fsm => fsm.FsmId == request.FsmId);
dfsm ??= new()
{
FsmId = request.FsmId,
Status = 1,
Flag = (int)EFsmStateFlag.Confirmed
};
dfsm.CurrentState = request.State;
context.Notifies.Add(new CombatNotifyData
{
CombatCommon = context.Request.CombatCommon,
ChangeStateConfirmNotify = new ChangeStateConfirmNotify
{
State = request.State,
FsmId = request.FsmId
}
});
}
rsp.ChangeStateConfirmResponse.State = context.Request.ChangeStateConfirmRequest.State;
rsp.ChangeStateConfirmResponse.FsmId = context.Request.ChangeStateConfirmRequest.FsmId;
return rsp;
}
[CombatRequest(CombatRequestData.MessageOneofCase.FsmConditionPassRequest)]
public CombatResponseData OnFsmConditionPassRequest(CombatRequestContext context)
{
return new CombatResponseData
{
CombatCommon = context.Request.CombatCommon,
RequestId = context.Request.RequestId,
FsmConditionPassResponse = new()
{
FsmId = context.Request.FsmConditionPassRequest.FsmId,
Error = new()
}
};
}
[CombatRequest(CombatRequestData.MessageOneofCase.AiInformationRequest)]
public CombatResponseData OnAiInformationRequest(CombatRequestContext context)
{
// Currently like this, TODO!
context.Notifies.Add(new CombatNotifyData
{
CombatCommon = new CombatCommon
{
EntityId = context.Request.CombatCommon.EntityId, // id of monster
},
AiHateNotify = new AiHateNotify
{
HateList =
{
new AiHateEntity
{
EntityId = _creatureController.GetPlayerEntity()!.Id, // id of hated entity (the player)
HatredValue = 99999
}
}
}
});
return new CombatResponseData
{
CombatCommon = context.Request.CombatCommon,
RequestId = context.Request.RequestId,
AiInformationResponse = new() // ?? contains nothing
};
}
public Task<CombatResponseData?> HandleRequest(CombatRequestContext context)
{
if (s_requestHandlers.TryGetValue(context.Request.MessageCase, out CombatRequestHandler? handler))
return handler(this, context)!;
_logger.LogWarning("Combat request not handled: {type}", context.Request.MessageCase);
return Task.FromResult<CombatResponseData?>(null);
}
private static ImmutableDictionary<CombatRequestData.MessageOneofCase, CombatRequestHandler> MapRequestHandlers()
{
var builder = ImmutableDictionary.CreateBuilder<CombatRequestData.MessageOneofCase, CombatRequestHandler>();
MethodInfo taskFromResultMethod = typeof(Task).GetMethod(nameof(Task.FromResult))!.MakeGenericMethod(typeof(CombatResponseData));
foreach (MethodInfo method in typeof(CombatManager).GetMethods())
{
CombatRequestAttribute? attribute = method.GetCustomAttribute<CombatRequestAttribute>();
if (attribute == null) continue;
ParameterExpression combatManagerParam = Expression.Parameter(typeof(CombatManager));
ParameterExpression contextParam = Expression.Parameter(typeof(CombatRequestContext));
Expression call = Expression.Call(combatManagerParam, method, contextParam);
if (method.ReturnType == typeof(CombatResponseData))
call = Expression.Call(taskFromResultMethod, call);
Expression<CombatRequestHandler> lambda = Expression.Lambda<CombatRequestHandler>(call, combatManagerParam, contextParam);
builder.Add(attribute.MessageCase, lambda.Compile());
}
return builder.ToImmutable();
}
}

View file

@ -0,0 +1,14 @@
using Protocol;
namespace GameServer.Controllers.Combat;
internal class CombatRequestContext
{
public CombatRequestData Request { get; }
public List<CombatNotifyData> Notifies { get; }
public CombatRequestContext(CombatRequestData request)
{
Request = request;
Notifies = [];
}
}

View file

@ -0,0 +1,45 @@
using GameServer.Controllers.Attributes;
using GameServer.Controllers.Combat;
using GameServer.Network;
using GameServer.Network.Messages;
using Protocol;
namespace GameServer.Controllers;
internal class CombatMessageController : Controller
{
public CombatMessageController(PlayerSession session) : base(session)
{
// CombatMessageController.
}
[NetEvent(MessageId.CombatSendPackRequest)] // TODO: CombatSendPackRequest is important
public async Task<RpcResult> OnCombatSendPackRequest(CombatSendPackRequest request, CombatManager combatManager)
{
CombatReceivePackNotify combatPackNotify = new();
foreach (CombatSendData sendData in request.Data)
{
if (sendData.Request != null)
{
CombatRequestContext context = new(sendData.Request);
CombatResponseData? responseData = await combatManager.HandleRequest(context);
combatPackNotify.Data.AddRange(context.Notifies.Select(notify => new CombatReceiveData
{
CombatNotifyData = notify
}));
if (responseData != null)
{
combatPackNotify.Data.Add(new CombatReceiveData
{
CombatResponseData = responseData
});
}
}
}
await Session.Push(MessageId.CombatReceivePackNotify, combatPackNotify);
return Response(MessageId.CombatSendPackResponse, new CombatSendPackResponse());
}
}

View file

@ -1,4 +1,5 @@
using GameServer.Network; global using GameServer.Controllers.Result;
using GameServer.Network;
using GameServer.Network.Messages; using GameServer.Network.Messages;
using Google.Protobuf; using Google.Protobuf;
using Protocol; using Protocol;
@ -13,9 +14,9 @@ internal abstract class Controller
Session = session; Session = session;
} }
protected static ResponseMessage Response<TProtoBuf>(MessageId messageId, TProtoBuf protoBuf) where TProtoBuf : IMessage<TProtoBuf> => new() protected static RpcResult Response<TProtoBuf>(MessageId messageId, TProtoBuf protoBuf) where TProtoBuf : IMessage<TProtoBuf> => new(new ResponseMessage
{ {
MessageId = messageId, MessageId = messageId,
Payload = protoBuf.ToByteArray() Payload = protoBuf.ToByteArray()
}; });
} }

View file

@ -1,4 +1,6 @@
using GameServer.Controllers.Attributes; using Core.Config;
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.Network.Messages;
@ -13,22 +15,25 @@ internal class CreatureController : Controller
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;
public CreatureController(PlayerSession session, EntitySystem entitySystem, EntityFactory entityFactory, ModelManager modelManager) : base(session) public CreatureController(PlayerSession session, EntitySystem entitySystem, EntityFactory entityFactory, ModelManager modelManager, ConfigManager configManager) : base(session)
{ {
_entitySystem = entitySystem; _entitySystem = entitySystem;
_entityFactory = entityFactory; _entityFactory = entityFactory;
_modelManager = modelManager; _modelManager = modelManager;
_configManager = configManager;
} }
public async Task JoinScene(int instanceId) public async Task JoinScene(int instanceId)
{ {
_modelManager.Creature.SetSceneLoadingData(instanceId); _modelManager.Creature.SetSceneLoadingData(instanceId);
CreateTeamPlayerEntities(); CreateTeamPlayerEntities();
CreateWorldEntities();
await Session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify await Session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify
{ {
MaxEntityId = 10000, MaxEntityId = 10000000,
TransitionOption = new TransitionOptionPb TransitionOption = new TransitionOptionPb
{ {
TransitionType = (int)TransitionType.Empty TransitionType = (int)TransitionType.Empty
@ -38,7 +43,7 @@ internal class CreatureController : Controller
} }
[NetEvent(MessageId.EntityActiveRequest)] [NetEvent(MessageId.EntityActiveRequest)]
public async Task<ResponseMessage> OnEntityActiveRequest(EntityActiveRequest request) public async Task<RpcResult> OnEntityActiveRequest(EntityActiveRequest request)
{ {
EntityActiveResponse response; EntityActiveResponse response;
@ -48,7 +53,8 @@ internal class CreatureController : Controller
_entitySystem.Activate(entity); _entitySystem.Activate(entity);
response = new EntityActiveResponse response = new EntityActiveResponse
{ {
ErrorCode = (int)ErrorCode.Success ErrorCode = (int)ErrorCode.Success,
IsVisible = entity.IsVisible
}; };
response.ComponentPbs.AddRange(entity.ComponentSystem.Pb); response.ComponentPbs.AddRange(entity.ComponentSystem.Pb);
@ -63,13 +69,62 @@ internal class CreatureController : Controller
} }
[NetEvent(MessageId.SceneLoadingFinishRequest)] [NetEvent(MessageId.SceneLoadingFinishRequest)]
public ResponseMessage OnSceneLoadingFinishRequest() public async Task<RpcResult> OnSceneLoadingFinishRequest()
{ {
_modelManager.Creature.OnWorldDone(); _modelManager.Creature.OnWorldDone();
await UpdateAiHate();
return Response(MessageId.SceneLoadingFinishResponse, new SceneLoadingFinishResponse()); return Response(MessageId.SceneLoadingFinishResponse, new SceneLoadingFinishResponse());
} }
[GameEvent(GameEventType.FormationUpdated)]
public async Task OnFormationUpdated()
{
// Remove old entities
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();
IEnumerable<PlayerEntity> newEntities = GetPlayerEntities();
await Session.Push(MessageId.EntityAddNotify, new EntityAddNotify
{
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();
}
[GameEvent(GameEventType.VisionSkillChanged)] [GameEvent(GameEventType.VisionSkillChanged)]
public async Task OnVisionSkillChanged() public async Task OnVisionSkillChanged()
{ {
@ -86,7 +141,20 @@ internal class CreatureController : Controller
public PlayerEntity? GetPlayerEntity() public PlayerEntity? GetPlayerEntity()
{ {
return _entitySystem.EnumerateEntities().FirstOrDefault(entity => entity.Id == _modelManager.Creature.PlayerEntityId) as PlayerEntity; return _entitySystem.Get<PlayerEntity>(_modelManager.Creature.PlayerEntityId);
}
public PlayerEntity? GetPlayerEntityByRoleId(int roleId)
{
return _entitySystem.EnumerateEntities()
.FirstOrDefault(e => e is PlayerEntity playerEntity && playerEntity.ConfigId == roleId && playerEntity.PlayerId == _modelManager.Player.Id) as PlayerEntity;
}
public IEnumerable<PlayerEntity> GetPlayerEntities()
{
return _entitySystem.EnumerateEntities()
.Where(e => e is PlayerEntity entity && entity.PlayerId == _modelManager.Player.Id)
.Cast<PlayerEntity>();
} }
public async Task SwitchPlayerEntity(int roleId) public async Task SwitchPlayerEntity(int roleId)
@ -96,24 +164,55 @@ internal class CreatureController : Controller
prevEntity.IsCurrentRole = false; prevEntity.IsCurrentRole = false;
PlayerEntity? newEntity = _entitySystem.EnumerateEntities().FirstOrDefault(e => e is PlayerEntity playerEntity && playerEntity.ConfigId == roleId) as PlayerEntity; if (_entitySystem.EnumerateEntities().FirstOrDefault(e => e is PlayerEntity playerEntity && playerEntity.ConfigId == roleId) is not PlayerEntity newEntity) return;
if (newEntity == null) return;
_modelManager.Creature.PlayerEntityId = newEntity.Id; _modelManager.Creature.PlayerEntityId = newEntity.Id;
newEntity.IsCurrentRole = true; newEntity.IsCurrentRole = true;
await OnVisionSkillChanged(); await UpdateAiHate();
} }
private SceneInformation CreateSceneInfo() public async Task UpdateAiHate()
{ {
SceneInformation scene = new() IEnumerable<EntityBase> monsters = _entitySystem.EnumerateEntities().Where(e => e is MonsterEntity);
if (!monsters.Any()) return;
await Session.Push(MessageId.CombatReceivePackNotify, new CombatReceivePackNotify
{
Data =
{
monsters.Select(monster => new CombatReceiveData
{
CombatNotifyData = new()
{
CombatCommon = new() { EntityId = monster.Id },
AiHateNotify = new()
{
HateList =
{
GetPlayerEntities().Select(player => new AiHateEntity
{
EntityId = player.Id,
HatredValue = 99999 // currently this, TODO!
})
}
}
}
})
}
});
}
private SceneInformation CreateSceneInfo() => new()
{ {
InstanceId = _modelManager.Creature.InstanceId, InstanceId = _modelManager.Creature.InstanceId,
OwnerId = _modelManager.Creature.OwnerId, OwnerId = _modelManager.Creature.OwnerId,
CurContextId = _modelManager.Player.Id, CurContextId = _modelManager.Player.Id,
TimeInfo = new(), TimeInfo = new(),
AoiData = new(), AoiData = new PlayerSceneAoiData
{
Entities = { _entitySystem.Pb }
},
PlayerInfos = PlayerInfos =
{ {
new ScenePlayerInformation new ScenePlayerInformation
@ -121,50 +220,86 @@ internal class CreatureController : Controller
PlayerId = _modelManager.Player.Id, PlayerId = _modelManager.Player.Id,
Level = 1, Level = 1,
IsOffline = false, IsOffline = false,
Location = new() Location = _modelManager.Player.Position,
{ PlayerName = _modelManager.Player.Name,
X = 4000, FightRoleInfos = { GetFightRoleInfos() }
Y = -2000,
Z = 260
},
PlayerName = _modelManager.Player.Name
} }
} }
}; };
for (int i = 0; i < _modelManager.Player.Characters.Length; i++) private IEnumerable<FightRoleInformation> GetFightRoleInfos()
{ {
scene.PlayerInfos[0].FightRoleInfos.Add(new FightRoleInformation return GetPlayerEntities().Select(playerEntity => new FightRoleInformation
{ {
EntityId = i + 1, EntityId = playerEntity.Id,
CurHp = 1000, CurHp = playerEntity.Health,
MaxHp = 1000, MaxHp = playerEntity.HealthMax,
IsControl = i == 0, IsControl = playerEntity.Id == _modelManager.Creature.PlayerEntityId,
RoleId = _modelManager.Player.Characters[i], RoleId = playerEntity.ConfigId,
RoleLevel = 1, RoleLevel = 1,
}); });
} }
scene.AoiData.Entities.AddRange(_entitySystem.Pb);
return scene;
}
private void CreateTeamPlayerEntities() private void CreateTeamPlayerEntities()
{ {
for (int i = 0; i < _modelManager.Player.Characters.Length; i++) for (int i = 0; i < _modelManager.Formation.RoleIds.Length; i++)
{ {
PlayerEntity entity = _entityFactory.CreatePlayer(_modelManager.Player.Characters[i], _modelManager.Player.Id); int roleId = _modelManager.Formation.RoleIds[i];
entity.Pos = new()
{ PlayerEntity entity = _entityFactory.CreatePlayer(roleId, _modelManager.Player.Id);
X = 4000, entity.Pos = _modelManager.Player.Position.Clone();
Y = -2000,
Z = 260
};
entity.IsCurrentRole = i == 0; entity.IsCurrentRole = i == 0;
_entitySystem.Create(entity); _entitySystem.Create(entity);
entity.ComponentSystem.Get<EntityAttributeComponent>().SetAll(_modelManager.Roles.GetRoleById(roleId)!.GetAttributeList());
CreateConcomitants(entity);
entity.WeaponId = _modelManager.Inventory.GetEquippedWeapon(roleId)?.Id ?? 0;
if (i == 0) _modelManager.Creature.PlayerEntityId = entity.Id; if (i == 0) _modelManager.Creature.PlayerEntityId = entity.Id;
} }
} }
private void CreateConcomitants(PlayerEntity entity)
{
(int roleId, int summonConfigId) = entity.ConfigId switch
{
1302 => (5002, 10070301),
_ => (-1, -1)
};
if (roleId != -1)
{
PlayerEntity concomitant = _entityFactory.CreatePlayer(roleId, 0);
_entitySystem.Create(concomitant);
EntityConcomitantsComponent concomitants = entity.ComponentSystem.Get<EntityConcomitantsComponent>();
concomitants.CustomEntityIds.Clear();
concomitants.CustomEntityIds.Add(concomitant.Id);
EntitySummonerComponent summoner = concomitant.ComponentSystem.Create<EntitySummonerComponent>();
summoner.SummonerId = entity.Id;
summoner.SummonConfigId = summonConfigId;
summoner.SummonType = ESummonType.ConcomitantCustom;
summoner.PlayerId = _modelManager.Player.Id;
concomitant.InitProps(_configManager.GetConfig<BasePropertyConfig>(roleId)!);
}
}
private void CreateWorldEntities()
{
Vector playerPos = _modelManager.Player.Position;
// Test monster
MonsterEntity monster = _entityFactory.CreateMonster(106003002); // Turtle.
monster.Pos = new()
{
X = playerPos.X + 250,
Y = playerPos.Y + 250,
Z = playerPos.Z
};
_entitySystem.Create(monster);
monster.InitProps(_configManager.GetConfig<BasePropertyConfig>(600000100)!);
}
} }

View file

@ -12,8 +12,8 @@ internal class DailyActivityController : Controller
} }
[NetEvent(MessageId.ActivityRequest)] [NetEvent(MessageId.ActivityRequest)]
public ResponseMessage OnActivityRequest() => Response(MessageId.ActivityResponse, new ActivityResponse()); public RpcResult OnActivityRequest() => Response(MessageId.ActivityResponse, new ActivityResponse());
[NetEvent(MessageId.LivenessRequest)] [NetEvent(MessageId.LivenessRequest)]
public ResponseMessage OnLivenessRequest() => Response(MessageId.LivenessResponse, new LivenessResponse()); public RpcResult OnLivenessRequest() => Response(MessageId.LivenessResponse, new LivenessResponse());
} }

View file

@ -12,5 +12,5 @@ internal class ExchangeRewardController : Controller
} }
[NetEvent(MessageId.ExchangeRewardInfoRequest)] [NetEvent(MessageId.ExchangeRewardInfoRequest)]
public ResponseMessage OnExchangeRewardInfoRequest() => Response(MessageId.ExchangeRewardInfoResponse, new ExchangeRewardInfoResponse()); public RpcResult OnExchangeRewardInfoRequest() => Response(MessageId.ExchangeRewardInfoResponse, new ExchangeRewardInfoResponse());
} }

View file

@ -7,7 +7,7 @@ namespace GameServer.Controllers;
internal class ExploreProgressController : Controller internal class ExploreProgressController : Controller
{ {
private static readonly int[] s_areaIds = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 100, 101, 102, 103, 107, 110, 113, 124, 122, 199, 301, 302, 303, 401, 402, 403, 404, 405, 406, 407, 408, 708, 601, 602, 603, 606, 607, 608, 609, 202, 203, 204, 501, 502, 503, 504, 508, 802, 803, 805, 807, 702, 703, 704, 705, 706, 709, 1201, 1202, 1203, 1204, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1301, 119, 120, 10001, 10002, 10003, 10004, 10005, 11001, 12001, 12002, 12003, 1500001, 1500002, 14001, 14002, 14003, 14004, 14005, 14006, 14007, 14008, 14011, 14012, 14013, 14021, 14022, 123, 125, 51, 804]; private static readonly int[] s_areaIds = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 100, 101, 102, 103, 107, 110, 113, 124, 122, 199, 301, 302, 303, 401, 402, 403, 404, 405, 406, 407, 408, 708, 601, 602, 603, 606, 607, 608, 609, 202, 203, 204, 501, 502, 503, 504, 508, 802, 803, 805, 807, 702, 703, 704, 705, 706, 709, 1201, 1202, 1203, 1204, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1301, 119, 120, 10001, 10002, 10003, 10004, 10005, 11001, 12001, 12002, 12003, 1500001, 1500002, 14001, 14002, 14003, 14004, 14005, 14006, 14007, 14008, 14011, 14012, 14013, 14021, 14022, 123, 125, 51, 804];
private static readonly (int, int)[] s_exploreIds = [(1, 2), (2, 2), (3, 2), (10, 3), (11, 4), (12, 4), (13, 4), (21, 6), (22, 6), (23, 6), (24, 6), (31, 8), (32, 8), (33, 8), (34, 8), (41, 10), (42, 10), (43, 10), (44, 10), (51, 12), (52, 12), (53, 12), (54, 12), (6, 3), (7, 3), (8, 3), (9, 3), (14, 4), (15, 4), (16, 5), (17, 5), (18, 5), (19, 5), (25, 6), (26, 7), (27, 7), (28, 7), (29, 7), (45, 10), (55, 12), (4, 2), (5, 2), (20, 5), (30, 7), (35, 8)]; private static readonly (int, int)[] s_exploreIds = [(1, 2), (2, 2), (3, 2), (11, 4), (12, 4), (13, 4), (21, 6), (22, 6), (23, 6), (24, 6), (31, 8), (32, 8), (33, 8), (34, 8), (41, 10), (42, 10), (43, 10), (44, 10), (51, 12), (52, 12), (53, 12), (54, 12), (6, 3), (7, 3), (8, 3), (9, 3), (14, 4), (15, 4), (16, 5), (17, 5), (18, 5), (19, 5), (25, 6), (26, 7), (27, 7), (28, 7), (29, 7), (45, 10), (55, 12), (4, 2), (5, 2), (20, 5), (30, 7), (35, 8)];
public ExploreProgressController(PlayerSession session) : base(session) public ExploreProgressController(PlayerSession session) : base(session)
{ {
@ -15,7 +15,7 @@ internal class ExploreProgressController : Controller
} }
[NetEvent(MessageId.ExploreProgressRequest)] [NetEvent(MessageId.ExploreProgressRequest)]
public ResponseMessage OnExploreProgressRequest() public RpcResult OnExploreProgressRequest()
{ {
return Response(MessageId.ExploreProgressResponse, new ExploreProgressResponse return Response(MessageId.ExploreProgressResponse, new ExploreProgressResponse
{ {

View file

@ -90,13 +90,13 @@ internal class EventHandlerFactory
var builder = ImmutableDictionary.CreateBuilder<MessageId, RpcHandler>(); var builder = ImmutableDictionary.CreateBuilder<MessageId, RpcHandler>();
MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod(nameof(ServiceProviderServiceExtensions.GetRequiredService), [typeof(IServiceProvider)])!; MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod(nameof(ServiceProviderServiceExtensions.GetRequiredService), [typeof(IServiceProvider)])!;
MethodInfo taskFromResultMethod = typeof(Task).GetMethod(nameof(Task.FromResult))!.MakeGenericMethod(typeof(ResponseMessage)); MethodInfo taskFromResultMethod = typeof(Task).GetMethod(nameof(Task.FromResult))!.MakeGenericMethod(typeof(RpcResult));
foreach (Type type in controllerTypes) foreach (Type type in controllerTypes)
{ {
IEnumerable<MethodInfo> methods = type.GetMethods() IEnumerable<MethodInfo> methods = type.GetMethods()
.Where(method => method.GetCustomAttribute<NetEventAttribute>() != null .Where(method => method.GetCustomAttribute<NetEventAttribute>() != null
&& (method.ReturnType == typeof(Task<ResponseMessage>) || method.ReturnType == typeof(ResponseMessage))); && (method.ReturnType == typeof(Task<RpcResult>) || method.ReturnType == typeof(RpcResult)));
foreach (MethodInfo method in methods) foreach (MethodInfo method in methods)
{ {
@ -108,7 +108,7 @@ internal class EventHandlerFactory
MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam); MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam);
Expression handlerCall = Expression.Call(getServiceCall, method, FetchArgumentsForMethod(method, serviceProviderParam, getServiceMethod, dataParam)); Expression handlerCall = Expression.Call(getServiceCall, method, FetchArgumentsForMethod(method, serviceProviderParam, getServiceMethod, dataParam));
if (method.ReturnType == typeof(ResponseMessage)) // Allow non-async methods as well if (method.ReturnType == typeof(RpcResult)) // Allow non-async methods as well
handlerCall = Expression.Call(taskFromResultMethod, handlerCall); handlerCall = Expression.Call(taskFromResultMethod, handlerCall);
Expression<RpcHandler> lambda = Expression.Lambda<RpcHandler>(handlerCall, serviceProviderParam, dataParam); Expression<RpcHandler> lambda = Expression.Lambda<RpcHandler>(handlerCall, serviceProviderParam, dataParam);

View file

@ -0,0 +1,19 @@
using GameServer.Controllers.Attributes;
using GameServer.Network;
using GameServer.Network.Messages;
using Protocol;
namespace GameServer.Controllers;
internal class FormationAttributeController : Controller
{
public FormationAttributeController(PlayerSession session) : base(session)
{
// FormationAttributeController.
}
[NetEvent(MessageId.TimeCheckRequest)]
public RpcResult OnTimeCheckRequest() => Response(MessageId.TimeCheckResponse, new TimeCheckResponse());
[NetEvent(MessageId.FormationAttrRequest)]
public RpcResult OnFormationAttrRequest() => Response(MessageId.FormationAttrResponse, new FormationAttrResponse());
}

View file

@ -2,6 +2,8 @@
using GameServer.Models; using GameServer.Models;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages; using GameServer.Network.Messages;
using GameServer.Systems.Entity;
using GameServer.Systems.Event;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
@ -15,20 +17,29 @@ internal class FormationController : Controller
} }
[NetEvent(MessageId.GetFormationDataRequest)] [NetEvent(MessageId.GetFormationDataRequest)]
public ResponseMessage OnGetFormationDataRequest() => Response(MessageId.GetFormationDataResponse, new GetFormationDataResponse public RpcResult OnGetFormationDataRequest() => Response(MessageId.GetFormationDataResponse, new GetFormationDataResponse
{ {
Formations = Formations =
{ {
new FightFormation new FightFormation
{ {
CurRole = _modelManager.Player.Characters[0], CurRole = _modelManager.Formation.RoleIds[0],
FormationId = 1, FormationId = 1,
IsCurrent = true, IsCurrent = true,
RoleIds = { _modelManager.Player.Characters }, RoleIds = { _modelManager.Formation.RoleIds },
} }
}, },
}); });
[NetEvent(MessageId.FormationAttrRequest)] [NetEvent(MessageId.UpdateFormationRequest)]
public ResponseMessage OnFormationAttrRequest() => Response(MessageId.FormationAttrResponse, new FormationAttrResponse()); public async Task<RpcResult> OnUpdateFormationRequest(UpdateFormationRequest request, EventSystem eventSystem)
{
_modelManager.Formation.Set([.. request.Formation.RoleIds]);
await eventSystem.Emit(GameEventType.FormationUpdated);
return Response(MessageId.UpdateFormationResponse, new UpdateFormationResponse
{
Formation = request.Formation
});
}
} }

View file

@ -12,5 +12,25 @@ internal class FriendSystemController : Controller
} }
[NetEvent(MessageId.FriendAllRequest)] [NetEvent(MessageId.FriendAllRequest)]
public ResponseMessage OnFriendAllRequest() => Response(MessageId.FriendAllResponse, new FriendAllResponse()); public RpcResult OnFriendAllRequest() => Response(MessageId.FriendAllResponse, new FriendAllResponse
{
FriendInfoList =
{
CreateDummyFriendInfo(1338, "Yangyang", "discord.gg/reversedrooms", 1402)
}
});
private static FriendInfo CreateDummyFriendInfo(int id, string name, string signature, int headIconId) => new()
{
Info = new()
{
PlayerId = id,
Name = name,
Signature = signature,
Level = 5,
HeadId = headIconId,
IsOnline = true,
LastOfflineTime = -1
}
};
} }

View file

@ -12,5 +12,5 @@ internal class GachaController : Controller
} }
[NetEvent(MessageId.GachaInfoRequest)] [NetEvent(MessageId.GachaInfoRequest)]
public ResponseMessage OnGachaInfoRequest() => Response(MessageId.GachaInfoResponse, new GachaInfoResponse()); public RpcResult OnGachaInfoRequest() => Response(MessageId.GachaInfoResponse, new GachaInfoResponse());
} }

View file

@ -12,5 +12,5 @@ internal class InfluenceReputationController : Controller
} }
[NetEvent(MessageId.InfluenceInfoRequest)] [NetEvent(MessageId.InfluenceInfoRequest)]
public ResponseMessage OnInfluenceInfoRequest() => Response(MessageId.InfluenceInfoResponse, new InfluenceInfoResponse()); public RpcResult OnInfluenceInfoRequest() => Response(MessageId.InfluenceInfoResponse, new InfluenceInfoResponse());
} }

View file

@ -1,6 +1,11 @@
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.Entity.Component;
using GameServer.Systems.Event;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
@ -12,14 +17,120 @@ internal class InventoryController : Controller
} }
[NetEvent(MessageId.NormalItemRequest)] [NetEvent(MessageId.NormalItemRequest)]
public ResponseMessage OnNormalItemRequest() => Response(MessageId.NormalItemResponse, new NormalItemResponse()); public RpcResult OnNormalItemRequest() => Response(MessageId.NormalItemResponse, new NormalItemResponse());
[NetEvent(MessageId.WeaponItemRequest)] [NetEvent(MessageId.WeaponItemRequest)]
public ResponseMessage 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 ResponseMessage OnPhantomItemRequest() => Response(MessageId.PhantomItemResponse, new PhantomItemResponse()); public RpcResult OnPhantomItemRequest() => Response(MessageId.PhantomItemResponse, new PhantomItemResponse());
[NetEvent(MessageId.ItemExchangeInfoRequest)] [NetEvent(MessageId.ItemExchangeInfoRequest)]
public ResponseMessage OnItemExchangeInfoRequest() => Response(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse()); public RpcResult OnItemExchangeInfoRequest() => Response(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse());
[NetEvent(MessageId.EquipTakeOnRequest)]
public async Task<RpcResult> OnEquipTakeOnRequest(EquipTakeOnRequest request, ModelManager modelManager, CreatureController creatureController, 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;
role.ApplyWeaponProperties(weaponConf);
// Update role prop data on client
await Session.Push(MessageId.PbRolePropsNotify, new PbRolePropsNotify
{
RoleId = role.RoleId,
BaseProp = { role.BaseProp },
AddProp = { role.AddProp }
});
PlayerEntity? entity = creatureController.GetPlayerEntityByRoleId(request.Data.RoleId);
if (entity != null)
{
// Update entity equipment
EntityEquipComponent equipComponent = entity.ComponentSystem.Get<EntityEquipComponent>();
equipComponent.WeaponId = weapon.Id;
await Session.Push(MessageId.EntityEquipChangeNotify, new EntityEquipChangeNotify
{
EntityId = entity.Id,
EquipComponent = equipComponent.Pb.EquipComponent
});
// Update entity gameplay attributes
EntityAttributeComponent attrComponent = entity.ComponentSystem.Get<EntityAttributeComponent>();
attrComponent.SetAll(role.GetAttributeList());
await Session.Push(MessageId.AttributeChangedNotify, new AttributeChangedNotify
{
Id = entity.Id,
Attributes = { attrComponent.Pb.AttributeComponent.GameAttributes }
});
}
// Response
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.DebugUnlockAllWeapons)]
public void DebugUnlockAllWeapons(ConfigManager configManager, ModelManager modelManager)
{
foreach (WeaponConfig weaponConf in configManager.Enumerate<WeaponConfig>())
{
modelManager.Inventory.AddNewWeapon(weaponConf.ItemId);
}
}
} }

View file

@ -1,8 +1,6 @@
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 Microsoft.Extensions.Logging;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
@ -14,29 +12,36 @@ internal class LoginController : Controller
} }
[NetEvent(MessageId.LoginRequest)] [NetEvent(MessageId.LoginRequest)]
public async Task<ResponseMessage> OnLoginRequest(EventSystem eventSystem) public async Task<RpcResult> OnLoginRequest(EventSystem eventSystem)
{ {
await eventSystem.Emit(GameEventType.Login); await eventSystem.Emit(GameEventType.Login);
// Debug
await eventSystem.Emit(GameEventType.DebugUnlockAllRoles);
await eventSystem.Emit(GameEventType.DebugUnlockAllWeapons);
return Response(MessageId.LoginResponse, new LoginResponse return Response(MessageId.LoginResponse, new LoginResponse
{ {
Code = 0, Code = 0,
Platform = "PC", Platform = "CBT3_EU",
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds() Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds()
}); });
} }
[NetEvent(MessageId.EnterGameRequest)] [NetEvent(MessageId.EnterGameRequest)]
public async Task<ResponseMessage> OnEnterGameRequest(EnterGameRequest request, ILogger<LoginController> logger, EventSystem eventSystem) public RpcResult OnEnterGameRequest()
{ {
logger.LogInformation("Enter Game Request:\n{req}", request); return Response(MessageId.EnterGameResponse, new EnterGameResponse())
.AddPostEvent(GameEventType.EnterGame)
.AddPostEvent(GameEventType.PushDataDone);
}
await eventSystem.Emit(GameEventType.EnterGame); [GameEvent(GameEventType.PushDataDone)]
public async Task OnPushDataDone()
{
await Session.Push(MessageId.PushDataCompleteNotify, new PushDataCompleteNotify()); await Session.Push(MessageId.PushDataCompleteNotify, new PushDataCompleteNotify());
return Response(MessageId.EnterGameResponse, new EnterGameResponse());
} }
[NetEvent(MessageId.HeartbeatRequest)] [NetEvent(MessageId.HeartbeatRequest)]
public ResponseMessage OnHeartbeatRequest() => Response(MessageId.HeartbeatResponse, new HeartbeatResponse()); public RpcResult OnHeartbeatRequest() => Response(MessageId.HeartbeatResponse, new HeartbeatResponse());
} }

View file

@ -12,5 +12,5 @@ internal class LordGymController : Controller
} }
[NetEvent(MessageId.LordGymInfoRequest)] [NetEvent(MessageId.LordGymInfoRequest)]
public ResponseMessage OnLordGymInfoRequest() => Response(MessageId.LordGymInfoResponse, new LordGymInfoResponse()); public RpcResult OnLordGymInfoRequest() => Response(MessageId.LordGymInfoResponse, new LordGymInfoResponse());
} }

View file

@ -1,6 +1,7 @@
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;
@ -40,26 +41,58 @@ internal class PlayerInfoController : Controller
{ {
Key = (int)PlayerAttrKey.HeadPhoto, Key = (int)PlayerAttrKey.HeadPhoto,
ValueType = (int)PlayerAttrType.Int32, ValueType = (int)PlayerAttrType.Int32,
Int32Value = 1601 Int32Value = 1402
}, },
new PlayerAttr new PlayerAttr
{ {
Key = (int)PlayerAttrKey.HeadFrame, Key = (int)PlayerAttrKey.HeadFrame,
ValueType = (int)PlayerAttrType.Int32, ValueType = (int)PlayerAttrType.Int32,
Int32Value = 80060009 Int32Value = 80060009
},
new PlayerAttr
{
Key = (int)PlayerAttrKey.Sex,
ValueType = (int)PlayerAttrType.Int32,
Int32Value = 1
}
},
RoleShowList =
{
new RoleShowEntry
{
Level = 1,
RoleId = 1501 // Rover
}
},
CurCardId = 80060000,
CardUnlockList =
{
new CardShowEntry
{
CardId = 80060000,
IsRead = true
} }
} }
}; };
for (int i = 0; i < player.Characters.Length; i++)
{
basicInfo.RoleShowList.Add(new RoleShowEntry
{
Level = 1,
RoleId = player.Characters[i]
});
}
await Session.Push(MessageId.BasicInfoNotify, basicInfo); await Session.Push(MessageId.BasicInfoNotify, basicInfo);
} }
[NetEvent(MessageId.PlayerBasicInfoGetRequest)]
public RpcResult OnPlayerBasicInfoGetRequest()
{
return Response(MessageId.PlayerBasicInfoGetResponse, new PlayerBasicInfoGetResponse
{
Info = new PlayerDetails
{
Name = "Yangyang",
Signature = "discord.gg/reversedrooms",
HeadId = 1402,
PlayerId = 1338,
IsOnline = true,
LastOfflineTime = -1,
Level = 5
}
});
}
} }

View file

@ -0,0 +1,21 @@
using GameServer.Network.Messages;
using GameServer.Systems.Event;
namespace GameServer.Controllers.Result;
internal class RpcResult
{
public ResponseMessage Response { get; }
public List<GameEventType> PostEvents { get; }
public RpcResult(ResponseMessage response)
{
Response = response;
PostEvents = [];
}
public RpcResult AddPostEvent(GameEventType type)
{
PostEvents.Add(type);
return this;
}
}

View file

@ -12,5 +12,5 @@ internal class RoguelikeController : Controller
} }
[NetEvent(MessageId.RoguelikeSeasonDataRequest)] [NetEvent(MessageId.RoguelikeSeasonDataRequest)]
public ResponseMessage OnRoguelikeSeasonDataRequest() => Response(MessageId.RoguelikeSeasonDataResponse, new RoguelikeSeasonDataResponse()); public RpcResult OnRoguelikeSeasonDataRequest() => Response(MessageId.RoguelikeSeasonDataResponse, new RoguelikeSeasonDataResponse());
} }

View file

@ -1,7 +1,8 @@
using GameServer.Controllers.Attributes; using Core.Config;
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 Protocol; using Protocol;
@ -13,27 +14,37 @@ internal class RoleController : Controller
// RoleController. // RoleController.
} }
[GameEvent(GameEventType.DebugUnlockAllRoles)]
public void UnlockAllRoles(ConfigManager configManager, ModelManager modelManager)
{
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) 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 =
{ {
player.Characters.Select(i => new roleInfo modelManager.Roles.Roles
{
RoleId = i,
Level = 1
})
} }
}); });
} }
[NetEvent(MessageId.SwitchRoleRequest)] [NetEvent(MessageId.SwitchRoleRequest)]
public ResponseMessage OnSwitchRoleRequest(SwitchRoleRequest request) public async Task<RpcResult> OnSwitchRoleRequest(SwitchRoleRequest request, CreatureController creatureController)
{ {
await creatureController.SwitchPlayerEntity(request.RoleId);
return Response(MessageId.SwitchRoleResponse, new SwitchRoleResponse return Response(MessageId.SwitchRoleResponse, new SwitchRoleResponse
{ {
RoleId = request.RoleId RoleId = request.RoleId
@ -41,5 +52,150 @@ internal class RoleController : Controller
} }
[NetEvent(MessageId.RoleFavorListRequest)] [NetEvent(MessageId.RoleFavorListRequest)]
public ResponseMessage OnRoleFavorListRequest() => Response(MessageId.RoleFavorListResponse, new RoleFavorListResponse()); public RpcResult OnRoleFavorListRequest() => Response(MessageId.RoleFavorListResponse, new RoleFavorListResponse());
private static List<ArrayIntInt> CreateBasePropList(BasePropertyConfig? config)
{
List<ArrayIntInt> baseProp = [];
if (config == null) return baseProp;
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.Life, Value = config.Life });
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.SheildDamageReduce, Value = config.SheildDamageReduce });
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.CritDamage, Value = config.CritDamage });
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.CdReduse, Value = config.CdReduse });
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.DamageChange, Value = config.DamageChange });
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.DamageChangeCast, Value = config.DamageChangeCast });
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.DamageChangePhys, Value = config.DamageChangePhys });
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.DamageChangeElement3, Value = config.DamageChangeElement3 });
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.DamageChangeElement6, Value = config.DamageChangeElement6 });
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.DamageResistanceElement2, Value = config.DamageResistanceElement2 });
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.DamageResistanceElement5, Value = config.DamageResistanceElement5 });
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.HealedChange, Value = config.HealedChange });
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.DamageReduceElement2, Value = config.DamageReduceElement2 });
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.DamageReduceElement5, Value = config.DamageReduceElement5 });
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.ReactionChange2, Value = config.ReactionChange2 });
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.ReactionChange5, Value = config.ReactionChange5 });
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.ReactionChange8, Value = config.ReactionChange8 });
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.ReactionChange11, Value = config.ReactionChange11 });
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.ReactionChange14, Value = config.ReactionChange14 });
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.Energy, Value = config.Energy });
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.SpecialEnergy2Max, Value = config.SpecialEnergy2Max });
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.SpecialEnergy3, Value = config.SpecialEnergy3 });
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.StrengthMax, Value = config.StrengthMax });
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.StrengthPunishTime, Value = config.StrengthPunishTime });
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.StrengthFastSwim, Value = config.StrengthFastSwim });
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.HardnessMax, Value = config.HardnessMax });
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.HardnessPunishTime, Value = config.HardnessPunishTime });
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.RageMax, Value = config.RageMax });
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.RagePunishTime, Value = config.RagePunishTime });
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.ToughMax, Value = config.ToughMax });
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.ToughChange, Value = config.ToughChange });
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.ElementPower1, Value = config.ElementPower1 });
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.ElementPower4, Value = config.ElementPower4 });
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.SpecialDamageChange, Value = config.SpecialDamageChange });
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.WeakTime, Value = config.WeakTime });
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.IgnoreDamageResistanceElement1, Value = config.IgnoreDamageResistanceElement1 });
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.IgnoreDamageResistanceElement4, Value = config.IgnoreDamageResistanceElement4 });
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.SkillToughRatio, Value = config.SkillToughRatio });
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.Mass, Value = config.Mass });
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.SpeedRatio, Value = config.SpeedRatio });
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.CastAttackSpeed, Value = config.CastAttackSpeed });
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.StatusBuildUp2Max, Value = config.StatusBuildUp2Max });
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.StatusBuildUp3, Value = config.StatusBuildUp3 });
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.StatusBuildUp5Max, Value = config.StatusBuildUp5Max });
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.ParalysisTime, Value = config.ParalysisTime });
baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTimeRecover, Value = config.ParalysisTimeRecover });
return baseProp;
}
} }

View file

@ -20,7 +20,7 @@ internal class RouletteController : Controller
{ {
await Session.Push(MessageId.ExploreToolAllNotify, new ExploreToolAllNotify await Session.Push(MessageId.ExploreToolAllNotify, new ExploreToolAllNotify
{ {
SkillList = { 1001, 1004, 1003 }, SkillList = { 3001, 3002, 1005, 1006, 1001, 1004, 1003, 1007, 1009 },
ExploreSkill = 1001 ExploreSkill = 1001
}); });
@ -34,14 +34,14 @@ internal class RouletteController : Controller
}, },
new ExploreSkillRoulette new ExploreSkillRoulette
{ {
SkillIds = {1001, 1004, 1003, 0, 0, 0, 0, 0}, SkillIds = {10002, 10004, 0, 0, 0, 0, 0, 0},
} }
} }
}); });
} }
[NetEvent(MessageId.VisionExploreSkillSetRequest)] [NetEvent(MessageId.VisionExploreSkillSetRequest)]
public async Task<ResponseMessage> OnVisionExploreSkillSetRequest(VisionExploreSkillSetRequest request, CreatureController creatureController, EventSystem eventSystem) public async Task<RpcResult> OnVisionExploreSkillSetRequest(VisionExploreSkillSetRequest request, CreatureController creatureController, EventSystem eventSystem)
{ {
PlayerEntity? playerEntity = creatureController.GetPlayerEntity(); PlayerEntity? playerEntity = creatureController.GetPlayerEntity();
if (playerEntity == null) return Response(MessageId.VisionExploreSkillSetResponse, new VisionExploreSkillSetResponse { ErrCode = (int)ErrorCode.PlayerNotInAnyScene }); if (playerEntity == null) return Response(MessageId.VisionExploreSkillSetResponse, new VisionExploreSkillSetResponse { ErrCode = (int)ErrorCode.PlayerNotInAnyScene });

View file

@ -12,5 +12,5 @@ internal class ShopController : Controller
} }
[NetEvent(MessageId.PayShopInfoRequest)] [NetEvent(MessageId.PayShopInfoRequest)]
public ResponseMessage OnPayShopInfoRequest() => Response(MessageId.PayShopInfoResponse, new PayShopInfoResponse()); public RpcResult OnPayShopInfoRequest() => Response(MessageId.PayShopInfoResponse, new PayShopInfoResponse());
} }

View file

@ -0,0 +1,19 @@
using GameServer.Controllers.Attributes;
using GameServer.Network;
using GameServer.Network.Messages;
using Protocol;
namespace GameServer.Controllers;
internal class TeleportController : Controller
{
public TeleportController(PlayerSession session) : base(session)
{
// TeleportController.
}
[NetEvent(MessageId.TeleportFinishRequest)]
public RpcResult OnTeleportFinishRequest() => Response(MessageId.TeleportFinishResponse, new TeleportFinishResponse());
[NetEvent(MessageId.TeleportDataRequest)]
public RpcResult OnTeleportDataRequest() => Response(MessageId.TeleportDataResponse, new TeleportDataResponse());
}

View file

@ -12,8 +12,8 @@ internal class TowerController : Controller
} }
[NetEvent(MessageId.TowerChallengeRequest)] [NetEvent(MessageId.TowerChallengeRequest)]
public ResponseMessage OnTowerChallengeRequest() => Response(MessageId.TowerChallengeResponse, new TowerChallengeResponse()); public RpcResult OnTowerChallengeRequest() => Response(MessageId.TowerChallengeResponse, new TowerChallengeResponse());
[NetEvent(MessageId.CycleTowerChallengeRequest)] [NetEvent(MessageId.CycleTowerChallengeRequest)]
public ResponseMessage OnCycleTowerChallengeRequest() => Response(MessageId.CycleTowerChallengeResponse, new CycleTowerChallengeResponse()); public RpcResult OnCycleTowerChallengeRequest() => Response(MessageId.CycleTowerChallengeResponse, new CycleTowerChallengeResponse());
} }

View file

@ -12,7 +12,7 @@ internal class TutorialController : Controller
} }
[NetEvent(MessageId.TutorialInfoRequest)] [NetEvent(MessageId.TutorialInfoRequest)]
public ResponseMessage OnTutorialInfoRequest() public RpcResult OnTutorialInfoRequest()
{ {
int[] tutorials = [30001, 30002, 30003, 30004, 30005, 30006, 30007, 30011, 30012, 30008, 30009, 30010, 30013, 30014, 30015, 30016, 30017, 30018, 30019, 30020, 30021, 30022, 30023, 30024, 40001, 30025, 30026, 30027, 30028, 30029, 30030, 30031, 30032, 30033, 30034, 30035, 30036, 50001, 50002, 50003, 50004, 50005, 50006, 50007, 50008, 50009, 50010, 50011, 33001, 34017, 34018, 32001, 32002, 32003, 32004, 32005, 32006, 32007, 32008, 32009, 32010, 32011, 32012, 32013, 32014, 32015, 32016, 32017, 32018, 32019, 32020, 32021, 33002, 33003, 33004, 33005, 34001, 34002, 34003, 34004, 34005, 34006, 34007, 34008, 34009, 34010, 34011, 34012, 34013, 34014, 34015, 34016, 34019, 34020, 34021, 34022, 34023, 34024, 34025, 34027, 34028, 34029, 34030, 34031, 34032, 34033]; int[] tutorials = [30001, 30002, 30003, 30004, 30005, 30006, 30007, 30011, 30012, 30008, 30009, 30010, 30013, 30014, 30015, 30016, 30017, 30018, 30019, 30020, 30021, 30022, 30023, 30024, 40001, 30025, 30026, 30027, 30028, 30029, 30030, 30031, 30032, 30033, 30034, 30035, 30036, 50001, 50002, 50003, 50004, 50005, 50006, 50007, 50008, 50009, 50010, 50011, 33001, 34017, 34018, 32001, 32002, 32003, 32004, 32005, 32006, 32007, 32008, 32009, 32010, 32011, 32012, 32013, 32014, 32015, 32016, 32017, 32018, 32019, 32020, 32021, 33002, 33003, 33004, 33005, 34001, 34002, 34003, 34004, 34005, 34006, 34007, 34008, 34009, 34010, 34011, 34012, 34013, 34014, 34015, 34016, 34019, 34020, 34021, 34022, 34023, 34024, 34025, 34027, 34028, 34029, 34030, 34031, 34032, 34033];
TutorialInfoResponse rsp = new(); TutorialInfoResponse rsp = new();
@ -30,7 +30,7 @@ internal class TutorialController : Controller
} }
[NetEvent(MessageId.GetDetectionLabelInfoRequest)] [NetEvent(MessageId.GetDetectionLabelInfoRequest)]
public ResponseMessage OnGetDetectionLabelInfoRequest() public RpcResult OnGetDetectionLabelInfoRequest()
{ {
int[] guides = [0, 1, 2, 3, 14, 15, 16, 4, 21, 22, 7, 5, 18, 6, 61, 8, 9, 10, 11, 12, 13, 17, 19]; int[] guides = [0, 1, 2, 3, 14, 15, 16, 4, 21, 22, 7, 5, 18, 6, 61, 8, 9, 10, 11, 12, 13, 17, 19];
int[] detectionTexts = [1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 61]; int[] detectionTexts = [1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 61];
@ -43,7 +43,7 @@ internal class TutorialController : Controller
} }
[NetEvent(MessageId.GuideInfoRequest)] [NetEvent(MessageId.GuideInfoRequest)]
public ResponseMessage OnGuideInfoRequest() => Response(MessageId.GuideInfoResponse, new GuideInfoResponse() public RpcResult OnGuideInfoRequest() => Response(MessageId.GuideInfoResponse, new GuideInfoResponse()
{ {
GuideGroupFinishList = { 60001, 60002, 60003, 60004, 60005, 60006, 60007, 60008, 60009, 60010, 60011, 60012, 60013, 60014, 60015, 60016, 60017, 60018, 60019, 60020, 60021, 60101, 60102, 60103, 62002, 62004, 62005, 62006, 62007, 62009, 62010, 62011, 62012, 62013, 62014, 62015, 62016, 62017, 62022, 62027, 62028, 62029, 62030, 62031, 62032, 62033, 62034, 62036, 65001, 67001, 67002, 67003, 67004, 67005, 67006, 67007, 67008, 67009, 67010, 67011, 67012, 67013, 67014, 67015, 67016, 67017, 67018, 67019, 67022, 62001, 62008, 62018, 62019, 62020, 62021, 62023, 62024, 62025, 62026, 62035, 65002, 65003, 65004, 65005 } GuideGroupFinishList = { 60001, 60002, 60003, 60004, 60005, 60006, 60007, 60008, 60009, 60010, 60011, 60012, 60013, 60014, 60015, 60016, 60017, 60018, 60019, 60020, 60021, 60101, 60102, 60103, 62002, 62004, 62005, 62006, 62007, 62009, 62010, 62011, 62012, 62013, 62014, 62015, 62016, 62017, 62022, 62027, 62028, 62029, 62030, 62031, 62032, 62033, 62034, 62036, 65001, 67001, 67002, 67003, 67004, 67005, 67006, 67007, 67008, 67009, 67010, 67011, 67012, 67013, 67014, 67015, 67016, 67017, 67018, 67019, 67022, 62001, 62008, 62018, 62019, 62020, 62021, 62023, 62024, 62025, 62026, 62035, 65002, 65003, 65004, 65005 }
}); });

View file

@ -19,14 +19,14 @@ internal class WorldController : Controller
} }
[NetEvent(MessageId.EntityOnLandedRequest)] [NetEvent(MessageId.EntityOnLandedRequest)]
public ResponseMessage OnEntityOnLandedRequest() => Response(MessageId.EntityOnLandedResponse, new EntityOnLandedResponse()); public RpcResult OnEntityOnLandedRequest() => Response(MessageId.EntityOnLandedResponse, new EntityOnLandedResponse());
[NetEvent(MessageId.PlayerMotionRequest)] [NetEvent(MessageId.PlayerMotionRequest)]
public ResponseMessage OnPlayerMotionRequest() => Response(MessageId.PlayerMotionResponse, new PlayerMotionResponse()); public RpcResult OnPlayerMotionRequest() => Response(MessageId.PlayerMotionResponse, new PlayerMotionResponse());
[NetEvent(MessageId.EntityLoadCompleteRequest)] [NetEvent(MessageId.EntityLoadCompleteRequest)]
public ResponseMessage OnEntityLoadCompleteRequest() => Response(MessageId.EntityLoadCompleteResponse, new EntityLoadCompleteResponse()); public RpcResult OnEntityLoadCompleteRequest() => Response(MessageId.EntityLoadCompleteResponse, new EntityLoadCompleteResponse());
[NetEvent(MessageId.UpdateSceneDateRequest)] [NetEvent(MessageId.UpdateSceneDateRequest)]
public ResponseMessage OnUpdateSceneDateRequest() => Response(MessageId.UpdateSceneDateResponse, new UpdateSceneDateResponse()); public RpcResult OnUpdateSceneDateRequest() => Response(MessageId.UpdateSceneDateResponse, new UpdateSceneDateResponse());
} }

View file

@ -1,6 +1,9 @@
using GameServer.Controllers.Attributes; using GameServer.Controllers.Attributes;
using GameServer.Network; using GameServer.Network;
using GameServer.Network.Messages; using GameServer.Network.Messages;
using GameServer.Settings;
using GameServer.Systems.Entity;
using Microsoft.Extensions.Options;
using Protocol; using Protocol;
namespace GameServer.Controllers; namespace GameServer.Controllers;
@ -12,9 +15,56 @@ internal class WorldMapController : Controller
} }
[NetEvent(MessageId.MapTraceInfoRequest)] [NetEvent(MessageId.MapTraceInfoRequest)]
public ResponseMessage OnMapTraceInfoRequest() => Response(MessageId.MapTraceInfoResponse, new MapTraceInfoResponse() public RpcResult OnMapTraceInfoRequest() => Response(MessageId.MapTraceInfoResponse, new MapTraceInfoResponse()
{ {
// Don't. // Don't.
//MarkIdList = { 1, 2, 3, 1000, 1001, 1002, 1003, 1004, 1005, 1007, 1008, 1009, 3000, 3002, 3003, 3005, 3010, 3011, 3012, 4020, 4021, 4022, 4023, 5001, 5002, 5003, 5004, 5005, 5006, 5007, 5021, 5022, 5023, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 380000, 380002, 380003, 380004, 380006, 380007, 380015, 301203, 380013, 380014, 301204, 300201, 300202, 300203, 300301, 300302, 300303, 300304, 300306, 300309, 300310, 300311, 300312, 300313, 300401, 300402, 300403, 300404, 300405, 300406, 300407, 300408, 300410, 300413, 300501, 300502, 300506, 300507, 300508, 300509, 300510, 300511, 300601, 300603, 300604, 300605, 300606, 300607, 300608, 300701, 300703, 300704, 300707, 300708, 300711, 300712, 300713, 300901, 300902, 300911, 300912, 300914, 300915, 300918, 301001, 301003, 301004, 301005, 301006, 301007, 301008, 301009, 301010, 301012, 301013, 301014, 301015, 10000, 10001, 10002, 10003, 10005, 10006, 300801, 301201, 300412, 3015, 3016, 3017, 300411 } //MarkIdList = { 1, 2, 3, 1000, 1001, 1002, 1003, 1004, 1005, 1007, 1008, 1009, 3000, 3002, 3003, 3005, 3010, 3011, 3012, 4020, 4021, 4022, 4023, 5001, 5002, 5003, 5004, 5005, 5006, 5007, 5021, 5022, 5023, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 380000, 380002, 380003, 380004, 380006, 380007, 380015, 301203, 380013, 380014, 301204, 300201, 300202, 300203, 300301, 300302, 300303, 300304, 300306, 300309, 300310, 300311, 300312, 300313, 300401, 300402, 300403, 300404, 300405, 300406, 300407, 300408, 300410, 300413, 300501, 300502, 300506, 300507, 300508, 300509, 300510, 300511, 300601, 300603, 300604, 300605, 300606, 300607, 300608, 300701, 300703, 300704, 300707, 300708, 300711, 300712, 300713, 300901, 300902, 300911, 300912, 300914, 300915, 300918, 301001, 301003, 301004, 301005, 301006, 301007, 301008, 301009, 301010, 301012, 301013, 301014, 301015, 10000, 10001, 10002, 10003, 10005, 10006, 300801, 301201, 300412, 3015, 3016, 3017, 300411 }
}); });
[NetEvent(MessageId.MapUnlockFieldInfoRequest)]
public RpcResult OnMapUnlockFieldInfoRequest() => Response(MessageId.MapUnlockFieldInfoResponse, new MapUnlockFieldInfoResponse
{
FieldId = { Enumerable.Range(1, 12) }
});
[NetEvent(MessageId.MapMarkRequest)]
public async Task<RpcResult> OnMapMarkRequest(MapMarkRequest request, IOptions<GameplayFeatureSettings> gameplayFeatures, CreatureController creatureController)
{
if (gameplayFeatures.Value.TeleportByMapMark)
{
PlayerEntity? entity = creatureController.GetPlayerEntity();
if (entity != null)
{
await Session.Push(MessageId.TeleportNotify, new TeleportNotify
{
PosX = request.MarkPointRequestInfo.PosX * 100,
PosY = request.MarkPointRequestInfo.PosY * 100,
PosZ = request.MarkPointRequestInfo.PosZ * 100,
PosA = 0,
MapId = 8,
Reason = (int)TeleportReason.Gm,
TransitionOption = new TransitionOptionPb
{
TransitionType = (int)TransitionType.Empty
}
});
}
}
return Response(MessageId.MapMarkResponse, new MapMarkResponse
{
Info = new MarkPointInfo
{
PosX = request.MarkPointRequestInfo.PosX,
PosY = request.MarkPointRequestInfo.PosY,
PosZ = request.MarkPointRequestInfo.PosZ,
ConfigId = request.MarkPointRequestInfo.ConfigId,
MapId = request.MarkPointRequestInfo.MapId,
MarkId = 1,
MarkInfo = request.MarkPointRequestInfo.MarkInfo,
MarkType = request.MarkPointRequestInfo.MarkType
}
});
}
} }

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,6 @@
using System.Reflection; using System.Reflection;
using GameServer.Controllers; using GameServer.Controllers;
using GameServer.Controllers.Attributes;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace GameServer.Extensions; namespace GameServer.Extensions;
@ -17,4 +18,17 @@ internal static class ServiceCollectionExtensions
return services; return services;
} }
public static IServiceCollection AddCommands(this IServiceCollection services)
{
IEnumerable<Type> handlerTypes = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetCustomAttribute<ChatCommandCategoryAttribute>() != null);
foreach (Type type in handlerTypes)
{
services.AddScoped(type);
}
return services;
}
} }

View file

@ -5,13 +5,19 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="icon.ico" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
<ProjectReference Include="..\KcpSharp\KcpSharp.csproj" /> <ProjectReference Include="..\KcpSharp\KcpSharp.csproj" />
<ProjectReference Include="..\Protocol\Protocol.csproj" /> <ProjectReference Include="..\Protocol\Protocol.csproj" />
</ItemGroup> </ItemGroup>

View file

@ -0,0 +1,32 @@
using Google.Protobuf;
using Protocol;
namespace GameServer.Models.Chat;
internal class ChatRoom
{
private readonly List<ChatContentProto> _messages;
private int _msgIdCounter;
public int TargetUid { get; }
public ChatRoom(int targetUid)
{
TargetUid = targetUid;
_messages = [];
}
public IEnumerable<ChatContentProto> ChatHistory => _messages;
public void AddMessage(int senderId, int contentType, string content)
{
_messages.Add(new ChatContentProto
{
SenderUid = senderId,
ChatContentType = contentType,
Content = content,
MsgId = NextMessageId().ToString()
});
}
private int NextMessageId() => Interlocked.Increment(ref _msgIdCounter);
}

View file

@ -0,0 +1,29 @@
using GameServer.Models.Chat;
namespace GameServer.Models;
internal class ChatModel
{
private readonly Dictionary<int, ChatRoom> _rooms;
public ChatModel()
{
_rooms = [];
}
/// <summary>
/// Gets chat room for specified player id.
/// Creates new one if it doesn't exist.
/// </summary>
public ChatRoom GetChatRoom(int id)
{
if (!_rooms.TryGetValue(id, out ChatRoom? chatRoom))
{
chatRoom = new ChatRoom(id);
_rooms[id] = chatRoom;
}
return chatRoom;
}
public IEnumerable<ChatRoom> AllChatRooms => _rooms.Values;
}

View file

@ -0,0 +1,18 @@
namespace GameServer.Models;
internal class FormationModel
{
public int[] RoleIds { get; }
public FormationModel()
{
RoleIds = new int[3];
}
public void Set(int[] roleIds)
{
for (int i = 0; i < RoleIds.Length; i++)
{
RoleIds[i] = roleIds[i];
}
}
}

View file

@ -0,0 +1,34 @@
using Core.Config;
using Protocol;
namespace GameServer.Models;
internal class InventoryModel
{
private int _itemIncrId;
public List<WeaponItem> WeaponList { get; } = [];
public WeaponItem? GetEquippedWeapon(int roleId)
{
return WeaponList.SingleOrDefault(weapon => weapon.RoleId == roleId);
}
public WeaponItem? GetWeaponById(int incrId)
{
return WeaponList.SingleOrDefault(weapon => weapon.IncrId == incrId);
}
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)]
@ -21,9 +24,16 @@ internal class ModelManager
{ {
_playerModel = PlayerModel.CreateDefaultPlayer(_playerStartingValues.Value); _playerModel = PlayerModel.CreateDefaultPlayer(_playerStartingValues.Value);
_creatureModel = new CreatureModel(_playerModel.Id); _creatureModel = new CreatureModel(_playerModel.Id);
Formation.Set(_playerStartingValues.Value.Characters);
} }
public PlayerModel Player => _playerModel ?? throw new InvalidOperationException($"Trying to access {nameof(PlayerModel)} instance before initialization!"); public PlayerModel Player => _playerModel ?? throw new InvalidOperationException($"Trying to access {nameof(PlayerModel)} instance before initialization!");
public CreatureModel Creature => _creatureModel ?? throw new InvalidOperationException($"Trying to access {nameof(CreatureModel)} instance before initialization!"); 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 InventoryModel Inventory { get; } = new();
public ChatModel Chat { get; } = new();
} }

View file

@ -1,4 +1,5 @@
using GameServer.Settings; using Protocol;
using GameServer.Settings;
namespace GameServer.Models; namespace GameServer.Models;
internal class PlayerModel internal class PlayerModel
@ -6,11 +7,13 @@ internal class PlayerModel
public int Id { get; private set; } public int Id { get; private set; }
public string Name { get; private set; } public string Name { get; private set; }
public int[] Characters { get; private set; } public int[] Characters { get; private set; }
public Vector Position { get; private set; }
public PlayerModel() public PlayerModel()
{ {
Name = string.Empty; Name = string.Empty;
Characters = []; Characters = [];
Position = new Vector();
} }
public static PlayerModel CreateDefaultPlayer(PlayerStartingValues startingValues) public static PlayerModel CreateDefaultPlayer(PlayerStartingValues startingValues)
@ -19,7 +22,8 @@ internal class PlayerModel
{ {
Id = 1337, Id = 1337,
Name = startingValues.Name, Name = startingValues.Name,
Characters = startingValues.Characters Characters = startingValues.Characters,
Position = startingValues.Position.Clone()
}; };
} }
} }

View file

@ -0,0 +1,170 @@
using Core.Config;
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);
}
private static List<ArrayIntInt> CreateBasePropList(BasePropertyConfig? config)
{
List<ArrayIntInt> baseProp = [];
if (config == null) return baseProp;
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.Life, Value = config.Life });
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.SheildDamageReduce, Value = config.SheildDamageReduce });
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.CritDamage, Value = config.CritDamage });
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.CdReduse, Value = config.CdReduse });
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.DamageChange, Value = config.DamageChange });
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.DamageChangeCast, Value = config.DamageChangeCast });
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.DamageChangePhys, Value = config.DamageChangePhys });
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.DamageChangeElement3, Value = config.DamageChangeElement3 });
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.DamageChangeElement6, Value = config.DamageChangeElement6 });
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.DamageResistanceElement2, Value = config.DamageResistanceElement2 });
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.DamageResistanceElement5, Value = config.DamageResistanceElement5 });
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.HealedChange, Value = config.HealedChange });
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.DamageReduceElement2, Value = config.DamageReduceElement2 });
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.DamageReduceElement5, Value = config.DamageReduceElement5 });
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.ReactionChange2, Value = config.ReactionChange2 });
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.ReactionChange5, Value = config.ReactionChange5 });
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.ReactionChange8, Value = config.ReactionChange8 });
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.ReactionChange11, Value = config.ReactionChange11 });
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.ReactionChange14, Value = config.ReactionChange14 });
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.Energy, Value = config.Energy });
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.SpecialEnergy2Max, Value = config.SpecialEnergy2Max });
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.SpecialEnergy3, Value = config.SpecialEnergy3 });
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.StrengthMax, Value = config.StrengthMax });
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.StrengthPunishTime, Value = config.StrengthPunishTime });
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.StrengthFastSwim, Value = config.StrengthFastSwim });
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.HardnessMax, Value = config.HardnessMax });
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.HardnessPunishTime, Value = config.HardnessPunishTime });
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.RageMax, Value = config.RageMax });
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.RagePunishTime, Value = config.RagePunishTime });
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.ToughMax, Value = config.ToughMax });
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.ToughChange, Value = config.ToughChange });
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.ElementPower1, Value = config.ElementPower1 });
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.ElementPower4, Value = config.ElementPower4 });
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.SpecialDamageChange, Value = config.SpecialDamageChange });
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.WeakTime, Value = config.WeakTime });
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.IgnoreDamageResistanceElement1, Value = config.IgnoreDamageResistanceElement1 });
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.IgnoreDamageResistanceElement4, Value = config.IgnoreDamageResistanceElement4 });
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.SkillToughRatio, Value = config.SkillToughRatio });
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.Mass, Value = config.Mass });
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.SpeedRatio, Value = config.SpeedRatio });
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.CastAttackSpeed, Value = config.CastAttackSpeed });
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.StatusBuildUp2Max, Value = config.StatusBuildUp2Max });
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.StatusBuildUp3, Value = config.StatusBuildUp3 });
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.StatusBuildUp5Max, Value = config.StatusBuildUp5Max });
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.ParalysisTime, Value = config.ParalysisTime });
baseProp.Add(new() { Key = (int)EAttributeType.ParalysisTimeRecover, Value = config.ParalysisTimeRecover });
return baseProp;
}
}

View file

@ -4,7 +4,7 @@ using Protocol;
namespace GameServer.Network.Messages; namespace GameServer.Network.Messages;
internal delegate Task PushHandler(IServiceProvider serviceProvider, ReadOnlySpan<byte> data); internal delegate Task PushHandler(IServiceProvider serviceProvider, ReadOnlySpan<byte> data);
internal delegate Task<ResponseMessage> RpcHandler(IServiceProvider serviceProvider, ReadOnlySpan<byte> data); internal delegate Task<RpcResult> RpcHandler(IServiceProvider serviceProvider, ReadOnlySpan<byte> data);
internal class MessageManager internal class MessageManager
{ {
private readonly EventHandlerFactory _handlerFactory; private readonly EventHandlerFactory _handlerFactory;
@ -16,7 +16,7 @@ internal class MessageManager
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
public async Task<ResponseMessage?> ExecuteRpc(MessageId messageId, ReadOnlyMemory<byte> data) public async Task<RpcResult?> ExecuteRpc(MessageId messageId, ReadOnlyMemory<byte> data)
{ {
RpcHandler? handler = _handlerFactory.GetRpcHandler(messageId); RpcHandler? handler = _handlerFactory.GetRpcHandler(messageId);
if (handler != null) if (handler != null)

View file

@ -1,4 +1,5 @@
using GameServer.Network.Messages; using GameServer.Network.Messages;
using GameServer.Systems.Event;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace GameServer.Network.Rpc; namespace GameServer.Network.Rpc;
@ -7,26 +8,33 @@ internal class RpcManager
private readonly IRpcEndPoint _endPoint; private readonly IRpcEndPoint _endPoint;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly MessageManager _messageManager; private readonly MessageManager _messageManager;
private readonly EventSystem _eventSystem;
public RpcManager(MessageManager messageManager, IRpcEndPoint endPoint, ILogger<RpcManager> logger) public RpcManager(MessageManager messageManager, IRpcEndPoint endPoint, ILogger<RpcManager> logger, EventSystem eventSystem)
{ {
_endPoint = endPoint; _endPoint = endPoint;
_logger = logger; _logger = logger;
_messageManager = messageManager; _messageManager = messageManager;
_eventSystem = eventSystem;
} }
public async Task Execute(RequestMessage request) public async Task Execute(RequestMessage request)
{ {
ResponseMessage? response = await _messageManager.ExecuteRpc(request.MessageId, request.Payload); RpcResult? result = await _messageManager.ExecuteRpc(request.MessageId, request.Payload);
if (response == null) if (result == null)
{ {
_logger.LogWarning("Rpc was not handled properly (message: {msg_id}, id: {rpc_id})", request.MessageId, request.RpcID); _logger.LogWarning("Rpc was not handled properly (message: {msg_id}, id: {rpc_id})", request.MessageId, request.RpcID);
return; return;
} }
response.RpcID = request.RpcID; result.Response.RpcID = request.RpcID;
await _endPoint.SendRpcResult(response); await _endPoint.SendRpcResult(result.Response);
_logger.LogInformation("Rpc with id {rpc_id} was handled, return message: {msg_id}", request.RpcID, response.MessageId); foreach (GameEventType postEvent in result.PostEvents)
{
await _eventSystem.Emit(postEvent);
}
_logger.LogInformation("Rpc with id {rpc_id} was handled, return message: {msg_id}", request.RpcID, result.Response.MessageId);
} }
} }

View file

@ -1,4 +1,8 @@
using GameServer.Controllers.Factory; using Core.Config;
using Core.Extensions;
using GameServer.Controllers.ChatCommands;
using GameServer.Controllers.Combat;
using GameServer.Controllers.Factory;
using GameServer.Controllers.Manager; using GameServer.Controllers.Manager;
using GameServer.Extensions; using GameServer.Extensions;
using GameServer.Models; using GameServer.Models;
@ -20,20 +24,38 @@ internal static class Program
{ {
private static async Task Main(string[] args) private static async Task Main(string[] args)
{ {
Console.Title = "Wuthering Waves | Game Server";
Console.WriteLine(" __ __ __ .__ .__ __ __ \r\n/ \\ / \\__ ___/ |_| |__ ___________|__| ____ ____ / \\ / \\_____ ___ __ ____ ______\r\n\\ \\/\\/ / | \\ __\\ | \\_/ __ \\_ __ \\ |/ \\ / ___\\ \\ \\/\\/ /\\__ \\\\ \\/ // __ \\ / ___/\r\n \\ /| | /| | | Y \\ ___/| | \\/ | | \\/ /_/ > \\ / / __ \\\\ /\\ ___/ \\___ \\ \r\n \\__/\\ / |____/ |__| |___| /\\___ >__| |__|___| /\\___ / \\__/\\ / (____ /\\_/ \\___ >____ >\r\n \\/ \\/ \\/ \\//_____/ \\/ \\/ \\/ \\/ \r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\tGame Server\n");
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(); builder.Logging.AddConsole();
builder.SetupConfiguration(); builder.SetupConfiguration();
builder.Services.AddControllers() builder.Services.UseLocalResources()
.AddControllers()
.AddCommands()
.AddSingleton<ConfigManager>()
.AddSingleton<KcpGateway>().AddScoped<PlayerSession>() .AddSingleton<KcpGateway>().AddScoped<PlayerSession>()
.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<EntityFactory>()
.AddScoped<ModelManager>().AddScoped<ControllerManager>() .AddScoped<ModelManager>().AddScoped<ControllerManager>()
.AddScoped<CombatManager>().AddScoped<ChatCommandManager>()
.AddHostedService<WWGameServer>(); .AddHostedService<WWGameServer>();
await builder.Build().RunAsync(); IHost host = builder.Build();
ILogger logger = host.Services.GetRequiredService<ILoggerFactory>().CreateLogger("WutheringWaves");
logger.LogInformation("Support: discord.gg/reversedrooms or discord.xeondev.com");
logger.LogInformation("Preparing server...");
host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStarted.Register(() =>
{
logger.LogInformation("Server started! Let's play Wuthering Waves!");
});
await host.RunAsync();
} }
private static void SetupConfiguration(this HostApplicationBuilder builder) private static void SetupConfiguration(this HostApplicationBuilder builder)
@ -41,5 +63,6 @@ internal static class Program
builder.Configuration.AddJsonFile("gameplay.json"); builder.Configuration.AddJsonFile("gameplay.json");
builder.Services.Configure<GatewaySettings>(builder.Configuration.GetRequiredSection("Gateway")); builder.Services.Configure<GatewaySettings>(builder.Configuration.GetRequiredSection("Gateway"));
builder.Services.Configure<PlayerStartingValues>(builder.Configuration.GetRequiredSection("StartingValues")); builder.Services.Configure<PlayerStartingValues>(builder.Configuration.GetRequiredSection("StartingValues"));
builder.Services.Configure<GameplayFeatureSettings>(builder.Configuration.GetRequiredSection("Features"));
} }
} }

View file

@ -0,0 +1,5 @@
namespace GameServer.Settings;
internal class GameplayFeatureSettings
{
public bool TeleportByMapMark { get; set; }
}

View file

@ -1,6 +1,9 @@
namespace GameServer.Settings; using Protocol;
namespace GameServer.Settings;
internal class PlayerStartingValues internal class PlayerStartingValues
{ {
public required string Name { get; set; } public required string Name { get; set; }
public required int[] Characters { get; set; } public required int[] Characters { 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))
@ -37,6 +45,7 @@ internal class EntityAttributeComponent : EntityComponentBase
} }
attribute.CurrentValue = currentValue; attribute.CurrentValue = currentValue;
attribute.BaseValue = currentValue;
} }
public int GetAttribute(EAttributeType type) public int GetAttribute(EAttributeType type)

View file

@ -1,4 +1,5 @@
using Protocol; using System.Diagnostics.CodeAnalysis;
using Protocol;
namespace GameServer.Systems.Entity.Component; namespace GameServer.Systems.Entity.Component;
internal class EntityComponentSystem internal class EntityComponentSystem
@ -25,7 +26,7 @@ internal class EntityComponentSystem
return (_components.Single(component => component is TEntityComponent) as TEntityComponent)!; return (_components.Single(component => component is TEntityComponent) as TEntityComponent)!;
} }
public bool TryGet<TEntityComponent>(out TEntityComponent? component) where TEntityComponent : EntityComponentBase public bool TryGet<TEntityComponent>([NotNullWhen(true)] out TEntityComponent? component) where TEntityComponent : EntityComponentBase
{ {
return (component = _components.SingleOrDefault(component => component is TEntityComponent) as TEntityComponent) != null; return (component = _components.SingleOrDefault(component => component is TEntityComponent) as TEntityComponent) != null;
} }

View file

@ -0,0 +1,22 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityEquipComponent : EntityComponentBase
{
public int WeaponId { get; set; }
public EntityEquipComponent()
{
// EntityEquipComponent.
}
public override EntityComponentType Type => EntityComponentType.Equip;
public override EntityComponentPb Pb => new()
{
EquipComponent = new EquipComponentPb
{
WeaponId = WeaponId
}
};
}

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

@ -0,0 +1,22 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityFsmComponent : EntityComponentBase
{
public List<DFsm> Fsms { get; } = [];
public int CommonHashCode { get; set; }
public override EntityComponentType Type => EntityComponentType.EntityFsm;
public override EntityComponentPb Pb => new()
{
EntityFsmComponentPb = new()
{
Fsms =
{
Fsms
},
CommonHashCode = CommonHashCode
},
};
}

View file

@ -0,0 +1,22 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityLogicStateComponent : EntityComponentBase
{
public List<int> States { get; set; }
public EntityLogicStateComponent()
{
States = [];
}
public override EntityComponentType Type => EntityComponentType.LogicState;
public override EntityComponentPb Pb => new()
{
LogicStateComponentPb = new()
{
States = { States }
}
};
}

View file

@ -0,0 +1,17 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntityMonsterAiComponent : EntityComponentBase
{
public override EntityComponentType Type => EntityComponentType.MonsterAi;
public int AiTeamInitId { get; set; }
public override EntityComponentPb Pb => new()
{
MonsterAiComponentPb = new MonsterAiComponentPb
{
AiTeamInitId = AiTeamInitId,
}
};
}

View file

@ -0,0 +1,25 @@
using Protocol;
namespace GameServer.Systems.Entity.Component;
internal class EntitySummonerComponent : EntityComponentBase
{
public int SummonConfigId { get; set; }
public ESummonType SummonType { get; set; }
public long SummonerId { get; set; }
public int PlayerId { get; set; }
public int SummonSkillId { get; set; }
public override EntityComponentType Type => EntityComponentType.Summoner;
public override EntityComponentPb Pb => new()
{
SummonerComponent = new()
{
SummonCfgId = SummonConfigId,
Type = (int)SummonType,
SummonerId = SummonerId,
PlayerId = PlayerId,
SummonSkillId = SummonSkillId
}
};
}

View file

@ -1,4 +1,5 @@
using GameServer.Systems.Entity.Component; using Core.Config;
using GameServer.Systems.Entity.Component;
using Protocol; using Protocol;
namespace GameServer.Systems.Entity; namespace GameServer.Systems.Entity;
@ -27,6 +28,9 @@ internal abstract class EntityBase
public virtual void OnCreate() public virtual void OnCreate()
{ {
State = EntityState.Born; State = EntityState.Born;
_ = ComponentSystem.Create<EntityLogicStateComponent>();
_ = ComponentSystem.Create<EntityFightBuffComponent>();
} }
public virtual void Activate() public virtual void Activate()
@ -41,4 +45,146 @@ internal abstract class EntityBase
public abstract EntityConfigType ConfigType { get; } public abstract EntityConfigType ConfigType { get; }
public abstract EntityPb Pb { get; } public abstract EntityPb Pb { get; }
public void InitProps(BasePropertyConfig config)
{
EntityAttributeComponent attributeComponent = ComponentSystem.Get<EntityAttributeComponent>();
attributeComponent.SetAttribute(EAttributeType.Lv, config.Lv);
attributeComponent.SetAttribute(EAttributeType.LifeMax, config.LifeMax);
attributeComponent.SetAttribute(EAttributeType.Life, config.Life);
attributeComponent.SetAttribute(EAttributeType.Sheild, config.Sheild);
attributeComponent.SetAttribute(EAttributeType.SheildDamageChange, config.SheildDamageChange);
attributeComponent.SetAttribute(EAttributeType.SheildDamageReduce, config.SheildDamageReduce);
attributeComponent.SetAttribute(EAttributeType.Atk, config.Atk);
attributeComponent.SetAttribute(EAttributeType.Crit, config.Crit);
attributeComponent.SetAttribute(EAttributeType.CritDamage, config.CritDamage);
attributeComponent.SetAttribute(EAttributeType.Def, config.Def);
attributeComponent.SetAttribute(EAttributeType.EnergyEfficiency, config.EnergyEfficiency);
attributeComponent.SetAttribute(EAttributeType.CdReduse, config.CdReduse);
attributeComponent.SetAttribute(EAttributeType.ReactionEfficiency, config.ReactionEfficiency);
attributeComponent.SetAttribute(EAttributeType.DamageChangeNormalSkill, config.DamageChangeNormalSkill);
attributeComponent.SetAttribute(EAttributeType.DamageChange, config.DamageChange);
attributeComponent.SetAttribute(EAttributeType.DamageReduce, config.DamageReduce);
attributeComponent.SetAttribute(EAttributeType.DamageChangeAuto, config.DamageChangeAuto);
attributeComponent.SetAttribute(EAttributeType.DamageChangeCast, config.DamageChangeCast);
attributeComponent.SetAttribute(EAttributeType.DamageChangeUltra, config.DamageChangeUltra);
attributeComponent.SetAttribute(EAttributeType.DamageChangeQte, config.DamageChangeQte);
attributeComponent.SetAttribute(EAttributeType.DamageChangePhys, config.DamageChangePhys);
attributeComponent.SetAttribute(EAttributeType.DamageChangeElement1, config.DamageChangeElement1);
attributeComponent.SetAttribute(EAttributeType.DamageChangeElement2, config.DamageChangeElement2);
attributeComponent.SetAttribute(EAttributeType.DamageChangeElement3, config.DamageChangeElement3);
attributeComponent.SetAttribute(EAttributeType.DamageChangeElement4, config.DamageChangeElement4);
attributeComponent.SetAttribute(EAttributeType.DamageChangeElement5, config.DamageChangeElement5);
attributeComponent.SetAttribute(EAttributeType.DamageChangeElement6, config.DamageChangeElement6);
attributeComponent.SetAttribute(EAttributeType.DamageResistancePhys, config.DamageResistancePhys);
attributeComponent.SetAttribute(EAttributeType.DamageResistanceElement1, config.DamageResistanceElement1);
attributeComponent.SetAttribute(EAttributeType.DamageResistanceElement2, config.DamageResistanceElement2);
attributeComponent.SetAttribute(EAttributeType.DamageResistanceElement3, config.DamageResistanceElement3);
attributeComponent.SetAttribute(EAttributeType.DamageResistanceElement4, config.DamageResistanceElement4);
attributeComponent.SetAttribute(EAttributeType.DamageResistanceElement5, config.DamageResistanceElement5);
attributeComponent.SetAttribute(EAttributeType.DamageResistanceElement6, config.DamageResistanceElement6);
attributeComponent.SetAttribute(EAttributeType.HealChange, config.HealChange);
attributeComponent.SetAttribute(EAttributeType.HealedChange, config.HealedChange);
attributeComponent.SetAttribute(EAttributeType.DamageReducePhys, config.DamageReducePhys);
attributeComponent.SetAttribute(EAttributeType.DamageReduceElement1, config.DamageReduceElement1);
attributeComponent.SetAttribute(EAttributeType.DamageReduceElement2, config.DamageReduceElement2);
attributeComponent.SetAttribute(EAttributeType.DamageReduceElement3, config.DamageReduceElement3);
attributeComponent.SetAttribute(EAttributeType.DamageReduceElement4, config.DamageReduceElement4);
attributeComponent.SetAttribute(EAttributeType.DamageReduceElement5, config.DamageReduceElement5);
attributeComponent.SetAttribute(EAttributeType.DamageReduceElement6, config.DamageReduceElement6);
attributeComponent.SetAttribute(EAttributeType.ReactionChange1, config.ReactionChange1);
attributeComponent.SetAttribute(EAttributeType.ReactionChange2, config.ReactionChange2);
attributeComponent.SetAttribute(EAttributeType.ReactionChange3, config.ReactionChange3);
attributeComponent.SetAttribute(EAttributeType.ReactionChange4, config.ReactionChange4);
attributeComponent.SetAttribute(EAttributeType.ReactionChange5, config.ReactionChange5);
attributeComponent.SetAttribute(EAttributeType.ReactionChange6, config.ReactionChange6);
attributeComponent.SetAttribute(EAttributeType.ReactionChange7, config.ReactionChange7);
attributeComponent.SetAttribute(EAttributeType.ReactionChange8, config.ReactionChange8);
attributeComponent.SetAttribute(EAttributeType.ReactionChange9, config.ReactionChange9);
attributeComponent.SetAttribute(EAttributeType.ReactionChange10, config.ReactionChange10);
attributeComponent.SetAttribute(EAttributeType.ReactionChange11, config.ReactionChange11);
attributeComponent.SetAttribute(EAttributeType.ReactionChange12, config.ReactionChange12);
attributeComponent.SetAttribute(EAttributeType.ReactionChange13, config.ReactionChange13);
attributeComponent.SetAttribute(EAttributeType.ReactionChange14, config.ReactionChange14);
attributeComponent.SetAttribute(EAttributeType.ReactionChange15, config.ReactionChange15);
attributeComponent.SetAttribute(EAttributeType.EnergyMax, config.EnergyMax);
attributeComponent.SetAttribute(EAttributeType.Energy, config.Energy);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy1Max, config.SpecialEnergy1Max);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy1, config.SpecialEnergy1);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy2Max, config.SpecialEnergy2Max);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy2, config.SpecialEnergy2);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy3Max, config.SpecialEnergy3Max);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy3, config.SpecialEnergy3);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy4Max, config.SpecialEnergy4Max);
attributeComponent.SetAttribute(EAttributeType.SpecialEnergy4, config.SpecialEnergy4);
attributeComponent.SetAttribute(EAttributeType.StrengthMax, config.StrengthMax);
attributeComponent.SetAttribute(EAttributeType.Strength, config.Strength);
attributeComponent.SetAttribute(EAttributeType.StrengthRecover, config.StrengthRecover);
attributeComponent.SetAttribute(EAttributeType.StrengthPunishTime, config.StrengthPunishTime);
attributeComponent.SetAttribute(EAttributeType.StrengthRun, config.StrengthRun);
attributeComponent.SetAttribute(EAttributeType.StrengthSwim, config.StrengthSwim);
attributeComponent.SetAttribute(EAttributeType.StrengthFastSwim, config.StrengthFastSwim);
attributeComponent.SetAttribute(EAttributeType.StrengthClimb, config.StrengthClimb);
attributeComponent.SetAttribute(EAttributeType.StrengthFastClimb, config.StrengthFastClimb);
attributeComponent.SetAttribute(EAttributeType.HardnessMax, config.HardnessMax);
attributeComponent.SetAttribute(EAttributeType.Hardness, config.Hardness);
attributeComponent.SetAttribute(EAttributeType.HardnessRecover, config.HardnessRecover);
attributeComponent.SetAttribute(EAttributeType.HardnessPunishTime, config.HardnessPunishTime);
attributeComponent.SetAttribute(EAttributeType.HardnessChange, config.HardnessChange);
attributeComponent.SetAttribute(EAttributeType.HardnessReduce, config.HardnessReduce);
attributeComponent.SetAttribute(EAttributeType.RageMax, config.RageMax);
attributeComponent.SetAttribute(EAttributeType.Rage, config.Rage);
attributeComponent.SetAttribute(EAttributeType.RageRecover, config.RageRecover);
attributeComponent.SetAttribute(EAttributeType.RagePunishTime, config.RagePunishTime);
attributeComponent.SetAttribute(EAttributeType.RageChange, config.RageChange);
attributeComponent.SetAttribute(EAttributeType.RageReduce, config.RageReduce);
attributeComponent.SetAttribute(EAttributeType.ToughMax, config.ToughMax);
attributeComponent.SetAttribute(EAttributeType.Tough, config.Tough);
attributeComponent.SetAttribute(EAttributeType.ToughRecover, config.ToughRecover);
attributeComponent.SetAttribute(EAttributeType.ToughChange, config.ToughChange);
attributeComponent.SetAttribute(EAttributeType.ToughReduce, config.ToughReduce);
attributeComponent.SetAttribute(EAttributeType.ToughRecoverDelayTime, config.ToughRecoverDelayTime);
attributeComponent.SetAttribute(EAttributeType.ElementPower1, config.ElementPower1);
attributeComponent.SetAttribute(EAttributeType.ElementPower2, config.ElementPower2);
attributeComponent.SetAttribute(EAttributeType.ElementPower3, config.ElementPower3);
attributeComponent.SetAttribute(EAttributeType.ElementPower4, config.ElementPower4);
attributeComponent.SetAttribute(EAttributeType.ElementPower5, config.ElementPower5);
attributeComponent.SetAttribute(EAttributeType.ElementPower6, config.ElementPower6);
attributeComponent.SetAttribute(EAttributeType.SpecialDamageChange, config.SpecialDamageChange);
attributeComponent.SetAttribute(EAttributeType.StrengthFastClimbCost, config.StrengthFastClimbCost);
attributeComponent.SetAttribute(EAttributeType.ElementPropertyType, config.ElementPropertyType);
attributeComponent.SetAttribute(EAttributeType.WeakTime, config.WeakTime);
attributeComponent.SetAttribute(EAttributeType.IgnoreDefRate, config.IgnoreDefRate);
attributeComponent.SetAttribute(EAttributeType.IgnoreDamageResistancePhys, config.IgnoreDamageResistancePhys);
attributeComponent.SetAttribute(EAttributeType.IgnoreDamageResistanceElement1, config.IgnoreDamageResistanceElement1);
attributeComponent.SetAttribute(EAttributeType.IgnoreDamageResistanceElement2, config.IgnoreDamageResistanceElement2);
attributeComponent.SetAttribute(EAttributeType.IgnoreDamageResistanceElement3, config.IgnoreDamageResistanceElement3);
attributeComponent.SetAttribute(EAttributeType.IgnoreDamageResistanceElement4, config.IgnoreDamageResistanceElement4);
attributeComponent.SetAttribute(EAttributeType.IgnoreDamageResistanceElement5, config.IgnoreDamageResistanceElement5);
attributeComponent.SetAttribute(EAttributeType.IgnoreDamageResistanceElement6, config.IgnoreDamageResistanceElement6);
attributeComponent.SetAttribute(EAttributeType.SkillToughRatio, config.SkillToughRatio);
attributeComponent.SetAttribute(EAttributeType.StrengthClimbJump, config.StrengthClimbJump);
attributeComponent.SetAttribute(EAttributeType.StrengthGliding, config.StrengthGliding);
attributeComponent.SetAttribute(EAttributeType.Mass, config.Mass);
attributeComponent.SetAttribute(EAttributeType.BrakingFrictionFactor, config.BrakingFrictionFactor);
attributeComponent.SetAttribute(EAttributeType.GravityScale, config.GravityScale);
attributeComponent.SetAttribute(EAttributeType.SpeedRatio, config.SpeedRatio);
attributeComponent.SetAttribute(EAttributeType.DamageChangePhantom, config.DamageChangePhantom);
attributeComponent.SetAttribute(EAttributeType.AutoAttackSpeed, config.AutoAttackSpeed);
attributeComponent.SetAttribute(EAttributeType.CastAttackSpeed, config.CastAttackSpeed);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp1Max, config.StatusBuildUp1Max);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp1, config.StatusBuildUp1);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp2Max, config.StatusBuildUp2Max);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp2, config.StatusBuildUp2);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp3Max, config.StatusBuildUp3Max);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp3, config.StatusBuildUp3);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp4Max, config.StatusBuildUp4Max);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp4, config.StatusBuildUp4);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp5Max, config.StatusBuildUp5Max);
attributeComponent.SetAttribute(EAttributeType.StatusBuildUp5, config.StatusBuildUp5);
attributeComponent.SetAttribute(EAttributeType.ParalysisTimeMax, config.ParalysisTimeMax);
attributeComponent.SetAttribute(EAttributeType.ParalysisTime, config.ParalysisTime);
attributeComponent.SetAttribute(EAttributeType.ParalysisTimeRecover, config.ParalysisTimeRecover);
}
} }

View file

@ -6,5 +6,7 @@ internal class EntityFactory
public PlayerEntity CreatePlayer(int characterConfigId, int playerId) public PlayerEntity CreatePlayer(int characterConfigId, int playerId)
=> new(NextId(), characterConfigId, playerId); => new(NextId(), characterConfigId, playerId);
public MonsterEntity CreateMonster(int levelEntityId) => new(NextId(), levelEntityId);
private long NextId() => Interlocked.Increment(ref _entityIdCounter); private long NextId() => Interlocked.Increment(ref _entityIdCounter);
} }

View file

@ -24,6 +24,11 @@ internal class EntitySystem
_entities.Add(entity); _entities.Add(entity);
} }
public void Destroy(EntityBase entity)
{
_ = _entities.Remove(entity);
}
public void Activate(EntityBase entity) public void Activate(EntityBase entity)
{ {
entity.Activate(); entity.Activate();

View file

@ -0,0 +1,73 @@
using GameServer.Systems.Entity.Component;
using Protocol;
namespace GameServer.Systems.Entity;
internal class MonsterEntity : EntityBase
{
public MonsterEntity(long id, int configId) : base(id)
{
ConfigId = configId;
}
public int ConfigId { get; }
public override EEntityType Type => EEntityType.Monster;
public override EntityConfigType ConfigType => EntityConfigType.Level;
public override void OnCreate()
{
base.OnCreate();
EntityAttributeComponent attributeComponent = ComponentSystem.Create<EntityAttributeComponent>();
attributeComponent.SetAttribute(EAttributeType.LifeMax, 100);
attributeComponent.SetAttribute(EAttributeType.Life, 100);
State = EntityState.Born;
EntityMonsterAiComponent aiComponent = ComponentSystem.Create<EntityMonsterAiComponent>();
aiComponent.AiTeamInitId = 100;
EntityFsmComponent fsm = ComponentSystem.Create<EntityFsmComponent>();
fsm.Fsms.Add(new DFsm
{
FsmId = 10007, // Main State Machine
CurrentState = 10013, // Battle Branching
Status = 1, // ??
Flag = (int)EFsmStateFlag.Confirmed
});
fsm.Fsms.Add(new DFsm
{
FsmId = 10007, // Main State Machine
CurrentState = 10015, // Moving Combat
Status = 1, // ??
Flag = (int)EFsmStateFlag.Confirmed
});
}
public override EntityPb Pb
{
get
{
EntityPb pb = new()
{
Id = Id,
EntityType = (int)Type,
ConfigType = (int)ConfigType,
EntityState = (int)State,
ConfigId = ConfigId,
Pos = Pos,
Rot = Rot,
LivingStatus = (int)LivingStatus,
IsVisible = IsVisible,
InitLinearVelocity = new(),
InitPos = new()
};
pb.ComponentPbs.AddRange(ComponentSystem.Pb);
return pb;
}
}
}

View file

@ -15,6 +15,24 @@ internal class PlayerEntity : EntityBase
public bool IsCurrentRole { get; set; } public bool IsCurrentRole { get; set; }
public int WeaponId
{
get => ComponentSystem.Get<EntityEquipComponent>().WeaponId;
set => ComponentSystem.Get<EntityEquipComponent>().WeaponId = value;
}
public int Health
{
get => ComponentSystem.Get<EntityAttributeComponent>().GetAttribute(EAttributeType.Life);
set => ComponentSystem.Get<EntityAttributeComponent>().SetAttribute(EAttributeType.Life, value);
}
public int HealthMax
{
get => ComponentSystem.Get<EntityAttributeComponent>().GetAttribute(EAttributeType.LifeMax);
set => ComponentSystem.Get<EntityAttributeComponent>().SetAttribute(EAttributeType.LifeMax, value);
}
public override void OnCreate() public override void OnCreate()
{ {
base.OnCreate(); base.OnCreate();
@ -25,24 +43,40 @@ internal class PlayerEntity : EntityBase
EntityVisionSkillComponent visionSkillComponent = ComponentSystem.Create<EntityVisionSkillComponent>(); EntityVisionSkillComponent visionSkillComponent = ComponentSystem.Create<EntityVisionSkillComponent>();
visionSkillComponent.SetExploreTool(1001); visionSkillComponent.SetExploreTool(1001);
_ = ComponentSystem.Create<EntityEquipComponent>();
_ = 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()
{ {
base.Activate(); base.Activate();
_ = ComponentSystem.Create<EntityAttributeComponent>();
InitAttributes();
}
private void InitAttributes()
{
EntityAttributeComponent attributeComponent = ComponentSystem.Get<EntityAttributeComponent>();
attributeComponent.SetAttribute(EAttributeType.Life, 1000);
attributeComponent.SetAttribute(EAttributeType.LifeMax, 1000);
attributeComponent.SetAttribute(EAttributeType.Lv, 1);
attributeComponent.SetAttribute(EAttributeType.AutoAttackSpeed, 10000);
attributeComponent.SetAttribute(EAttributeType.CastAttackSpeed, 10000);
} }
public override EEntityType Type => EEntityType.Player; public override EEntityType Type => EEntityType.Player;

View file

@ -3,7 +3,13 @@ internal enum GameEventType
{ {
Login = 1, Login = 1,
EnterGame, EnterGame,
PushDataDone,
// Actions // Actions
VisionSkillChanged FormationUpdated,
VisionSkillChanged,
// Debug
DebugUnlockAllRoles,
DebugUnlockAllWeapons
} }

View file

@ -1,4 +1,5 @@
using GameServer.Controllers.Factory; using Core.Config;
using GameServer.Controllers.Factory;
using GameServer.Network.Kcp; using GameServer.Network.Kcp;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@ -7,8 +8,9 @@ internal class WWGameServer : IHostedService
{ {
private readonly KcpGateway _gateway; private readonly KcpGateway _gateway;
public WWGameServer(KcpGateway gateway, EventHandlerFactory messageHandlerFactory) public WWGameServer(KcpGateway gateway, ConfigManager manager, EventHandlerFactory messageHandlerFactory)
{ {
_ = manager;
_ = messageHandlerFactory; _ = messageHandlerFactory;
_gateway = gateway; _gateway = gateway;
} }

View file

@ -1,6 +1,14 @@
{ {
"StartingValues": { "StartingValues": {
"Name": "ReversedRooms", "Name": "ReversedRooms",
"Characters": [ 1601, 1302, 1203 ] "Characters": [ 1402, 1302, 1203 ],
"Position": {
"X": -35823,
"Y": 67132,
"Z": 4067
}
},
"Features": {
"TeleportByMapMark": true
} }
} }

BIN
GameServer/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

View file

@ -795,6 +795,8 @@ public enum MessageId
NewBieCourseRewardResponse = 12153, NewBieCourseRewardResponse = 12153,
NewJourneyRequest = 12214, NewJourneyRequest = 12214,
NewJourneyResponse = 12215, NewJourneyResponse = 12215,
ClientCurrentRoleReportRequest = 12217,
ClientCurrentRoleReportResponse = 12218,
NormalItemAddNotify = 5268, NormalItemAddNotify = 5268,
NormalItemRemoveNotify = 5269, NormalItemRemoveNotify = 5269,
NormalItemRequest = 5265, NormalItemRequest = 5265,

View file

@ -1573,6 +1573,17 @@ message ClientBasicInfoRequest { // MessageId: 5165
message ClientBasicInfoResponse { // MessageId: 3017 message ClientBasicInfoResponse { // MessageId: 3017
} }
message ClientCurrentRoleReportRequest { // MessageId: 12217
int32 player_id = 1;
int32 current_role_id = 2;
int64 current_entity_id = 3;
}
message ClientCurrentRoleReportResponse { // MessageId: 12218
int32 player_id = 1;
int64 current_entity_id = 2;
}
message ClientDataComponentPb { message ClientDataComponentPb {
bool is_static_init = 1; bool is_static_init = 1;
int64 owner_id = 2; int64 owner_id = 2;
@ -1604,6 +1615,7 @@ message CombatMaxCaseMessageResponse { // MessageId: 11987
message CombatNotifyData { message CombatNotifyData {
CombatCommon combat_common = 1; CombatCommon combat_common = 1;
oneof message {
CreateBulletNotify create_bullet_notify = 2; CreateBulletNotify create_bullet_notify = 2;
DestroyBulletNotify destroy_bullet_notify = 3; DestroyBulletNotify destroy_bullet_notify = 3;
DamageExecuteNotify damage_execute_notify = 4; DamageExecuteNotify damage_execute_notify = 4;
@ -1661,21 +1673,26 @@ message CombatNotifyData {
RecoverPropChangedNotify recover_prop_changed_notify = 58; RecoverPropChangedNotify recover_prop_changed_notify = 58;
RemoveBuffByIdS2cRequestNotify remove_buff_by_id_s2c_request_notify = 59; RemoveBuffByIdS2cRequestNotify remove_buff_by_id_s2c_request_notify = 59;
FormationBuffRemoveByIdS2cRequestNotify formation_buff_remove_by_id_s2c_request_notify = 60; FormationBuffRemoveByIdS2cRequestNotify formation_buff_remove_by_id_s2c_request_notify = 60;
}
} }
message CombatPushData { message CombatPushData {
CombatCommon combat_common = 1; CombatCommon combat_common = 1;
oneof message {
FormationBuffApplyS2cResponsePush formation_buff_apply_s2c_response_push = 2; FormationBuffApplyS2cResponsePush formation_buff_apply_s2c_response_push = 2;
FormationBuffRemoveS2cResponsePush formation_buff_remove_s2c_response_push = 3; FormationBuffRemoveS2cResponsePush formation_buff_remove_s2c_response_push = 3;
ApplyBuffS2cResponsePush apply_buff_s2c_response_push = 4; ApplyBuffS2cResponsePush apply_buff_s2c_response_push = 4;
RemoveBuffS2cResponsePush remove_buff_s2c_response_push = 5; RemoveBuffS2cResponsePush remove_buff_s2c_response_push = 5;
RemoveBuffByIdS2cResponsePush remove_buff_by_id_s2c_response_push = 6; RemoveBuffByIdS2cResponsePush remove_buff_by_id_s2c_response_push = 6;
FormationBuffRemoveByIdS2cResponsePush formation_buff_remove_by_id_s2c_response_push = 7; FormationBuffRemoveByIdS2cResponsePush formation_buff_remove_by_id_s2c_response_push = 7;
}
} }
message CombatReceiveData { message CombatReceiveData {
oneof message {
CombatNotifyData combat_notify_data = 2; CombatNotifyData combat_notify_data = 2;
CombatResponseData combat_response_data = 3; CombatResponseData combat_response_data = 3;
}
} }
message CombatReceivePackNotify { // MessageId: 1332 message CombatReceivePackNotify { // MessageId: 1332
@ -1685,6 +1702,7 @@ message CombatReceivePackNotify { // MessageId: 1332
message CombatRequestData { message CombatRequestData {
CombatCommon combat_common = 1; CombatCommon combat_common = 1;
int32 request_id = 2; int32 request_id = 2;
oneof message {
CreateBulletRequest create_bullet_request = 3; CreateBulletRequest create_bullet_request = 3;
DestroyBulletRequest destroy_bullet_request = 4; DestroyBulletRequest destroy_bullet_request = 4;
DamageExecuteRequest damage_execute_request = 5; DamageExecuteRequest damage_execute_request = 5;
@ -1745,12 +1763,14 @@ message CombatRequestData {
DrownEndTeleportRequest drown_end_teleport_request = 60; DrownEndTeleportRequest drown_end_teleport_request = 60;
MonsterDrownRequest monster_drown_request = 61; MonsterDrownRequest monster_drown_request = 61;
CombatMaxCaseMessageRequest combat_max_case_message_request = 62; CombatMaxCaseMessageRequest combat_max_case_message_request = 62;
}
CombatContext context = 100; CombatContext context = 100;
} }
message CombatResponseData { message CombatResponseData {
CombatCommon combat_common = 1; CombatCommon combat_common = 1;
int32 request_id = 2; int32 request_id = 2;
oneof message {
CreateBulletResponse create_bullet_response = 3; CreateBulletResponse create_bullet_response = 3;
DestroyBulletResponse destroy_bullet_response = 4; DestroyBulletResponse destroy_bullet_response = 4;
DamageExecuteResponse damage_execute_response = 5; DamageExecuteResponse damage_execute_response = 5;
@ -1810,6 +1830,7 @@ message CombatResponseData {
RTimeStopResponse r_time_stop_response = 59; RTimeStopResponse r_time_stop_response = 59;
DrownEndTeleportResponse drown_end_teleport_response = 60; DrownEndTeleportResponse drown_end_teleport_response = 60;
MonsterDrownResponse monster_drown_response = 61; MonsterDrownResponse monster_drown_response = 61;
}
} }
message CombatSendData { message CombatSendData {
@ -2623,6 +2644,7 @@ message EntityActiveRequest { // MessageId: 1541
message EntityActiveResponse { // MessageId: 1542 message EntityActiveResponse { // MessageId: 1542
int32 error_code = 1; int32 error_code = 1;
repeated EntityComponentPb component_pbs = 2; repeated EntityComponentPb component_pbs = 2;
bool is_visible = 3;
} }
message EntityAddBubbleNotify { // MessageId: 12203 message EntityAddBubbleNotify { // MessageId: 12203
@ -7890,6 +7912,7 @@ message SeasonData {
int64 end_time = 3; int64 end_time = 3;
repeated RoguelikeToken roguelike_token_list = 4; repeated RoguelikeToken roguelike_token_list = 4;
repeated SeasonReward season_reward_list = 5; repeated SeasonReward season_reward_list = 5;
int32 token_item_count = 6;
} }
message SeasonReward { message SeasonReward {
@ -10921,6 +10944,7 @@ enum ErrorCode {
ERROR_CODE_ERR_ENTITY_PATROL_COMPONENT_NOT_EXIST = 600088; ERROR_CODE_ERR_ENTITY_PATROL_COMPONENT_NOT_EXIST = 600088;
ERROR_CODE_ERR_INTERACT_IS_NOT_PARTICIPANT = 600089; ERROR_CODE_ERR_INTERACT_IS_NOT_PARTICIPANT = 600089;
ERROR_CODE_ERR_VISION_ENTITY_INTERACT_FAIL = 600090; ERROR_CODE_ERR_VISION_ENTITY_INTERACT_FAIL = 600090;
ERROR_CODE_ERR_MAX_DROP_TIMES = 600091;
ERROR_CODE_ERR_SCENE_WORLD_NOT_EXIST = 700000; ERROR_CODE_ERR_SCENE_WORLD_NOT_EXIST = 700000;
ERROR_CODE_ERR_PLAYER_NOT_IN_SCENE = 700001; ERROR_CODE_ERR_PLAYER_NOT_IN_SCENE = 700001;
ERROR_CODE_ERR_DROP_ENTITY_NOT_EXIST = 700002; ERROR_CODE_ERR_DROP_ENTITY_NOT_EXIST = 700002;
@ -11287,6 +11311,7 @@ enum ErrorCode {
ERROR_CODE_ERR_ITEM_POS_INVAILD = 900058; ERROR_CODE_ERR_ITEM_POS_INVAILD = 900058;
ERROR_CODE_ERR_ITEM_ID_INVAILD = 900059; ERROR_CODE_ERR_ITEM_ID_INVAILD = 900059;
ERROR_CODE_ERR_ROULETTE_FUNC_ID_INVAILD = 900060; ERROR_CODE_ERR_ROULETTE_FUNC_ID_INVAILD = 900060;
ERROR_CODE_ERR_STATE_IS_RUNNING = 900061;
ERROR_CODE_ERR_BUFF_ITEM_CONFIG = 1000000; ERROR_CODE_ERR_BUFF_ITEM_CONFIG = 1000000;
ERROR_CODE_ERR_BUFF_ITEM_NOT_SHARE = 1000001; ERROR_CODE_ERR_BUFF_ITEM_NOT_SHARE = 1000001;
ERROR_CODE_ERR_BUFF_ITEM_SHARE_ROLE_ID = 1000002; ERROR_CODE_ERR_BUFF_ITEM_SHARE_ROLE_ID = 1000002;
@ -11745,6 +11770,9 @@ enum ErrorCode {
ERROR_CODE_ERR_EXPLORE_SKILL_PULL_GIANT_NOT_EXIST = 1800027; ERROR_CODE_ERR_EXPLORE_SKILL_PULL_GIANT_NOT_EXIST = 1800027;
ERROR_CODE_ERR_HTTP_RPC_PARAM = 1800028; ERROR_CODE_ERR_HTTP_RPC_PARAM = 1800028;
ERROR_CODE_ERR_PLAYER_NOT_IN_GAME_NODE = 1800029; ERROR_CODE_ERR_PLAYER_NOT_IN_GAME_NODE = 1800029;
ERROR_CODE_ERR_APPLY_JOIN_PLAYER_CUR_ROLE_IS_DEAD = 1800030;
ERROR_CODE_ERR_PLAYER_CUR_ROLE_IS_DEAD_NO_JOIN = 1800031;
ERROR_CODE_ERR_PLAYER_CUR_ROLE_IS_DEAD = 1800032;
ERROR_CODE_ERR_CALABASH_LEVEL_REQUEST = 1900000; ERROR_CODE_ERR_CALABASH_LEVEL_REQUEST = 1900000;
ERROR_CODE_ERR_CALABASH_LEVEL_REWARD_DONE = 1900001; ERROR_CODE_ERR_CALABASH_LEVEL_REWARD_DONE = 1900001;
ERROR_CODE_ERR_CALABASH_LEVEL_CONFIG = 1900002; ERROR_CODE_ERR_CALABASH_LEVEL_CONFIG = 1900002;
@ -11783,6 +11811,7 @@ enum ErrorCode {
ERROR_CODE_ERR_NEW_BIE_COURSE_CONFIG = 1900035; ERROR_CODE_ERR_NEW_BIE_COURSE_CONFIG = 1900035;
ERROR_CODE_ERR_NEW_BIE_COURSE_REWARD_HAD = 1900036; ERROR_CODE_ERR_NEW_BIE_COURSE_REWARD_HAD = 1900036;
ERROR_CODE_ERR_NEW_BIE_COURSE_LEVEL = 1900037; ERROR_CODE_ERR_NEW_BIE_COURSE_LEVEL = 1900037;
ERROR_CODE_ERR_DETECTION_TARGET_SILENCE = 1900038;
ERROR_CODE_ERR_DO_COMMON_REWARD_CONFIG_ERROR = 2000000; ERROR_CODE_ERR_DO_COMMON_REWARD_CONFIG_ERROR = 2000000;
ERROR_CODE_INST_PLAY_NOT_SETTLE = 2000001; ERROR_CODE_INST_PLAY_NOT_SETTLE = 2000001;
ERROR_CODE_INST_PLAY_NOT_FINISH_EXECUTE = 2000002; ERROR_CODE_INST_PLAY_NOT_FINISH_EXECUTE = 2000002;
@ -12280,6 +12309,7 @@ enum WorldTeamLeaveReason {
WORLD_TEAM_LEAVE_REASON_BE_KICK = 1; WORLD_TEAM_LEAVE_REASON_BE_KICK = 1;
WORLD_TEAM_LEAVE_REASON_DISSOLVE = 2; WORLD_TEAM_LEAVE_REASON_DISSOLVE = 2;
WORLD_TEAM_LEAVE_REASON_LOGOUT = 3; WORLD_TEAM_LEAVE_REASON_LOGOUT = 3;
WORLD_TEAM_LEAVE_REASON_TO_OWN_BIG_WORLD = 4;
} }
enum WorldTeamPlayerChangeType { enum WorldTeamPlayerChangeType {

View file

@ -1,12 +1,13 @@
# WutheringWaves # WutheringWaves
Wuthering Waves server emulator (0.8.2) Wuthering Waves server emulator (0.9.0)
## How to run ## How to run
- Compile and run SDKServer and GameServer using dotnet8 (Visual Studio 2022 recommended) - Compile and run SDKServer and GameServer using dotnet8 (Visual Studio 2022 recommended)
## Connecting ## Connecting
- Download [Game Client](https://nogatekeep.ing/assets/ww/0.8.2/ww.7z), it's **pre-patched** and redirects http requests to `127.0.0.1:5500` - Get game client from somewhere. For example using official launcher (you can find it in our discord)
- [Download and extract patch](https://nogatekeep.ing/assets/ww/0.9.0/WuWa-0.9-patch.zip) to the game folder, it will redirect all http requests to `127.0.0.1:5500`
- Run game and connect to the server - Run game and connect to the server
## Need help? ## Need help?

View file

@ -4,10 +4,10 @@ namespace SDKServer.Handlers;
internal static class HotPatchHandler internal static class HotPatchHandler
{ {
public static string OnConfigRequest() => "N2wiD0yJQntN+Kv98DJOLWvjpgvdTa3/KRA9Rf288hPWxbPgAOzkf/mSfCXKMOFldB3zoIftzB7NIGkF+slYu1MoBcFyxkhx+lRW9oDsuqKVS32cgEyuopKuN71QdjPII82UGXGd4M5mBilVEkTaRKBZVypka5CB79x5Rs2iiPTxOk1zdOUi5F6uUD0cjQod/KM+7TtaiUsSNS+FVBldLPWJOdcQWomUw+q5cCLBJqhxYUJg/hQAt8rRRHyDj2H+0RAdMt1WS5qbYb+QkVCvZlT4UWUyaes3p3xsyyr0HpiqzHXf5iLjaxSv5iKNrLUpLMexn2c3bf+TG7Kca/QYwbJYz4TeYk8zuV7A1ccC4fgSSxwC50xChiOuPwyi7NWkP7Z2FEyqtBWpgoWo6+uFn+f1PaalaZt9e2JZXvmZjcY5pQwGfbXSHAGu3yopAScBvsX1nQzrRgi8LFN/2XTMaoYjfZp+FiNiVMFCK7IlcfjeL1Shd5FrwcKnEGPeu6dVBvrZnKWaK81lzZnZc0Yce95rZMa7o8ccQBTWXE/S/ZXV10ctCO6KYa1/+l+zhvAfPt3WcU7OvcOh24kg/WSYOZFg9nFaLpMzSCTqZPhi7OE1LU5o0uLfk8kFJtJFj7CVG0YiD/dZhWhA0HW6WZmbE/DESkUMagfVPanZCZpweBBnmmRnJZr+LMm5btmgmZZ8/pwGk/JbvpwRaSvP1Cw/hIVq6m/EJ+j+oHr/EscxoVa2f9pGB20TOS7ggTPiwkvfWxX04CXCQ6VgQ8dFNZB4tO6LiNsCfSARDz0VNjqdpdRpON0W2KjfhKlX4IlhsQj9Cnet4Dk/knWe/vO2e2/JGw4JGXzJmguhT9EQE81IaGoKkcOBjED5odgONH37TyNK86piMBHYHRy/ZhpKOHbMhNh5Vv2mv94kzLMD6elXnRiL888tcc3hgi2fmZR6DVKvbiN2SIXRABQy6YNtKQVjMBzJEGART29AyD4r89L85T4jbheTiyXiDIX4H0eGGfqLsCJnSocV7GLm2ZJrKY/ebsg50fadj3AlxnU2Hqkxyn3Xdmf5ilM9lQ8xurrtsbGSSn6cQ/56X8Vm5gJa/4bFyMvuSl4ygKuAq6Ae8S7faeRUPkmObPOBdU841OmMWrjf8IeXzt+jkK00VglUC8y+ZhvX33ZYNXGjAACwZF11GCS3plkhI7zwtOlLNavtO94INQQGulQ/fG+mu4BAh19xtMiZpUXIumleO0ehq5LHiE/UYMZizvONyvPbCnqmkvEI8f9kgkODPkPBfQjqQ7qeGpU7BdbKEv83BTr5SyG6m80Fq0BDVdX5BwkGdk28YeJdbjUhUH0/fmSr7ODw6YYpE3x8TaDpOq+taNZu40KZ6USeNqEofSZPHC4e1+SwSpjldOoQTdH7BBaQ6hN0plJkm+4HL7KbWAQOX82W7H3Ks/GoMeHmokTo9T2JUTi+dzNpuaOjxlxyiuoDkiZ+0cOcLIbKk5Df0cphrkq2GrvCJ5g4FGvLvcPrs8tdA15D89i8Ygrh6ERy6beiwTCEtK5sqWM11URUrCCTRi3JRprzq+R+ghOPVGK1svfpUbzAyWZK6ugt3OYdg0Ze3WKFnvLqRAhWu6ouy+iHrT8D1qVna+6IZ3OCZbDpTlszQTA+5YNY9iO2O9seOJvbUOep5T04RETWFEr0vunp3M0FAH9OtaUN9ePmshqk3h1wC0oftx+8d5QvyJ/EkrXi4LA1huKDGtbf2P8RK2oH7wXKZaMZKYl2IGtc1lzIebtzkkqY/6ygl4K8mhFUf1g4vXSvA17mjdcemrptVCxB5EAnjiyBbjhVtoSE4RAmN7ysA95MeF104cf4AFUS2V3c4h7RTRb0HRIOfWhPc/UP6UZnXYx+ALCKyfSDXZ7v9Ctxfqnd/WwxKg233tcPBOgOVP72wSYiXt3HtHve5/NqBrz3PW7KJLoA5wTG43iURlkhvgIXJV6PK6BbCXgusT0ZHOtKUYIbhomZ1Wa+2VmCcljQdfN/qH7C1nS6Icr+OGHqoA4XBoj3OMZ1wQrP+9BwWhp9dxsLlT8lNQny8f9IIzvxZNyDNK7WOfRAHuO1x0Wf8aqA11SM8BL99m90PuLzMXK7zZgqKxJVF1Ry640/JMr8vaK01aKGwMEQXaUt/so94Z/FXm8uwZJ9Rw7ZIviyzdPmq7JXNa8W2kkeEEdekBKdCOD+Q63F0pzFoqvkrqram17GJHbGN/0DXoYrQGkJjuo7ZiwdJGagtuFah7Fut+bWvfmy5CnK6/6VKmLaWxPqjXZrCPru05KoKaMEPtJNElGIGkYmohX4fbwwwe0LvwZBrST+45+KzcApMOIqj2iSo+m5iwEMwpMIdJoor6Tsx7gdhS3ekeK9Ad7hZBFLNSYxL+sirfBfMHVYIDCLRrDwkssT0Qzd8wUTZG88TkyKxLWKMNAqwkab5lvEc5Vk3gjFLyQXbxKk8zhx9pBa9fWIcVJeQpVhJXayqWYBTsLCXZ42aWRehX9M6tuR9j8cyXHNQI6NXDB5WA05P5qKV8VauoVoLIWZlmVoH/bfiiPxTHTFdj08jpwPWtDKPYG6XRt3/DgF+e9TcIxsDT/Tci885fA6SP4e46yML5F4e4bUMusTkjBkMBuqrSzAZyIuj1Xady3xFW35EanmzAH0jOoz2Qycgv5BpFmjmHytw1nOxAC/gOja8UwzIxgFmB55QGQKikI8u9hNCAJEMkz1yozOucWR7rUbtj9YvXXTAfn/p3Ea517/FnxjVMvpUwJssldhApzEdN+eyNbs806+ucaJvevmM5IP7/xDFgAf/wx1zorLQHT6N5rDqqvRnDr7inwcLyQSjcS+DU47UEnyvTzoZ6jBwrJWJ22a8O4jwnVmqMqmsfjrcprJ/HjTwIbnKzN1fIIIZQuC0V2E5IyxcPdYMJPBBGQM6jd583pYU5Zg6bjdNy5K7w=="; public static string OnConfigRequest() => "{\r\n \"PackageVersion\": \"0.9.0\",\r\n \"LauncherVersion\": \"0.9.0\",\r\n \"ResourceVersion\": \"0.9.3\",\r\n \"LauncherIndexSha1\": {},\r\n \"ResourceIndexSha1\": {\r\n \"0.9.1\": \"F9E8CE07677A58BE26B8D1A193712167A5AD7D6E\",\r\n \"0.9.2\": \"2ACEAED17AC801D6860C61E4AFB4F9E8FB07EC71\",\r\n \"0.9.3\": \"6627F17FAA25267DFFB6875F845DF4CFA2DE67EA\"\r\n },\r\n \"ChangeList\": \"1162610\",\r\n \"CompatibleChangeLists\": [],\r\n \"Versions\": [\r\n {\r\n \"Name\": \"en\",\r\n \"Version\": \"0.9.3\",\r\n \"IndexSha1\": {\r\n \"0.9.1\": \"80083122F01DEFAE73F24F41F1B9D4F87B72B1BF\",\r\n \"0.9.2\": \"1C41F2D5DF38027153F16C777B9FAD3F27578E17\",\r\n \"0.9.3\": \"7A611990E039B3D7AD1EA505E1E9F544D310E9B8\"\r\n }\r\n },\r\n {\r\n \"Name\": \"ja\",\r\n \"Version\": \"0.9.3\",\r\n \"IndexSha1\": {\r\n \"0.9.1\": \"DBA7744B8994E20BB85D993DF9D2A214A3C09460\",\r\n \"0.9.2\": \"C22680FDAAEB25787B83800DDD1CB6C5AC66D285\",\r\n \"0.9.3\": \"8CACF6381D82BA3B577DF804DA2599ADDD8A5AE8\"\r\n }\r\n },\r\n {\r\n \"Name\": \"ko\",\r\n \"Version\": \"0.9.3\",\r\n \"IndexSha1\": {\r\n \"0.9.1\": \"DCA10D3FA125185A7F5F919FDD580474497912C6\",\r\n \"0.9.2\": \"1FEF3B7B05A1D5C8A346E03868976217B4C49E59\",\r\n \"0.9.3\": \"89631EAA41697A4ABFE785A53273FB50745114C9\"\r\n }\r\n },\r\n {\r\n \"Name\": \"zh\",\r\n \"Version\": \"0.9.3\",\r\n \"IndexSha1\": {\r\n \"0.9.1\": \"20CCECCEBBB910C14BE3CF111037F15287CB46E7\",\r\n \"0.9.2\": \"A176E35CFB6DB0C1CDE8B1B1EC141BD9479CFE59\",\r\n \"0.9.3\": \"66E3A0365D324E303A56F85E347F05EBBEA56645\"\r\n }\r\n }\r\n ]\r\n}";
public static string OnKeyListRequest() => "dJf580+dhY42au4yMyAS8bT83C9lOj2Igaw6GHLdgMgu45ugRZGzR2HUowgKaW/qQMY2v1+DubYu4hOeINFYWRbKpZUoKO+FHvPeeBAt00gqLBeHEnChvUSOPpAeJJp8ryfahbqy+LQ5d+2W/UOP83XZEVUIYPWleXbWUVMXLCCznOg76nWywXdHe9/Yukff8Di99tDt3NiSTdQPEtmk/c4vgXay+QTcFW8mwHgEhVh6vb9jQOMwvG4cmmjnigedTqW7Q4jm6GbgSWdb/tesmg70Jrjj4aHn6PI9jMSBSwk3tClBx8/Qwz1Ji+gyt/7XwvFmf+MZ8MyAH1DuIKrsomr7IfsMboKlr3T0EEIwVsb6idrbYaphGhefSemA/v2D"; public static string OnKeyListRequest() => "{\r\n \"Hash\": \"da39a3ee5e6b4b0d3255bfef95601890afd80709\",\r\n \"Random\": \"CtBIsHPiwhwOqqBYxj\",\r\n \"Key\": \"EYA/aRYz0h2W77M07J2AHTftgGQiIcFQFpgGsdsm65Ee8YqP6uI86fIG4EWftUr7B2vLbh/U62yi21e4CeGIpr+HppDDmpprS/QTmZYQ0OX9q/KB06+4ihmwzSim1iTedixejDPQ8BP90iH7CI6Se/+BhfslZh3MxDmzzl8W9bw=\"\r\n}";
public static FileContentHttpResult OnPakDataRequest() public static FileContentHttpResult OnPakDataRequest()
=> TypedResults.File(Convert.FromHexString("446144334C5075782F544D35586C5A623669315A6A66774A32536265514843762B43567567764F41596B737A596874397164455861454B51626376474F664E544B536335754135444867385A674263356C4245654D6C374C43392F64326565473755796E754E59655753446C48624F544430785338514D39577165306F45706D5541353879377856304B38445970706D47683338386F51744C7A307472314C7743667A416D2F69536D70633D0A")); => TypedResults.File([]);
} }

View file

@ -10,6 +10,12 @@ public record BaseConfigModel
[JsonPropertyName("SecondaryUrl")] [JsonPropertyName("SecondaryUrl")]
public required CdnUrlEntry[] SecondaryUrl { get; set; } public required CdnUrlEntry[] SecondaryUrl { get; set; }
[JsonPropertyName("SpeedRatio")]
public int SpeedRatio { get; set; }
[JsonPropertyName("PriceRatio")]
public int PriceRatio { get; set; }
[JsonPropertyName("GmOpen")] [JsonPropertyName("GmOpen")]
public bool GmOpen { get; set; } public bool GmOpen { get; set; }

View file

@ -7,19 +7,22 @@ internal static class Program
{ {
private static async Task Main(string[] args) private static async Task Main(string[] args)
{ {
var builder = WebApplication.CreateBuilder(args); Console.Title = "Wuthering Waves | SDK Server";
Console.WriteLine(" __ __ __ .__ .__ __ __ \r\n/ \\ / \\__ ___/ |_| |__ ___________|__| ____ ____ / \\ / \\_____ ___ __ ____ ______\r\n\\ \\/\\/ / | \\ __\\ | \\_/ __ \\_ __ \\ |/ \\ / ___\\ \\ \\/\\/ /\\__ \\\\ \\/ // __ \\ / ___/\r\n \\ /| | /| | | Y \\ ___/| | \\/ | | \\/ /_/ > \\ / / __ \\\\ /\\ ___/ \\___ \\ \r\n \\__/\\ / |____/ |__| |___| /\\___ >__| |__|___| /\\___ / \\__/\\ / (____ /\\_/ \\___ >____ >\r\n \\/ \\/ \\/ \\//_____/ \\/ \\/ \\/ \\/ \r\n\r\n\t\t\t\t\t\t\t\t\t\t\t\tSDK Server\n");
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseUrls("http://*:5500"); builder.WebHost.UseUrls("http://*:5500");
builder.Logging.AddSimpleConsole(); builder.Logging.AddSimpleConsole();
var app = builder.Build(); WebApplication app = builder.Build();
app.UseMiddleware<NotFoundMiddleware>(); app.UseMiddleware<NotFoundMiddleware>();
app.MapGet("/api/login", LoginHandler.Login); app.MapGet("/api/login", LoginHandler.Login);
app.MapGet("/config/index.json", ConfigHandler.GetBaseConfig); app.MapGet("/index.json", ConfigHandler.GetBaseConfig);
app.MapGet("/dev/client/7cyFLmtLJlUauZ1hM8DsL5Sj7cXxSNQD/Windows/KeyList_0.8.0.json", HotPatchHandler.OnKeyListRequest); app.MapGet("/dev/client/mtZyW6ZYIu1pE0TCHUbXcM1oU8vx4hnb/Windows/KeyList_0.9.0.json", HotPatchHandler.OnKeyListRequest);
app.MapGet("/dev/client/7cyFLmtLJlUauZ1hM8DsL5Sj7cXxSNQD/Windows/config.json", HotPatchHandler.OnConfigRequest); app.MapGet("/dev/client/mtZyW6ZYIu1pE0TCHUbXcM1oU8vx4hnb/Windows/config.json", HotPatchHandler.OnConfigRequest);
app.MapGet("/dev/client/7cyFLmtLJlUauZ1hM8DsL5Sj7cXxSNQD/Windows/client_key/0.8.0/xFrH845q3t8Pgy5eB2/PakData", HotPatchHandler.OnPakDataRequest); app.MapGet("/dev/client/mtZyW6ZYIu1pE0TCHUbXcM1oU8vx4hnb/Windows/client_key/0.9.0/CtBIsHPiwhwOqqBYxj/PakData", HotPatchHandler.OnPakDataRequest);
await app.RunAsync(); await app.RunAsync();
} }

View file

@ -4,8 +4,13 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>icon.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="icon.ico" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" /> <None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup> </ItemGroup>

BIN
SDKServer/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

View file

@ -0,0 +1,52 @@
using Google.Protobuf;
using Protocol;
namespace TrafficAnalyzer;
internal static class Program
{
private const int StdInSize = 65535;
private const string ProtoAssembly = "Protocol";
private const string ProtoNamespace = "Protocol";
private const string MessageParserProperty = "Parser";
private static readonly DumpOptions s_objectDumperOpts = new() { DumpStyle = DumpStyle.CSharp, IndentSize = 4, IndentChar = ' ', IgnoreDefaultValues = true };
private static void Main(string[] args)
{
Console.SetIn(new StreamReader(Console.OpenStandardInput(StdInSize), Console.InputEncoding, false, StdInSize));
List<Tuple<int, byte[]>> inList = [];
string? idInput;
string? payloadInput;
while (!string.IsNullOrEmpty(idInput = Console.ReadLine()) && (payloadInput = Console.ReadLine()) != null)
{
int messageId = int.Parse(idInput);
byte[] payload = Convert.FromHexString(payloadInput);
inList.Add(Tuple.Create(messageId, payload));
}
foreach ((int messageId, byte[] payload) in inList)
{
string messageName = ((MessageId)messageId).ToString();
Type? type = Type.GetType($"{ProtoNamespace}.{messageName},{ProtoAssembly}");
if (type is null)
{
Console.WriteLine($"Message with id {messageName} wasn't found in proto definition.");
continue;
}
MessageParser parser = (MessageParser)type.GetProperty(MessageParserProperty)!.GetValue(null)!;
IMessage message = parser.ParseFrom(payload);
string outputInitializer = ObjectDumper.Dump(message, s_objectDumperOpts);
Console.WriteLine($"Message: {messageName}");
Console.WriteLine(outputInitializer);
}
}
}

View file

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ObjectDumper.NET" Version="4.1.15" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Protocol\Protocol.csproj" />
</ItemGroup>
</Project>

View file

@ -12,9 +12,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameServer", "GameServer\GameServer.csproj", "{78D639E8-D607-41F1-B0B8-AB1611ADE08F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GameServer", "GameServer\GameServer.csproj", "{78D639E8-D607-41F1-B0B8-AB1611ADE08F}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KcpSharp", "KcpSharp\KcpSharp.csproj", "{C2BDCF0A-C256-4E97-9D9A-45FF5C8614CD}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KcpSharp", "KcpSharp\KcpSharp.csproj", "{C2BDCF0A-C256-4E97-9D9A-45FF5C8614CD}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Protocol", "Protocol\Protocol.csproj", "{9900A88C-7818-4335-84F7-1538ECC8B338}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Protocol", "Protocol\Protocol.csproj", "{9900A88C-7818-4335-84F7-1538ECC8B338}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{C025BDED-6DC7-493D-8D10-05DCCB3072F3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrafficAnalyzer", "TrafficAnalyzer\TrafficAnalyzer.csproj", "{7DA57BA1-215F-4DD8-86A9-CAC62908A6F3}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -38,6 +42,14 @@ Global
{9900A88C-7818-4335-84F7-1538ECC8B338}.Debug|Any CPU.Build.0 = Debug|Any CPU {9900A88C-7818-4335-84F7-1538ECC8B338}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9900A88C-7818-4335-84F7-1538ECC8B338}.Release|Any CPU.ActiveCfg = Release|Any CPU {9900A88C-7818-4335-84F7-1538ECC8B338}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9900A88C-7818-4335-84F7-1538ECC8B338}.Release|Any CPU.Build.0 = Release|Any CPU {9900A88C-7818-4335-84F7-1538ECC8B338}.Release|Any CPU.Build.0 = Release|Any CPU
{C025BDED-6DC7-493D-8D10-05DCCB3072F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C025BDED-6DC7-493D-8D10-05DCCB3072F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C025BDED-6DC7-493D-8D10-05DCCB3072F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C025BDED-6DC7-493D-8D10-05DCCB3072F3}.Release|Any CPU.Build.0 = Release|Any CPU
{7DA57BA1-215F-4DD8-86A9-CAC62908A6F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DA57BA1-215F-4DD8-86A9-CAC62908A6F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DA57BA1-215F-4DD8-86A9-CAC62908A6F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DA57BA1-215F-4DD8-86A9-CAC62908A6F3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE