From e6e5a890b8239ab82d1115dc37a2a7890baa9044 Mon Sep 17 00:00:00 2001 From: Xsear Date: Thu, 4 Jan 2024 19:59:01 +0100 Subject: [PATCH] grpc wip --- .gitignore | 5 +- Lib/AeroMessages | 2 +- Lib/Shared.Web/BaseWebServer.cs | 3 +- PIN.targets | 2 +- UdpHosts/GameServer/Channel.cs | 37 +- UdpHosts/GameServer/ChannelType.cs | 1 + .../Controllers/Character/BaseController.cs | 620 +----------- .../Controllers/Character/CombatController.cs | 57 +- UdpHosts/GameServer/Controllers/Generic.cs | 42 - .../{Generic2.cs => GenericShard.cs} | 30 +- UdpHosts/GameServer/Data/CharacterGender.cs | 7 + UdpHosts/GameServer/Data/CharacterRace.cs | 60 +- UdpHosts/GameServer/Entities/BaseEntity.cs | 13 +- .../Entities/Character/CharMovementState.cs | 1 + .../Entities/Character/Character.cs | 953 +++++++++++++++++- .../Character/MovementStateContainer.cs | 14 +- UdpHosts/GameServer/Entities/IEntity.cs | 4 +- .../GameServer/Enums/ControlPacketType.cs | 1 + .../Enums/GSS/AreaVisualData/Events.cs | 5 +- .../Enums/GSS/Character/Commands.cs | 1 + .../GameServer/Enums/GSS/Character/Events.cs | 5 +- UdpHosts/GameServer/Enums/GSS/Controllers.cs | 6 +- .../GameServer/Enums/GSS/Deployable/Events.cs | 5 +- .../GameServer/Enums/GSS/Generic/Commands.cs | 1 + .../GameServer/Enums/GSS/Generic/Events.cs | 1 + .../Enums/GSS/LootStoreExtensions/Events.cs | 5 +- .../GameServer/Enums/GSS/Turret/Commands.cs | 1 + .../GameServer/Enums/GSS/Turret/Events.cs | 5 +- .../GameServer/Enums/GSS/Vehicle/Commands.cs | 1 + .../GameServer/Enums/GSS/Vehicle/Events.cs | 5 +- UdpHosts/GameServer/Enums/MatrixPacketType.cs | 1 + .../GameServer/Enums/Visuals/PaletteType.cs | 16 +- UdpHosts/GameServer/Extensions/GamePacket.cs | 4 +- UdpHosts/GameServer/Extensions/IAero.cs | 8 +- UdpHosts/GameServer/GameServer.cs | 6 +- UdpHosts/GameServer/GameServer.csproj | 77 +- UdpHosts/GameServer/GameServerSettings.cs | 7 +- UdpHosts/GameServer/INetworkClient.cs | 1 + UdpHosts/GameServer/IPlayer.cs | 2 + UdpHosts/GameServer/IShard.cs | 3 + UdpHosts/GameServer/NetworkClient.cs | 21 +- UdpHosts/GameServer/NetworkPlayer.cs | 434 ++++---- .../GameServer/Protos/GameServerAPI.proto | 76 ++ UdpHosts/GameServer/Shard.cs | 13 +- .../Systems/EntityManager/EntityManager.cs | 209 ++++ .../Systems/MovementRelay/MovementRelay.cs | 92 ++ 46 files changed, 1904 insertions(+), 959 deletions(-) delete mode 100644 UdpHosts/GameServer/Controllers/Generic.cs rename UdpHosts/GameServer/Controllers/{Generic2.cs => GenericShard.cs} (72%) create mode 100644 UdpHosts/GameServer/Protos/GameServerAPI.proto create mode 100644 UdpHosts/GameServer/Systems/EntityManager/EntityManager.cs create mode 100644 UdpHosts/GameServer/Systems/MovementRelay/MovementRelay.cs diff --git a/.gitignore b/.gitignore index 38dcbc37..e43a05e7 100644 --- a/.gitignore +++ b/.gitignore @@ -368,4 +368,7 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd + +# VSCode Shared Configuration Files +.vscode/ \ No newline at end of file diff --git a/Lib/AeroMessages b/Lib/AeroMessages index 227b7d32..bb4a3d49 160000 --- a/Lib/AeroMessages +++ b/Lib/AeroMessages @@ -1 +1 @@ -Subproject commit 227b7d326bad9b69659abdc9d05de0b449598006 +Subproject commit bb4a3d49f721433d5b0a3fbc1f801084b299730a diff --git a/Lib/Shared.Web/BaseWebServer.cs b/Lib/Shared.Web/BaseWebServer.cs index 78924886..272e3b99 100644 --- a/Lib/Shared.Web/BaseWebServer.cs +++ b/Lib/Shared.Web/BaseWebServer.cs @@ -35,6 +35,7 @@ public static IHost Build(Type serverType, IConfiguration configuration) } Log.Information($"Starting web host {serverType.FullName}"); + #pragma warning disable SYSLIB0039 // TLS 1.0 required var hostBuilder = Host.CreateDefaultBuilder() .ConfigureWebHostDefaults(webBuilder => @@ -71,7 +72,7 @@ public static IHost Build(Type serverType, IConfiguration configuration) }); }) .UseSerilog(); - + #pragma warning restore SYSLIB0039 return hostBuilder.Build(); } catch (Exception ex) diff --git a/PIN.targets b/PIN.targets index 26e13453..34339c08 100644 --- a/PIN.targets +++ b/PIN.targets @@ -1,6 +1,6 @@ - net6.0 + net8.0 latest ..\..\stylecop.ruleset diff --git a/UdpHosts/GameServer/Channel.cs b/UdpHosts/GameServer/Channel.cs index 65ea37da..9c13aee0 100644 --- a/UdpHosts/GameServer/Channel.cs +++ b/UdpHosts/GameServer/Channel.cs @@ -298,8 +298,8 @@ public bool SendGSS(T packet, ulong entityId, Enums.GSS.Controllers? controll /// Send a GSS class to the client /// /// The type of the packet - /// - /// + /// The packet + /// Id of the entity the packet is for /// If not provided on the on the packet, the controller Id may be specified here /// Optionally, the enum type containing the message id may be specified for enhanced verbose-level logging /// true if the operation succeeded, false in all other cases @@ -347,12 +347,12 @@ public bool SendGSSClass(TPacket packet, ulong entityId, Enums.GSS.Cont /// Send an package to the client /// /// The type of the packet - /// - /// + /// The Aero message + /// Id of the entity the packet is for /// Optional way to override the message ID /// Optionally, the enum type containing the message id may be specified for enhanced verbose-level logging /// true if the operation succeeded, false in all other cases - /// + /// The passed packet does not have the AeroMessageIdAttribute public bool SendIAero(TPacket packet, ulong entityId = 0, byte messageIdOverride = 0, Type? messageEnumType = null) where TPacket : class, IAero { @@ -419,13 +419,26 @@ public bool SendIAeroChanges(TPacket packet, ulong entityId) return SendPacketMemory(entityId, 1, typeCode, ref packetMemory); } + public bool SendIAeroChanges(TPacket packet, ulong entityId, Memory packetMemory) + where TPacket : class, IAeroViewInterface + { + if (typeof(TPacket).GetCustomAttributes(typeof(AeroMessageIdAttribute), false).FirstOrDefault() is not AeroMessageIdAttribute aeroMsgAttr) + { + throw new ArgumentException($"The passed package is required to be annotated with {nameof(AeroMessageIdAttribute)} (Type: {typeof(TPacket).FullName})"); + } + + var typeCode = (Enums.GSS.Controllers)aeroMsgAttr.ControllerId; + + return SendPacketMemory(entityId, 1, typeCode, ref packetMemory); + } + /// /// Send serialized data of a gss channel packet to the client /// - /// - /// - /// - /// + /// Id of the entity the packet is for + /// Message Id relative to the controllerId + /// Typecode of the entity matching the view or controller + /// Memory buffer /// Optionally, the enum type containing the message id may be specified for enhanced verbose-level logging /// true if the operation succeeded, false in all other cases /// If is not null and does not contain an element with a value equal to @@ -472,8 +485,8 @@ private bool SendPacketMemory(ulong entityId, /// /// Send serialized data of a matrix channel packet to the client /// - /// - /// + /// Id of the matrix message being sent + /// Memory buffer /// TODO: Optionally, the enum type containing the message id may be specified for enhanced verbose-level logging /// true if the operation succeeded, false in all other cases private bool SendPacketMemoryMatrix(byte messageId, ref Memory packetMemory, Type? msgEnumType = null) @@ -488,7 +501,7 @@ private bool SendPacketMemoryMatrix(byte messageId, ref Memory packetMemor /// /// Send data to the client /// - /// + /// Memory buffer /// true if the operation succeeded, false in all other cases private bool Send(Memory packetData) { diff --git a/UdpHosts/GameServer/ChannelType.cs b/UdpHosts/GameServer/ChannelType.cs index c2190dae..3c59afba 100644 --- a/UdpHosts/GameServer/ChannelType.cs +++ b/UdpHosts/GameServer/ChannelType.cs @@ -1,6 +1,7 @@ #nullable enable namespace GameServer; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "See the wiki for more details on how the channels differ: https://github.com/themeldingwars/Documentation/wiki/Game-Server-Protocol-Overview#main-connection")] public enum ChannelType : byte { Control = 0, diff --git a/UdpHosts/GameServer/Controllers/Character/BaseController.cs b/UdpHosts/GameServer/Controllers/Character/BaseController.cs index 3dd81ff4..64a913c8 100644 --- a/UdpHosts/GameServer/Controllers/Character/BaseController.cs +++ b/UdpHosts/GameServer/Controllers/Character/BaseController.cs @@ -26,528 +26,6 @@ public class BaseController : Base public override void Init(INetworkClient client, IPlayer player, IShard shard, ILogger logger) { _logger = logger; - - var cd = player.CharacterEntity.CharData; - - var staticInfo = new StaticInfoData - { - DisplayName = cd.Name, - UniqueName = cd.Name, - Gender = (byte)cd.Gender, - Race = (byte)cd.Race, - CharInfoId = cd.CharInfoID, - HeadMain = cd.CharVisuals.HeadMain, - Eyes = cd.CharVisuals.Eyes, - Unk_1 = 0xff, - IsNPC = 0, - StaffFlags = 0x3, - CharacterTypeId = cd.CharVisuals.CharTypeID, - VoiceSet = cd.VoiceSet, - TitleId = cd.TitleID, - NameLocalizationId = cd.NameLocalizationID, - HeadAccessories = ((List)cd.CharVisuals.HeadAccessories).ToArray(), - LoadoutVehicle = cd.Loadout.VehicleID, - LoadoutGlider = cd.Loadout.GliderID, - Visuals = new VisualsBlock - { - Decals = Array.Empty(), - Gradients = Array.Empty(), - Colors = ((List)cd.CharVisuals.Colors).ToArray(), - Palettes = Array.Empty(), - Patterns = Array.Empty(), - OrnamentGroupIds = ((List)cd.CharVisuals.OrnamentGroups).ToArray(), - CziMapAssetIds = Array.Empty(), - MorphWeights = Array.Empty(), - Overlays = Array.Empty() - }, - ArmyTag = cd.Army.Name - }; - - var gibVisuals = new GibVisuals { Id = 0, Time = shard.CurrentTime }; - var processDelay = new ProcessDelayData { Unk1 = 30721, Unk2 = 236 }; - var characterState = new CharacterStateData { State = CharacterStateData.CharacterStatus.Respawning, Time = shard.CurrentTime }; - var hostilityInfo = new HostilityInfoData { Flags = 0 | HostilityInfoData.HostilityFlags.Faction, FactionId = 1 }; - var maxShields = new MaxVital { Value = 0, Time = shard.CurrentTime }; - var maxHealth = new MaxVital { Value = 19192, Time = shard.CurrentTime }; - var emote = new EmoteData { Id = 0, Time = 0 }; - var dockedParams = new DockedParamsData { Unk1 = new EntityId { Backing = 0 }, Unk2 = Vector3.Zero, Unk3 = 0 }; - var assetOverrides = new AssetOverridesField { Ids = Array.Empty() }; - - var currentEquipment = new EquipmentData - { - Chassis = new SlottedItem - { - SdbId = cd.Loadout.ChassisID, - SlotIndex = 0, - Flags = 0, - Unk2 = 0, - Modules = Array.Empty(), - Visuals = new VisualsBlock - { - Decals = new VisualsDecalsBlock[] - { - new() - { - DecalId = 10000, - Color = 4294967295, - Usage = 255, - Transform = new HalfVector4[] - { - new() - { - X = new HalfFloat { Value = 10935 }, - Y = new HalfFloat { Value = 9478 }, - Z = new HalfFloat { Value = 0 }, - W = new HalfFloat { Value = 8106 } - }, - new() - { - X = new HalfFloat { Value = 42272 }, - Y = new HalfFloat { Value = 43680 }, - Z = new HalfFloat { Value = 9380 }, - W = new HalfFloat { Value = 43573 } - }, - new() - { - X = new HalfFloat { Value = 9592 }, - Y = new HalfFloat { Value = 12012 }, - Z = new HalfFloat { Value = 44736 }, - W = new HalfFloat { Value = 15867 } - } - } - } - }, - Gradients = Array.Empty(), - Colors = ((List)cd.ChassisVisuals.Colors).ToArray(), - Palettes = new VisualsPaletteBlock[] { new() { PaletteId = 85163, PaletteType = 0 } }, - Patterns = new VisualsPatternBlock[] - { - new() - { - PatternId = 10022, - TransformValues = new HalfVector4 - { - X = new HalfFloat { Value = 0 }, - Y = new HalfFloat { Value = 16384 }, - Z = new HalfFloat { Value = 0 }, - W = new HalfFloat { Value = 0 } - } - } - }, - OrnamentGroupIds = Array.Empty(), - CziMapAssetIds = Array.Empty(), - MorphWeights = Array.Empty(), - Overlays = Array.Empty() - } - }, - Backpack = new SlottedItem - { - SdbId = cd.Loadout.BackpackID, - SlotIndex = 0, - Flags = 0, - Unk2 = 0, - Modules = Array.Empty(), - Visuals = new VisualsBlock - { - Decals = Array.Empty(), - Gradients = Array.Empty(), - Colors = Array.Empty(), - Palettes = Array.Empty(), - Patterns = Array.Empty(), - OrnamentGroupIds = Array.Empty(), - CziMapAssetIds = Array.Empty(), - MorphWeights = Array.Empty(), - Overlays = Array.Empty() - } - }, - PrimaryWeapon = new SlottedWeapon - { - Item = new SlottedItem - { - SdbId = cd.Loadout.PrimaryWeaponID, - SlotIndex = 0, - Flags = 0, - Unk2 = 0, - Modules = Array.Empty(), - Visuals = new VisualsBlock - { - Decals = Array.Empty(), - Gradients = Array.Empty(), - Colors = new uint[] { 0x322c0000, 0x543110a2, 0x65b42104 }, - Palettes = Array.Empty(), - Patterns = Array.Empty(), - OrnamentGroupIds = Array.Empty(), - CziMapAssetIds = Array.Empty(), - MorphWeights = Array.Empty(), - Overlays = Array.Empty() - } - }, - Unk1 = 0, - Unk2 = 0 - }, - SecondaryWeapon = new SlottedWeapon - { - Item = new SlottedItem - { - SdbId = cd.Loadout.SecondaryWeaponID, - SlotIndex = 0, - Flags = 0, - Unk2 = 0, - Modules = Array.Empty(), - Visuals = new VisualsBlock - { - Decals = Array.Empty(), - Gradients = Array.Empty(), - Colors = new uint[] { 0x322c0000, 0x543110a2, 0x65b42104 }, - Palettes = Array.Empty(), - Patterns = Array.Empty(), - OrnamentGroupIds = Array.Empty(), - CziMapAssetIds = Array.Empty(), - MorphWeights = Array.Empty(), - Overlays = Array.Empty() - } - }, - Unk1 = 0, - Unk2 = 0 - }, - EndUnk1 = 0, - EndUnk2 = 0 - }; - - var spawnPose = new CharacterSpawnPose - { - Time = shard.CurrentTime, - Position = player.CharacterEntity.Position, - Rotation = player.CharacterEntity.Rotation, - AimDirection = player.CharacterEntity.AimDirection, - Velocity = player.CharacterEntity.Velocity, - MovementState = 0x1000, - Unk1 = 0, - Unk2 = 0, - JetpackEnergy = 0x639c, - AirGroundTimer = 0, - JumpTimer = 0, - HaveDebugData = 0 - }; - - var energyParams = new EnergyParamsData { Max = 1000.0f, Delay = 500, Recharge = 156.0f, Time = shard.CurrentTime }; - - var characterStats = new CharacterStatsData - { - ItemAttributes = new StatsData[] - { - new() { Id = 5, Value = 156.414169f }, new() { Id = 6, Value = 1037.8347f }, new() { Id = 7, Value = 177.44128f }, new() { Id = 12, Value = 16.250000f }, new() { Id = 35, Value = 300 }, - new() { Id = 36, Value = 250 }, new() { Id = 37, Value = 2.092090f }, new() { Id = 142, Value = 12.55f }, new() { Id = 143, Value = 1136 }, new() { Id = 144, Value = 18.433180f }, - new() { Id = 173, Value = 10 }, new() { Id = 186, Value = 11.40f }, new() { Id = 959, Value = 1 }, new() { Id = 1050, Value = 34.5f }, new() { Id = 1051, Value = 13.824884f }, - new() { Id = 1052, Value = 5.5f }, new() { Id = 1121, Value = 150 }, new() { Id = 1146, Value = 10.0f }, new() { Id = 1367, Value = 85 }, new() { Id = 1368, Value = 100 }, - new() { Id = 1370, Value = 65 }, new() { Id = 1371, Value = 120 }, new() { Id = 1372, Value = 140 }, new() { Id = 1377, Value = 140.531250f }, new() { Id = 1395, Value = 75 }, - new() { Id = 1419, Value = 32.769249f }, new() { Id = 1420, Value = 16901.744141f }, new() { Id = 1439, Value = 15279.667969f }, new() { Id = 1451, Value = 681 }, - new() { Id = 1583, Value = 1 }, new() { Id = 1620, Value = 5049.767090f }, new() { Id = 1622, Value = 8 }, new() { Id = 1733, Value = 1.800000f }, new() { Id = 1736, Value = 60 }, - new() { Id = 1737, Value = 5486.919434f }, new() { Id = 1746, Value = 9.320923f }, new() { Id = 1785, Value = 1.084000f }, new() { Id = 1835, Value = 5932.512207f }, - new() { Id = 1904, Value = 4 }, new() { Id = 1905, Value = 2 }, new() { Id = 1987, Value = 8 }, new() { Id = 2034, Value = 22 }, new() { Id = 2037, Value = 9887.518555f }, - new() { Id = 2039, Value = 9 }, new() { Id = 2042, Value = 12.252850f } - }, - Unk1 = 0, - WeaponA = Array.Empty(), - Unk2 = 0, - WeaponB = Array.Empty(), - Unk3 = 0, - AttributeCategories1 = Array.Empty(), - AttributeCategories2 = Array.Empty() - }; - - var scopeBubble = new ScopeBubbleInfoData { Unk1 = 0, Unk2 = 0 }; - - var visualOverrides = new VisualOverridesField { Data = Array.Empty() }; - - var baseController = new AeroMessages.GSS.V66.Character.Controller.BaseController - { - TimePlayedProp = 0, - CurrentWeightProp = 0, - EncumberedWeightProp = 255, - AuthorizedTerminalProp = new AuthorizedTerminalData { TerminalType = 0, TerminalId = 0, TerminalEntityId = 0 }, - PingTimeProp = 0, // shard.CurrentTime, - StaticInfoProp = staticInfo, - SpawnTimeProp = shard.CurrentTime, - VisualOverridesProp = visualOverrides, - CurrentEquipmentProp = currentEquipment, - SelectedLoadoutProp = 184538131, // cd.Loadout.ChassisID, - SelectedLoadoutIsPvPProp = 0, - GibVisualsIdProp = gibVisuals, - SpawnPoseProp = spawnPose, - ProcessDelayProp = processDelay, - SpectatorModeProp = 0, - CinematicCameraProp = null, - CharacterStateProp = characterState, - HostilityInfoProp = hostilityInfo, - PersonalFactionStanceProp = null, - CurrentHealthProp = 0, - CurrentShieldsProp = 0, - MaxShieldsProp = maxShields, - MaxHealthProp = maxHealth, - CurrentDurabilityPctProp = 100, - EnergyParamsProp = energyParams, - CharacterStatsProp = characterStats, - EmoteIDProp = emote, - AttachedToProp = null, - SnapMountProp = 0, - SinFlagsProp = 0, - SinFlagsPrivateProp = 0, - SinFactionsAcquiredByProp = null, - SinTeamsAcquiredByProp = null, - ArmyGUIDProp = cd.Army.GUID, - ArmyIsOfficerProp = 0, - EncounterPartyTupleProp = null, - DockedParamsProp = dockedParams, - LookAtTargetProp = null, - ZoneUnlocksProp = 0, - RegionUnlocksProp = 0, - ChatPartyLeaderIdProp = new EntityId { Backing = 0 }, - ScopeBubbleInfoProp = scopeBubble, - CarryableObjects_0Prop = null, - CarryableObjects_1Prop = null, - CarryableObjects_2Prop = null, - CachedAssetsProp = null, - RespawnTimesProp = null, - ProgressionXpProp = 0, - PermanentStatusEffectsProp = new PermanentStatusEffectsData { Effects = Array.Empty() }, - XpBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - XpPermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - XpZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - XpVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - XpEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ResourceBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ResourcePermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ResourceZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ResourceVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ResourceEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - MoneyBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - MoneyPermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - MoneyZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - MoneyVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - MoneyEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ReputationBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ReputationPermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ReputationZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ReputationVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - ReputationEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, - WalletProp = new WalletData { Beans = 999, Epoch = 1462889864 }, - LoyaltyProp = new LoyaltyData { Current = 0, Lifetime = 0, Tier = 0 }, - LevelProp = cd.Level, - EffectiveLevelProp = cd.Level, - LevelResetCountProp = 0, - OldestDeployablesProp = new OldestDeployablesField { Data = Array.Empty() }, - PerkRespecsProp = 0, - ArcStatusProp = null, - LeaveZoneTimeProp = null, - ChatMuteStatusProp = 0, - TimedDailyRewardProp = new TimedDailyRewardData - { - Unk1 = 0, - Unk2 = 0, - Unk3 = 0, - Unk4 = 0, - Unk5 = 0 - }, - TimedDailyRewardResultProp = null, - SinCardTypeProp = 0, - SinCardFields_0Prop = null, - SinCardFields_1Prop = null, - SinCardFields_2Prop = null, - SinCardFields_3Prop = null, - SinCardFields_4Prop = null, - SinCardFields_5Prop = null, - SinCardFields_6Prop = null, - SinCardFields_7Prop = null, - SinCardFields_8Prop = null, - SinCardFields_9Prop = null, - SinCardFields_10Prop = null, - SinCardFields_11Prop = null, - SinCardFields_12Prop = null, - SinCardFields_13Prop = null, - SinCardFields_14Prop = null, - SinCardFields_15Prop = null, - SinCardFields_16Prop = null, - SinCardFields_17Prop = null, - SinCardFields_18Prop = null, - SinCardFields_19Prop = null, - SinCardFields_20Prop = null, - SinCardFields_21Prop = null, - SinCardFields_22Prop = null, - AssetOverridesProp = assetOverrides, - FriendCountProp = 0, // :'( - CAISStatusProp = new CAISStatusData { State = CAISStatusData.CAISState.None, Elapsed = 0 }, - ScalingLevelProp = 0, - PvPRankProp = 0, - PvPRankPointsProp = 0, - PvPTokensProp = 0, - BountyPointsLastClaimedProp = 0, - EliteLevelProp = 1 - }; - - var combatController = new AeroMessages.GSS.V66.Character.Controller.CombatController - { - RunSpeedMultProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - FwdRunSpeedMultProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - JumpHeightMultProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - AirControlMultProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - ThrustStrengthMultProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - ThrustAirControlProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - FrictionProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - AmmoConsumptionProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - MaxTurnRateProp = new StatMultiplierData { Value = 0f, Time = shard.CurrentTime }, - TurnSpeedProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - TimeDilationProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - FireRateModifierProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - AccuracyModifierProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - GravityMultProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - AirResistanceMultProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - WeaponChargeupModProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - WeaponDamageDealtModProp = new StatMultiplierData { Value = 1.0f, Time = shard.CurrentTime }, - FireMode_0Prop = new FireModeData { Mode = 0, Time = shard.CurrentTime }, - FireMode_1Prop = new FireModeData { Mode = 0, Time = shard.CurrentTime }, - WeaponIndexProp = new WeaponIndexData { Time = shard.CurrentTime }, - WeaponFireBaseTimeProp = new WeaponFireBaseTimeData { ChangeTime = 0, Unk = 0 }, - WeaponAgilityModProp = 1.0f, - CombatFlagsProp = new CombatFlagsData { Value = 0, Time = shard.CurrentTime }, - PermissionFlagsProp = new PermissionFlagsData - { - Time = shard.CurrentTime, - Value = 0ul - | PermissionFlagsData.CharacterPermissionFlags.movement - | PermissionFlagsData.CharacterPermissionFlags.sprint - | PermissionFlagsData.CharacterPermissionFlags.unk_3 - | PermissionFlagsData.CharacterPermissionFlags.jump - | PermissionFlagsData.CharacterPermissionFlags.weapon - | PermissionFlagsData.CharacterPermissionFlags.unk_5 - | PermissionFlagsData.CharacterPermissionFlags.abilities - | PermissionFlagsData.CharacterPermissionFlags.unk_7 - | PermissionFlagsData.CharacterPermissionFlags.jetpack - | PermissionFlagsData.CharacterPermissionFlags.unk_13 - | PermissionFlagsData.CharacterPermissionFlags.unk_14 - | PermissionFlagsData.CharacterPermissionFlags.unk_15 - | PermissionFlagsData.CharacterPermissionFlags.crouch - | PermissionFlagsData.CharacterPermissionFlags.unk_21 - | PermissionFlagsData.CharacterPermissionFlags.calldown_abilities - | PermissionFlagsData.CharacterPermissionFlags.unk_23 - | PermissionFlagsData.CharacterPermissionFlags.unk_24 - | PermissionFlagsData.CharacterPermissionFlags.unk_25 - | PermissionFlagsData.CharacterPermissionFlags.unk_26 - | PermissionFlagsData.CharacterPermissionFlags.self_revive - - // | PermissionFlagsData.CharacterPermissionFlags.respawn_input - | PermissionFlagsData.CharacterPermissionFlags.battleframe_abilities - | PermissionFlagsData.CharacterPermissionFlags.unk_31 - }, - NemesesProp = new NemesesData { Values = Array.Empty() }, - SuperChargeProp = new SuperChargeData { Value = 0, Op = 0 } - }; - var effectsController = new LocalEffectsController(); - var missionController = new AeroMessages.GSS.V66.Character.Controller.MissionAndMarkerController(); - - // Temp - var observer = new ObserverView - { - StaticInfoProp = staticInfo, - SpawnTimeProp = shard.CurrentTime, - EffectsFlagsProp = 0, - GibVisualsIDProp = gibVisuals, - ProcessDelayProp = processDelay, - CharacterStateProp = characterState, - HostilityInfoProp = hostilityInfo, - PersonalFactionStanceProp = null, - CurrentHealthPctProp = 100, - MaxHealthProp = maxHealth, - EmoteIDProp = emote, - AttachedToProp = null, - SnapMountProp = 0, - SinFlagsProp = 0, - SinFactionsAcquiredByProp = null, - SinTeamsAcquiredByProp = null, - ArmyGUIDProp = baseController.ArmyGUIDProp, - OwnerIdProp = 0, - NPCTypeProp = 0, - DockedParamsProp = dockedParams, - LookAtTargetProp = null, - WaterLevelAndDescProp = 0, - CarryableObjects_0Prop = null, - CarryableObjects_1Prop = null, - CarryableObjects_2Prop = null, - RespawnTimesProp = null, - SinCardTypeProp = 0, - SinCardFields_0Prop = null, - SinCardFields_1Prop = null, - SinCardFields_2Prop = null, - SinCardFields_3Prop = null, - SinCardFields_4Prop = null, - SinCardFields_5Prop = null, - SinCardFields_6Prop = null, - SinCardFields_7Prop = null, - SinCardFields_8Prop = null, - SinCardFields_9Prop = null, - SinCardFields_10Prop = null, - SinCardFields_11Prop = null, - SinCardFields_12Prop = null, - SinCardFields_13Prop = null, - SinCardFields_14Prop = null, - SinCardFields_15Prop = null, - SinCardFields_16Prop = null, - SinCardFields_17Prop = null, - SinCardFields_18Prop = null, - SinCardFields_19Prop = null, - SinCardFields_20Prop = null, - SinCardFields_21Prop = null, - SinCardFields_22Prop = null, - AssetOverridesProp = assetOverrides - }; - - var combat = new CombatView - { - FireMode_0Prop = combatController.FireMode_0Prop, - FireMode_1Prop = combatController.FireMode_1Prop, - WeaponIndexProp = combatController.WeaponIndexProp, - WeaponAgilityModProp = combatController.WeaponAgilityModProp, - CombatFlagsProp = combatController.CombatFlagsProp, - MimicParentProp = new EntityId { Backing = 0 }, - MimicOffsetProp = Vector3.Zero - }; - - var equipment = new EquipmentView - { - VisualOverridesProp = visualOverrides, - CurrentEquipmentProp = currentEquipment, - LevelProp = baseController.CurrentDurabilityPctProp, - CurrentDurabilityPctProp = baseController.CurrentDurabilityPctProp, - CharacterStatsProp = characterStats, - ScalingLevelProp = baseController.ScalingLevelProp, - PvPRankProp = baseController.PvPRankProp, - EliteLevelProp = baseController.EliteLevelProp - }; - - var movement = new MovementView - { - MovementProp = new MovementData - { - Position = spawnPose.Position, - Rotation = spawnPose.Rotation, - Aim = spawnPose.AimDirection, - MovementState = spawnPose.MovementState, - Time = spawnPose.Time - } - }; - - // Controllers - client.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(baseController, player.EntityId, player.PlayerId); - client.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(combatController, player.EntityId, player.PlayerId); - client.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(effectsController, player.EntityId, player.PlayerId); - client.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(missionController, player.EntityId, player.PlayerId); - client.NetChannels[ChannelType.ReliableGss].SendIAero(new CharacterLoaded(), player.EntityId); - - // Views - client.NetChannels[ChannelType.ReliableGss].SendIAero(observer, player.EntityId, 3); - client.NetChannels[ChannelType.ReliableGss].SendIAero(equipment, player.EntityId, 3); - client.NetChannels[ChannelType.ReliableGss].SendIAero(combat, player.EntityId, 3); - client.NetChannels[ChannelType.ReliableGss].SendIAero(movement, player.EntityId, 3); } [MessageID((byte)Commands.FetchQueueInfo)] @@ -657,66 +135,17 @@ public void MovementInput(INetworkClient client, IPlayer player, ulong entityId, return; // can't move if you're dead (or at least shouldn't o.o") } - var poseData = movementInput.PoseData; - var posRotState = poseData.PosRotState; - - player.CharacterEntity.Position = posRotState.Pos; - player.CharacterEntity.Rotation = posRotState.Rot; - player.CharacterEntity.Velocity = poseData.Velocity; - player.CharacterEntity.AimDirection = poseData.Aim; - - var movementStateValue = posRotState.MovementState; - player.CharacterEntity.MovementStateContainer.MovementState = (CharMovementState)movementStateValue; - - if (player.CharacterEntity.MovementStateContainer.InvalidFlags != CharMovementState.None) - { - _logger.Error($"Unmapped {nameof(CharMovementState)} encountered! \n{player.CharacterEntity.MovementStateContainer}"); - } - - var timeSinceLastJumpValue = poseData.TimeSinceLastJump; - player.CharacterEntity.TimeSinceLastJump ??= - timeSinceLastJumpValue >= 0 ? Convert.ToUInt16(timeSinceLastJumpValue) : throw new ArgumentOutOfRangeException($"{nameof(poseData.TimeSinceLastJump)} is <0, but we're only allowing >=0. This is bad!"); - - // _logger.Warning( "Movement Unknown1: {0:X4} {1:X4} {2:X4} {3:X4} {4:X4}", pkt.UnkUShort1, pkt.UnkUShort2, pkt.UnkUShort3, pkt.UnkUShort4, pkt.LastJumpTimer ); - var resp = new ConfirmedPoseUpdate - { - PoseData = new MovementPoseData - { - ShortTime = movementInput.ShortTime, - MovementType = MovementDataType.PosRotState, - MovementUnk3 = 0, // ToDo: Find out why this has to be 0; What does it control? - PosRotState = new MovementPosRotState - { - Pos = player.CharacterEntity.Position, - Rot = player.CharacterEntity.Rotation, - MovementState = (short)player.CharacterEntity.MovementStateContainer.MovementState // ToDo: This was ushort previously! - }, - Velocity = player.CharacterEntity.Velocity, - JetpackEnergy = poseData.JetpackEnergy, - GroundTimePositiveAirTimeNegative = poseData.GroundTimePositiveAirTimeNegative, // Somehow affects gravity - TimeSinceLastJump = poseData.TimeSinceLastJump, - HaveDebugData = 0 - }, - NextShortTime = unchecked((ushort)(movementInput.ShortTime + 90)) - }; - - // ToDo: Set "Aim" property of response if the input had the respective flag - // ToDo: Handle JetPackEnergy changes / add to CharacterEntity class - client.NetChannels[ChannelType.UnreliableGss].SendIAero(resp, entityId, 0, typeof(Events)); - - if (player.CharacterEntity.TimeSinceLastJump.HasValue && poseData.TimeSinceLastJump > player.CharacterEntity.TimeSinceLastJump.Value) - { - player.Jump(); - } + client.AssignedShard.Movement.CharacterMovementInput(client, player.CharacterEntity, movementInput); } [MessageID((byte)Commands.SetMovementSimulation)] public void SetMovementSimulation(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // ToDo: Implement BaseController.SetMovementSimulation - var setMovementSimulation = packet.Unpack(); - - LogMissingImplementation(nameof(SetMovementSimulation), entityId, packet, _logger); + // Dear client, thanks for informing us about often you will update us. + // We will continue to rely on your support, whilst continuing to do nothing ourselves. + // Best regards, TMW + // var setMovementSimulation = packet.Unpack(); + // LogMissingImplementation(nameof(SetMovementSimulation), entityId, packet, _logger); } [MessageID((byte)Commands.BagInventorySettings)] @@ -947,19 +376,42 @@ public void VehicleCalldownRequest(INetworkClient client, IPlayer player, ulong [MessageID((byte)Commands.SetEffectsFlag)] public void SetEffectsFlag(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // ToDo: Implement BaseController.SetEffectsFlag - LogMissingImplementation(nameof(SetEffectsFlag), entityId, packet, _logger); + var query = packet.Unpack(); + player.CharacterEntity.SetEffectsFlags(query.Flashlight); + } - packet.Unpack(); + [MessageID((byte)Commands.PerformEmote)] + public void PerformEmote(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) + { + var query = packet.Unpack(); + player.CharacterEntity.SetEmote(new EmoteData { Id = query.EmoteId, Time = query.Time }); } [MessageID((byte)Commands.ClientQueryInteractionStatus)] public void ClientQueryInteractionStatus(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // ToDo: Implement BaseController.ClientQueryInteractionStatus - LogMissingImplementation(nameof(ClientQueryInteractionStatus), entityId, packet, _logger); - - packet.Unpack(); + var query = packet.Unpack(); + ulong requestedEntityId = query.Entity.Id; + var found = client.AssignedShard.Entities.TryGetValue(requestedEntityId, out var entity); + if (found) + { + if (entity.IsInteractable()) + { + // TODO: Support interactable entities, get interaction data and send AddOrUpdateInteractives + } + else + { + var response = new RemoveInteractives + { + Entities = new EntityId[] { new EntityId { Backing = requestedEntityId } } + }; + client.NetChannels[ChannelType.ReliableGss].SendIAero(response, player.CharacterEntity.EntityId); + } + } + else + { + _logger.Verbose("ClientQueryInteractionStatus entity {0:x8} not found!", requestedEntityId); + } } [MessageID((byte)Commands.ResourceLocationInfosRequest)] diff --git a/UdpHosts/GameServer/Controllers/Character/CombatController.cs b/UdpHosts/GameServer/Controllers/Character/CombatController.cs index 25948d1d..926404e5 100644 --- a/UdpHosts/GameServer/Controllers/Character/CombatController.cs +++ b/UdpHosts/GameServer/Controllers/Character/CombatController.cs @@ -1,4 +1,5 @@ -using AeroMessages.GSS.V66.Character.Command; +using AeroMessages.GSS.V66.Character; +using AeroMessages.GSS.V66.Character.Command; using AeroMessages.GSS.V66.Character.Event; using GameServer.Enums.GSS.Character; using GameServer.Extensions; @@ -10,9 +11,11 @@ namespace GameServer.Controllers.Character; [ControllerID(Enums.GSS.Controllers.Character_CombatController)] public class CombatController : Base { + private ILogger _logger; + public override void Init(INetworkClient client, IPlayer player, IShard shard, ILogger logger) { - // TODO: Implement + _logger = logger; } [MessageID((byte)Commands.FireInputIgnored)] @@ -24,7 +27,8 @@ public void FireInputIgnored(INetworkClient client, IPlayer player, ulong entity [MessageID((byte)Commands.FireBurst)] public void FireBurst(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // TODO: Implement + var query = packet.Unpack(); + player.CharacterEntity.SetFireBurst(query.Time); } [MessageID((byte)Commands.FireWeaponProjectile)] @@ -40,37 +44,72 @@ public void FireWeaponProjectile(INetworkClient client, IPlayer player, ulong en MoreData = fireWeaponProjectile.ShooterVelocity }; - client.NetChannels[ChannelType.ReliableGss].SendIAero(fireWeaponProjectile, player.CharacterEntity.EntityId); + // TODO: This should be sent remote + // FIXME: Because WeaponProjectileFired has two AeroMessageId and SendIAero grabs the first one, it tries to send this to the CombatController instead of the CombatView which is invalid + // client.NetChannels[ChannelType.ReliableGss].SendIAero(weaponProjectileFired, player.CharacterEntity.EntityId); } [MessageID((byte)Commands.FireEnd)] public void FireEnd(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // TODO: Implement + var query = packet.Unpack(); + player.CharacterEntity.SetFireEnd(query.Time); + } + + [MessageID((byte)Commands.FireCancel)] + public void FireCancel(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) + { + var query = packet.Unpack(); + player.CharacterEntity.SetFireCancel(query.Time); } [MessageID((byte)Commands.UseScope)] public void UseScope(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // TODO: Implement + var query = packet.Unpack(); + player.CharacterEntity.SetFireMode(1, new FireModeData + { + Mode = query.InScope, + Time = query.Time, + }); } [MessageID((byte)Commands.SelectWeapon)] public void SelectWeapon(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // TODO: Implement + var query = packet.Unpack(); + player.CharacterEntity.SetWeaponIndex(new WeaponIndexData + { + Index = query.SelectedWeaponIndex, + Unk1 = query.Unk3, + Unk2 = 0, + Time = query.Time, + }); } [MessageID((byte)Commands.SelectFireMode)] public void SelectFireMode(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // TODO: Implement + var query = packet.Unpack(); + player.CharacterEntity.SetFireMode(0, new FireModeData + { + Mode = query.FireMode, + Time = query.Time, + }); } [MessageID((byte)Commands.ReloadWeapon)] public void ReloadWeapon(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { - // TODO: Implement + var query = packet.Unpack(); + player.CharacterEntity.SetWeaponReloaded(query.Time); + } + + [MessageID((byte)Commands.CancelReload)] + public void CancelReload(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) + { + var query = packet.Unpack(); + player.CharacterEntity.SetWeaponReloadCancelled(query.Time); } [MessageID((byte)Commands.ActivateAbility)] diff --git a/UdpHosts/GameServer/Controllers/Generic.cs b/UdpHosts/GameServer/Controllers/Generic.cs deleted file mode 100644 index 0fca09c5..00000000 --- a/UdpHosts/GameServer/Controllers/Generic.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using AeroMessages.GSS.V66.Generic; -using GameServer.Enums; -using GameServer.Enums.GSS.Generic; -using GameServer.Extensions; -using GameServer.Packets; -using GameServer.Packets.Control; -using Serilog; - -namespace GameServer.Controllers; - -[ControllerID(Enums.GSS.Controllers.Generic)] -public class Generic : Base -{ - public override void Init(INetworkClient client, IPlayer player, IShard shard, ILogger logger) - { - } - - [MessageID((byte)Commands.ScheduleUpdateRequest)] - public void ScheduleUpdateRequest(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) - { - var updateRequest = packet.Unpack(); - - player.LastRequestedUpdate = client.AssignedShard.CurrentTime; - player.RequestedClientTime = Math.Max(updateRequest.Time, player.RequestedClientTime); - - if (!player.FirstUpdateRequested) - { - player.FirstUpdateRequested = true; - player.Respawn(); - } - - // Program.Logger.Error( "Update scheduled" ); - } - - [MessageID((byte)Commands.RequestLogout)] - public void RequestLogout(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) - { - var resp = new CloseConnection { Unknown1 = 0 }; - client.NetChannels[ChannelType.Control].SendClass(resp, typeof(ControlPacketType)); - } -} \ No newline at end of file diff --git a/UdpHosts/GameServer/Controllers/Generic2.cs b/UdpHosts/GameServer/Controllers/GenericShard.cs similarity index 72% rename from UdpHosts/GameServer/Controllers/Generic2.cs rename to UdpHosts/GameServer/Controllers/GenericShard.cs index 93bae6ff..e972d0ad 100644 --- a/UdpHosts/GameServer/Controllers/Generic2.cs +++ b/UdpHosts/GameServer/Controllers/GenericShard.cs @@ -1,16 +1,38 @@ -using GameServer.Enums.GSS.Generic; +using System; +using AeroMessages.GSS.V66.Generic; +using GameServer.Enums; +using GameServer.Enums.GSS.Generic; +using GameServer.Extensions; using GameServer.Packets; +using GameServer.Packets.Control; using Serilog; namespace GameServer.Controllers; -[ControllerID(Enums.GSS.Controllers.Generic2)] -public class Generic2 : Base +[ControllerID(Enums.GSS.Controllers.GenericShard)] +public class GenericShard : Base { public override void Init(INetworkClient client, IPlayer player, IShard shard, ILogger logger) { } + [MessageID((byte)Commands.ScheduleUpdateRequest)] + public void ScheduleUpdateRequest(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) + { + var updateRequest = packet.Unpack(); + + player.LastRequestedUpdate = client.AssignedShard.CurrentTime; + player.RequestedClientTime = Math.Max(updateRequest.Time, player.RequestedClientTime); + + if (!player.FirstUpdateRequested) + { + player.FirstUpdateRequested = true; + player.Respawn(); + } + + // Program.Logger.Error( "Update scheduled" ); + } + [MessageID((byte)Commands.UIToEncounterMessage)] public void UiToEncounter(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { @@ -44,6 +66,8 @@ public void RequestLeaveZone(INetworkClient client, IPlayer player, ulong entity [MessageID((byte)Commands.RequestLogout)] public void RequestLogout(INetworkClient client, IPlayer player, ulong entityId, GamePacket packet) { + var resp = new CloseConnection { Unknown1 = 0 }; + client.NetChannels[ChannelType.Control].SendClass(resp, typeof(ControlPacketType)); } [MessageID((byte)Commands.RequestEncounterInfo)] diff --git a/UdpHosts/GameServer/Data/CharacterGender.cs b/UdpHosts/GameServer/Data/CharacterGender.cs index dcaec5ad..3ed4423d 100644 --- a/UdpHosts/GameServer/Data/CharacterGender.cs +++ b/UdpHosts/GameServer/Data/CharacterGender.cs @@ -2,6 +2,13 @@ public enum CharacterGender : byte { + /// + /// Male + /// Male = 0, + + /// + /// Female + /// Female = 1 } \ No newline at end of file diff --git a/UdpHosts/GameServer/Data/CharacterRace.cs b/UdpHosts/GameServer/Data/CharacterRace.cs index f6db2aba..711e968e 100644 --- a/UdpHosts/GameServer/Data/CharacterRace.cs +++ b/UdpHosts/GameServer/Data/CharacterRace.cs @@ -2,7 +2,63 @@ public enum CharacterRace : byte { + /// + /// Default + /// Human = 0, - Unknown = 1, - DarkOne = 2 + + /// + /// Deprecated + /// + Falcari = 1, + + /// + /// Chosen + /// + DarkOne = 2, + + /// + /// Deprecated + /// + Rakshar = 3, + + /// + /// Deprecated + /// + Ascendant = 4, + + /// + /// Deprecated + /// + Celestial = 5, + + /// + /// TODO + /// + Monster = 6, + + /// + /// TODO + /// + Friendly = 7, + + /// + /// TODO + /// + Melding = 8, + + /// + /// Aranha and co + /// + Gaea = 9, + + /// + /// Oww you shot me + /// + Bandit = 10, + + /// + /// Brontodons? + /// + Neutral = 11, } \ No newline at end of file diff --git a/UdpHosts/GameServer/Entities/BaseEntity.cs b/UdpHosts/GameServer/Entities/BaseEntity.cs index 346b6bed..dc72ec4b 100644 --- a/UdpHosts/GameServer/Entities/BaseEntity.cs +++ b/UdpHosts/GameServer/Entities/BaseEntity.cs @@ -5,19 +5,24 @@ namespace GameServer.Entities; public class BaseEntity : IEntity { - public BaseEntity(IShard owner, ulong id) + public BaseEntity(IShard shard, ulong id) { - Owner = owner; + Shard = shard; EntityId = id; ControllerRefMap = new ConcurrentDictionary(); } public ulong EntityId { get; } - public IShard Owner { get; } + public IShard Shard { get; } public IDictionary ControllerRefMap { get; } + public bool IsInteractable() + { + return false; + } + public void RegisterController(Enums.GSS.Controllers controller) { - ControllerRefMap.Add(controller, Owner.AssignNewRefId(this, controller)); + ControllerRefMap.Add(controller, Shard.AssignNewRefId(this, controller)); } } \ No newline at end of file diff --git a/UdpHosts/GameServer/Entities/Character/CharMovementState.cs b/UdpHosts/GameServer/Entities/Character/CharMovementState.cs index be5fbb71..01f4da05 100644 --- a/UdpHosts/GameServer/Entities/Character/CharMovementState.cs +++ b/UdpHosts/GameServer/Entities/Character/CharMovementState.cs @@ -3,6 +3,7 @@ namespace GameServer.Entities.Character; [Flags] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "Document when overhauling this")] public enum CharMovementState : short { JetSprint = -1 << 14, diff --git a/UdpHosts/GameServer/Entities/Character/Character.cs b/UdpHosts/GameServer/Entities/Character/Character.cs index 8f9f75f7..607decd9 100644 --- a/UdpHosts/GameServer/Entities/Character/Character.cs +++ b/UdpHosts/GameServer/Entities/Character/Character.cs @@ -1,37 +1,958 @@ -using System.Numerics; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using AeroMessages.Common; +using AeroMessages.GSS.V66; +using AeroMessages.GSS.V66.Character; +using AeroMessages.GSS.V66.Character.Command; +using AeroMessages.GSS.V66.Character.Controller; +using AeroMessages.GSS.V66.Character.View; +using GrpcGameServerAPIClient; namespace GameServer.Entities.Character; public class Character : BaseEntity { - public Character(IShard owner, ulong eid) - : base(owner, eid) + public Character(IShard shard, ulong eid) + : base(shard, eid) { - Position = new Vector3(); - Rotation = Quaternion.Identity; - Velocity = new Vector3(); - AimDirection = Vector3.UnitZ; - - Alive = false; - TimeSinceLastJump = null; - - /*RegisterController(Enums.GSS.Controllers.Character_BaseController); - RegisterController(Enums.GSS.Controllers.Character_CombatController); - RegisterController(Enums.GSS.Controllers.Character_MissionAndMarkerController); - RegisterController(Enums.GSS.Controllers.Character_LocalEffectsController);*/ + InitFields(); + InitViews(); } + public BaseController Character_BaseController { get; set; } + public CombatController Character_CombatController { get; set; } + public MissionAndMarkerController Character_MissionAndMarkerController { get; set; } + public LocalEffectsController Character_LocalEffectsController { get; set; } + public SpectatorController Character_SpectatorController { get; set; } + public ObserverView Character_ObserverView { get; set; } + public EquipmentView Character_EquipmentView { get; set; } + public CombatView Character_CombatView { get; set; } + public MovementView Character_MovementView { get; set; } + public TinyObjectView Character_TinyObjectView { get; set; } + + public INetworkPlayer Player { get; set; } + public bool IsPlayerControlled => Player != null; public Data.Character CharData { get; set; } public Vector3 Position { get; set; } public Quaternion Rotation { get; set; } public Vector3 Velocity { get; set; } public Vector3 AimDirection { get; set; } + public short MovementState { get; set; } + public ushort MovementShortTime { get; set; } public bool Alive { get; set; } - public ushort? TimeSinceLastJump { get; set; } + public short TimeSinceLastJump { get; set; } + + public StaticInfoData StaticInfo { get; set; } + public CharacterStateData CharacterState { get; set; } + public HostilityInfoData HostilityInfo { get; set; } + public MaxVital MaxShields { get; set; } + public MaxVital MaxHealth { get; set; } + public GibVisuals GibVisualsInfo { get; set; } + public ProcessDelayData ProcessDelay { get; set; } + public EmoteData Emote { get; set; } + public DockedParamsData DockedParams { get; set; } + public AssetOverridesField AssetOverrides { get; set; } + public VisualOverridesField VisualOverrides { get; set; } + public EquipmentData CurrentEquipment { get; set; } + public CharacterStatsData CharacterStats { get; set; } + public EnergyParamsData EnergyParams { get; set; } + public ScopeBubbleInfoData ScopeBubble { get; set; } + public CharacterSpawnPose SpawnPose { get; set; } + public byte EffectsFlags { get; set; } + public WeaponIndexData WeaponIndex { get; set; } + public FireModeData FireMode_0 { get; set; } + public FireModeData FireMode_1 { get; set; } + internal MovementStateContainer MovementStateContainer { get; set; } = new(); + public void LoadRemote(CharacterAndBattleframeVisuals remoteData) + { + Data.Character staticData = Data.Character.Load(0); + CharData = staticData; // Necessary for InitControllers + + StaticInfo = new StaticInfoData + { + DisplayName = remoteData.CharacterInfo.Name, + UniqueName = remoteData.CharacterInfo.Name, + Gender = (byte)remoteData.CharacterInfo.Gender, + Race = (byte)remoteData.CharacterInfo.Race, + CharInfoId = staticData.CharInfoID, // 1 for players + HeadMain = (uint)remoteData.CharacterVisuals.Head.Id, + Eyes = (uint)remoteData.CharacterVisuals.Eyes.Id, + Unk_1 = 0xff, + IsNPC = 0, + StaffFlags = 0x3, + CharacterTypeId = staticData.CharVisuals.CharTypeID, + VoiceSet = (uint)remoteData.CharacterVisuals.VoiceSet.Id, + TitleId = (ushort)remoteData.CharacterInfo.TitleId, + NameLocalizationId = staticData.NameLocalizationID, // Not relevant for players + HeadAccessories = remoteData.CharacterVisuals.HeadAccessories.ToList().Select(item => (uint)item.Id).ToArray(), + LoadoutVehicle = (uint)remoteData.CharacterVisuals.Vehicle.Id, + LoadoutGlider = (uint)remoteData.CharacterVisuals.Glider.Id, + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = new uint[5] + { + remoteData.CharacterVisuals.SkinColor.Value.Color, + remoteData.CharacterVisuals.LipColor.Value.Color, + remoteData.CharacterVisuals.EyeColor.Value.Color, + remoteData.CharacterVisuals.HairColor.Value.Color, + remoteData.CharacterVisuals.FacialHairColor.Value.Color + }, + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = remoteData.CharacterVisuals.Ornaments.ToList().Select(item => (uint)item.Id).ToArray(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + }, + ArmyTag = staticData.Army.Name + }; + Character_ObserverView.StaticInfoProp = StaticInfo; + + CurrentEquipment = new EquipmentData + { + Chassis = new SlottedItem + { + SdbId = 77733, // staticData.Loadout.ChassisID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = new VisualsDecalsBlock[] + { + new() + { + DecalId = 10000, + Color = 4294967295, + Usage = 255, + Transform = new HalfVector4[] + { + new() + { + X = new HalfFloat { Value = 10935 }, + Y = new HalfFloat { Value = 9478 }, + Z = new HalfFloat { Value = 0 }, + W = new HalfFloat { Value = 8106 } + }, + new() + { + X = new HalfFloat { Value = 42272 }, + Y = new HalfFloat { Value = 43680 }, + Z = new HalfFloat { Value = 9380 }, + W = new HalfFloat { Value = 43573 } + }, + new() + { + X = new HalfFloat { Value = 9592 }, + Y = new HalfFloat { Value = 12012 }, + Z = new HalfFloat { Value = 44736 }, + W = new HalfFloat { Value = 15867 } + } + } + } + }, + Gradients = Array.Empty(), + Colors = ((List)staticData.ChassisVisuals.Colors).ToArray(), + Palettes = new VisualsPaletteBlock[] { new() { PaletteId = 85163, PaletteType = 0 } }, + Patterns = new VisualsPatternBlock[] + { + new() + { + PatternId = 10022, + TransformValues = new HalfVector4 + { + X = new HalfFloat { Value = 0 }, + Y = new HalfFloat { Value = 16384 }, + Z = new HalfFloat { Value = 0 }, + W = new HalfFloat { Value = 0 } + } + } + }, + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + Backpack = new SlottedItem + { + SdbId = 0, // staticData.Loadout.BackpackID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = Array.Empty(), + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + PrimaryWeapon = new SlottedWeapon + { + Item = new SlottedItem + { + SdbId = staticData.Loadout.PrimaryWeaponID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = new uint[] { 0x322c0000, 0x543110a2, 0x65b42104 }, + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + Unk1 = 0, + Unk2 = 0 + }, + SecondaryWeapon = new SlottedWeapon + { + Item = new SlottedItem + { + SdbId = staticData.Loadout.SecondaryWeaponID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = new uint[] { 0x322c0000, 0x543110a2, 0x65b42104 }, + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + Unk1 = 0, + Unk2 = 0 + }, + EndUnk1 = 0, + EndUnk2 = 0 + }; + Character_EquipmentView.CurrentEquipmentProp = CurrentEquipment; + } + public void Load(ulong characterId) { CharData = Data.Character.Load(characterId); + + var cd = CharData; + StaticInfo = new StaticInfoData + { + DisplayName = cd.Name, + UniqueName = cd.Name, + Gender = (byte)cd.Gender, + Race = (byte)cd.Race, + CharInfoId = cd.CharInfoID, + HeadMain = cd.CharVisuals.HeadMain, + Eyes = cd.CharVisuals.Eyes, + Unk_1 = 0xff, + IsNPC = 0, + StaffFlags = 0x3, + CharacterTypeId = cd.CharVisuals.CharTypeID, + VoiceSet = cd.VoiceSet, + TitleId = cd.TitleID, + NameLocalizationId = cd.NameLocalizationID, + HeadAccessories = ((List)cd.CharVisuals.HeadAccessories).ToArray(), + LoadoutVehicle = cd.Loadout.VehicleID, + LoadoutGlider = cd.Loadout.GliderID, + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = ((List)cd.CharVisuals.Colors).ToArray(), + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = ((List)cd.CharVisuals.OrnamentGroups).ToArray(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + }, + ArmyTag = cd.Army.Name + }; + Character_ObserverView.StaticInfoProp = StaticInfo; + + CurrentEquipment = new EquipmentData + { + Chassis = new SlottedItem + { + SdbId = cd.Loadout.ChassisID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = new VisualsDecalsBlock[] + { + new() + { + DecalId = 10000, + Color = 4294967295, + Usage = 255, + Transform = new HalfVector4[] + { + new() + { + X = new HalfFloat { Value = 10935 }, + Y = new HalfFloat { Value = 9478 }, + Z = new HalfFloat { Value = 0 }, + W = new HalfFloat { Value = 8106 } + }, + new() + { + X = new HalfFloat { Value = 42272 }, + Y = new HalfFloat { Value = 43680 }, + Z = new HalfFloat { Value = 9380 }, + W = new HalfFloat { Value = 43573 } + }, + new() + { + X = new HalfFloat { Value = 9592 }, + Y = new HalfFloat { Value = 12012 }, + Z = new HalfFloat { Value = 44736 }, + W = new HalfFloat { Value = 15867 } + } + } + } + }, + Gradients = Array.Empty(), + Colors = ((List)cd.ChassisVisuals.Colors).ToArray(), + Palettes = new VisualsPaletteBlock[] { new() { PaletteId = 85163, PaletteType = 0 } }, + Patterns = new VisualsPatternBlock[] + { + new() + { + PatternId = 10022, + TransformValues = new HalfVector4 + { + X = new HalfFloat { Value = 0 }, + Y = new HalfFloat { Value = 16384 }, + Z = new HalfFloat { Value = 0 }, + W = new HalfFloat { Value = 0 } + } + } + }, + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + Backpack = new SlottedItem + { + SdbId = cd.Loadout.BackpackID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = Array.Empty(), + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + PrimaryWeapon = new SlottedWeapon + { + Item = new SlottedItem + { + SdbId = cd.Loadout.PrimaryWeaponID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = new uint[] { 0x322c0000, 0x543110a2, 0x65b42104 }, + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + Unk1 = 0, + Unk2 = 0 + }, + SecondaryWeapon = new SlottedWeapon + { + Item = new SlottedItem + { + SdbId = cd.Loadout.SecondaryWeaponID, + SlotIndex = 0, + Flags = 0, + Unk2 = 0, + Modules = Array.Empty(), + Visuals = new VisualsBlock + { + Decals = Array.Empty(), + Gradients = Array.Empty(), + Colors = new uint[] { 0x322c0000, 0x543110a2, 0x65b42104 }, + Palettes = Array.Empty(), + Patterns = Array.Empty(), + OrnamentGroupIds = Array.Empty(), + CziMapAssetIds = Array.Empty(), + MorphWeights = Array.Empty(), + Overlays = Array.Empty() + } + }, + Unk1 = 0, + Unk2 = 0 + }, + EndUnk1 = 0, + EndUnk2 = 0 + }; + Character_EquipmentView.CurrentEquipmentProp = CurrentEquipment; + } + + public void SetAimDirection(Vector3 newDirection) + { + AimDirection = newDirection; + RefreshMovementView(); + } + + public void SetCharacterState(CharacterStateData.CharacterStatus characterStatus, uint time) + { + CharacterState = new CharacterStateData + { + State = characterStatus, Time = time + }; + Character_BaseController.CharacterStateProp = CharacterState; + Character_ObserverView.CharacterStateProp = CharacterState; + } + + public void SetControllingPlayer(INetworkPlayer player) + { + Player = player; + InitControllers(); + } + + public void SetEffectsFlags(byte value) + { + EffectsFlags = value; + Character_ObserverView.EffectsFlagsProp = EffectsFlags; + } + + public void SetEmote(EmoteData value) + { + Emote = value; + Character_ObserverView.EmoteIDProp = value; + Character_BaseController.EmoteIDProp = value; + } + + public void SetFireBurst(uint time) + { + Character_CombatView.WeaponBurstFiredProp = time; + } + + public void SetFireCancel(uint time) + { + Character_CombatView.WeaponBurstCancelledProp = time; + } + + public void SetFireEnd(uint time) + { + Character_CombatView.WeaponBurstEndedProp = time; + } + + public void SetFireMode(byte index, FireModeData value) + { + switch (index) + { + case 0: + FireMode_0 = value; + Character_CombatController.FireMode_0Prop = FireMode_0; + Character_CombatView.FireMode_0Prop = FireMode_0; + break; + case 1: + FireMode_1 = value; + Character_CombatController.FireMode_1Prop = FireMode_1; + Character_CombatView.FireMode_1Prop = FireMode_1; + break; + } + } + + public void SetPoseData(MovementPoseData poseData, ushort shortTime) + { + Position = poseData.PosRotState.Pos; + Rotation = poseData.PosRotState.Rot; + MovementState = poseData.PosRotState.MovementState; + Velocity = poseData.Velocity; + AimDirection = poseData.Aim; + MovementShortTime = shortTime; + RefreshMovementView(); + } + + public void SetPosition(Vector3 newPosition) + { + Position = newPosition; + RefreshMovementView(); + } + + public void SetWeaponReloaded(uint time) + { + Character_CombatView.WeaponReloadedProp = time; + } + + public void SetWeaponReloadCancelled(uint time) + { + Character_CombatView.WeaponReloadCancelledProp = time; + } + + public void SetRotation(Quaternion newRotation) + { + Rotation = newRotation; + RefreshMovementView(); + } + + public void SetSpawnPose() + { + SpawnPose = new CharacterSpawnPose + { + Time = Shard.CurrentTime, + Position = Position, + Rotation = Rotation, + AimDirection = AimDirection, + Velocity = Velocity, + MovementState = 0x1000, + Unk1 = 0, + Unk2 = 0, + JetpackEnergy = 0x639c, + AirGroundTimer = 0, + JumpTimer = 0, + HaveDebugData = 0 + }; + Character_BaseController.SpawnPoseProp = SpawnPose; + Character_BaseController.SpawnTimeProp = Shard.CurrentTime; + Character_ObserverView.SpawnTimeProp = Shard.CurrentTime; + } + + public void SetSpawnTime(uint time) + { + Character_BaseController.SpawnTimeProp = time; + Character_ObserverView.SpawnTimeProp = time; + } + + public void SetWeaponIndex(WeaponIndexData value) + { + WeaponIndex = value; + Character_CombatController.WeaponIndexProp = value; + Character_CombatView.WeaponIndexProp = value; + } + + private void InitFields() + { + Position = new Vector3(); + Rotation = Quaternion.Identity; + Velocity = new Vector3(); + AimDirection = Vector3.UnitZ; + MovementState = 0x1000; + MovementShortTime = Shard.CurrentShortTime; + + Alive = false; + TimeSinceLastJump = 0; + + StaticInfo = new StaticInfoData(); + CharacterState = new CharacterStateData { State = CharacterStateData.CharacterStatus.Living, Time = Shard.CurrentTime }; + HostilityInfo = new HostilityInfoData { Flags = 0 | HostilityInfoData.HostilityFlags.Faction, FactionId = 1 }; + MaxShields = new MaxVital { Value = 0, Time = Shard.CurrentTime }; + MaxHealth = new MaxVital { Value = 19192, Time = Shard.CurrentTime }; + GibVisualsInfo = new GibVisuals { Id = 0, Time = Shard.CurrentTime }; + ProcessDelay = new ProcessDelayData { Unk1 = 30721, Unk2 = 236 }; + Emote = new EmoteData { Id = 0, Time = 0 }; + DockedParams = new DockedParamsData { Unk1 = new EntityId { Backing = 0 }, Unk2 = Vector3.Zero, Unk3 = 0 }; + AssetOverrides = new AssetOverridesField { Ids = Array.Empty() }; + VisualOverrides = new VisualOverridesField { Data = Array.Empty() }; + CurrentEquipment = new EquipmentData { }; + CharacterStats = new CharacterStatsData + { + ItemAttributes = new StatsData[] + { + new() { Id = 5, Value = 156.414169f }, new() { Id = 6, Value = 1037.8347f }, new() { Id = 7, Value = 177.44128f }, new() { Id = 12, Value = 16.250000f }, new() { Id = 35, Value = 300 }, + new() { Id = 36, Value = 250 }, new() { Id = 37, Value = 2.092090f }, new() { Id = 142, Value = 12.55f }, new() { Id = 143, Value = 1136 }, new() { Id = 144, Value = 18.433180f }, + new() { Id = 173, Value = 10 }, new() { Id = 186, Value = 11.40f }, new() { Id = 959, Value = 1 }, new() { Id = 1050, Value = 34.5f }, new() { Id = 1051, Value = 13.824884f }, + new() { Id = 1052, Value = 5.5f }, new() { Id = 1121, Value = 150 }, new() { Id = 1146, Value = 10.0f }, new() { Id = 1367, Value = 85 }, new() { Id = 1368, Value = 100 }, + new() { Id = 1370, Value = 65 }, new() { Id = 1371, Value = 120 }, new() { Id = 1372, Value = 140 }, new() { Id = 1377, Value = 140.531250f }, new() { Id = 1395, Value = 75 }, + new() { Id = 1419, Value = 32.769249f }, new() { Id = 1420, Value = 16901.744141f }, new() { Id = 1439, Value = 15279.667969f }, new() { Id = 1451, Value = 681 }, + new() { Id = 1583, Value = 1 }, new() { Id = 1620, Value = 5049.767090f }, new() { Id = 1622, Value = 8 }, new() { Id = 1733, Value = 1.800000f }, new() { Id = 1736, Value = 60 }, + new() { Id = 1737, Value = 5486.919434f }, new() { Id = 1746, Value = 9.320923f }, new() { Id = 1785, Value = 1.084000f }, new() { Id = 1835, Value = 5932.512207f }, + new() { Id = 1904, Value = 4 }, new() { Id = 1905, Value = 2 }, new() { Id = 1987, Value = 8 }, new() { Id = 2034, Value = 22 }, new() { Id = 2037, Value = 9887.518555f }, + new() { Id = 2039, Value = 9 }, new() { Id = 2042, Value = 12.252850f } + }, + Unk1 = 0, + WeaponA = Array.Empty(), + Unk2 = 0, + WeaponB = Array.Empty(), + Unk3 = 0, + AttributeCategories1 = Array.Empty(), + AttributeCategories2 = Array.Empty() + }; + + EnergyParams = new EnergyParamsData { Max = 1000.0f, Delay = 500, Recharge = 156.0f, Time = Shard.CurrentTime }; + ScopeBubble = new ScopeBubbleInfoData { Unk1 = 0, Unk2 = 0 }; + SpawnPose = new CharacterSpawnPose + { + Time = Shard.CurrentTime, + Position = Position, + Rotation = Rotation, + AimDirection = AimDirection, + Velocity = Velocity, + MovementState = 0x1000, + Unk1 = 0, + Unk2 = 0, + JetpackEnergy = 0x639c, + AirGroundTimer = 0, + JumpTimer = 0, + HaveDebugData = 0 + }; + + EffectsFlags = 0; + FireMode_0 = new FireModeData { Mode = 0, Time = Shard.CurrentTime }; + FireMode_1 = new FireModeData { Mode = 0, Time = Shard.CurrentTime }; + WeaponIndex = new WeaponIndexData { Index = 0, Unk1 = 1, Unk2 = 0, Time = Shard.CurrentTime }; + } + + private void InitControllers() + { + var cd = CharData; + + Character_BaseController = new BaseController + { + TimePlayedProp = 0, + CurrentWeightProp = 0, + EncumberedWeightProp = 255, + AuthorizedTerminalProp = new AuthorizedTerminalData { TerminalType = 0, TerminalId = 0, TerminalEntityId = 0 }, + PingTimeProp = 0, // Shard.CurrentTime, + StaticInfoProp = StaticInfo, + SpawnTimeProp = Shard.CurrentTime, + VisualOverridesProp = VisualOverrides, + CurrentEquipmentProp = CurrentEquipment, + SelectedLoadoutProp = 184538131, // cd.Loadout.ChassisID, + SelectedLoadoutIsPvPProp = 0, + GibVisualsIdProp = GibVisualsInfo, + SpawnPoseProp = SpawnPose, + ProcessDelayProp = ProcessDelay, + SpectatorModeProp = 0, + CinematicCameraProp = null, + CharacterStateProp = CharacterState, + HostilityInfoProp = HostilityInfo, + PersonalFactionStanceProp = null, + CurrentHealthProp = 0, + CurrentShieldsProp = 0, + MaxShieldsProp = MaxShields, + MaxHealthProp = MaxHealth, + CurrentDurabilityPctProp = 100, + EnergyParamsProp = EnergyParams, + CharacterStatsProp = CharacterStats, + EmoteIDProp = Emote, + AttachedToProp = null, + SnapMountProp = 0, + SinFlagsProp = 0, + SinFlagsPrivateProp = 0, + SinFactionsAcquiredByProp = null, + SinTeamsAcquiredByProp = null, + ArmyGUIDProp = cd.Army.GUID, + ArmyIsOfficerProp = 0, + EncounterPartyTupleProp = null, + DockedParamsProp = DockedParams, + LookAtTargetProp = null, + ZoneUnlocksProp = 0, + RegionUnlocksProp = 0, + ChatPartyLeaderIdProp = new EntityId { Backing = 0 }, + ScopeBubbleInfoProp = ScopeBubble, + CarryableObjects_0Prop = null, + CarryableObjects_1Prop = null, + CarryableObjects_2Prop = null, + CachedAssetsProp = null, + RespawnTimesProp = null, + ProgressionXpProp = 0, + PermanentStatusEffectsProp = new PermanentStatusEffectsData { Effects = Array.Empty() }, + XpBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + XpPermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + XpZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + XpVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + XpEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ResourceBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ResourcePermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ResourceZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ResourceVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ResourceEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + MoneyBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + MoneyPermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + MoneyZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + MoneyVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + MoneyEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ReputationBoostModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ReputationPermanentModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ReputationZoneModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ReputationVipModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + ReputationEventModifierProp = new StatModifierData { ModifierId = 0, StatValue = 0.0f }, + WalletProp = new WalletData { Beans = 999, Epoch = 1462889864 }, + LoyaltyProp = new LoyaltyData { Current = 0, Lifetime = 0, Tier = 0 }, + LevelProp = cd.Level, + EffectiveLevelProp = cd.Level, + LevelResetCountProp = 0, + OldestDeployablesProp = new OldestDeployablesField { Data = Array.Empty() }, + PerkRespecsProp = 0, + ArcStatusProp = null, + LeaveZoneTimeProp = null, + ChatMuteStatusProp = 0, + TimedDailyRewardProp = new TimedDailyRewardData + { + Stage = 0, + State = 0, + RollNumber = 0, + MaxRolls = 0, + CountdownToTime = 0 + }, + TimedDailyRewardResultProp = null, + SinCardTypeProp = 0, + SinCardFields_0Prop = null, + SinCardFields_1Prop = null, + SinCardFields_2Prop = null, + SinCardFields_3Prop = null, + SinCardFields_4Prop = null, + SinCardFields_5Prop = null, + SinCardFields_6Prop = null, + SinCardFields_7Prop = null, + SinCardFields_8Prop = null, + SinCardFields_9Prop = null, + SinCardFields_10Prop = null, + SinCardFields_11Prop = null, + SinCardFields_12Prop = null, + SinCardFields_13Prop = null, + SinCardFields_14Prop = null, + SinCardFields_15Prop = null, + SinCardFields_16Prop = null, + SinCardFields_17Prop = null, + SinCardFields_18Prop = null, + SinCardFields_19Prop = null, + SinCardFields_20Prop = null, + SinCardFields_21Prop = null, + SinCardFields_22Prop = null, + AssetOverridesProp = AssetOverrides, + FriendCountProp = 0, // :'( + CAISStatusProp = new CAISStatusData { State = CAISStatusData.CAISState.None, Elapsed = 0 }, + ScalingLevelProp = 0, + PvPRankProp = 0, + PvPRankPointsProp = 0, + PvPTokensProp = 0, + BountyPointsLastClaimedProp = 0, + EliteLevelProp = 1 + }; + + Character_CombatController = new CombatController + { + RunSpeedMultProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + FwdRunSpeedMultProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + JumpHeightMultProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + AirControlMultProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + ThrustStrengthMultProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + ThrustAirControlProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + FrictionProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + AmmoConsumptionProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + MaxTurnRateProp = new StatMultiplierData { Value = 0f, Time = Shard.CurrentTime }, + TurnSpeedProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + TimeDilationProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + FireRateModifierProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + AccuracyModifierProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + GravityMultProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + AirResistanceMultProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + WeaponChargeupModProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + WeaponDamageDealtModProp = new StatMultiplierData { Value = 1.0f, Time = Shard.CurrentTime }, + FireMode_0Prop = FireMode_0, + FireMode_1Prop = FireMode_1, + WeaponIndexProp = WeaponIndex, + WeaponFireBaseTimeProp = new WeaponFireBaseTimeData { ChangeTime = 0, Unk = 0 }, + WeaponAgilityModProp = 1.0f, + CombatFlagsProp = new CombatFlagsData { Value = 0, Time = Shard.CurrentTime }, + PermissionFlagsProp = new PermissionFlagsData + { + Time = Shard.CurrentTime, + Value = 0ul + | PermissionFlagsData.CharacterPermissionFlags.movement + | PermissionFlagsData.CharacterPermissionFlags.sprint + | PermissionFlagsData.CharacterPermissionFlags.unk_3 + | PermissionFlagsData.CharacterPermissionFlags.jump + | PermissionFlagsData.CharacterPermissionFlags.weapon + | PermissionFlagsData.CharacterPermissionFlags.unk_5 + | PermissionFlagsData.CharacterPermissionFlags.abilities + | PermissionFlagsData.CharacterPermissionFlags.unk_7 + | PermissionFlagsData.CharacterPermissionFlags.jetpack + | PermissionFlagsData.CharacterPermissionFlags.unk_13 + | PermissionFlagsData.CharacterPermissionFlags.unk_14 + | PermissionFlagsData.CharacterPermissionFlags.unk_15 + | PermissionFlagsData.CharacterPermissionFlags.crouch + | PermissionFlagsData.CharacterPermissionFlags.unk_21 + | PermissionFlagsData.CharacterPermissionFlags.calldown_abilities + | PermissionFlagsData.CharacterPermissionFlags.unk_23 + | PermissionFlagsData.CharacterPermissionFlags.unk_24 + | PermissionFlagsData.CharacterPermissionFlags.unk_25 + | PermissionFlagsData.CharacterPermissionFlags.unk_26 + | PermissionFlagsData.CharacterPermissionFlags.self_revive + + // | PermissionFlagsData.CharacterPermissionFlags.respawn_input + | PermissionFlagsData.CharacterPermissionFlags.battleframe_abilities + | PermissionFlagsData.CharacterPermissionFlags.unk_31 + }, + NemesesProp = new NemesesData { Values = Array.Empty() }, + SuperChargeProp = new SuperChargeData { Value = 0, Op = 0 } + }; + Character_MissionAndMarkerController = new MissionAndMarkerController(); + Character_LocalEffectsController = new LocalEffectsController(); + } + + private void InitViews() + { + Character_ObserverView = new ObserverView + { + StaticInfoProp = StaticInfo, + SpawnTimeProp = Shard.CurrentTime, + EffectsFlagsProp = EffectsFlags, + GibVisualsIDProp = GibVisualsInfo, + ProcessDelayProp = ProcessDelay, + CharacterStateProp = CharacterState, + HostilityInfoProp = HostilityInfo, + PersonalFactionStanceProp = null, + CurrentHealthPctProp = 100, + MaxHealthProp = MaxHealth, + EmoteIDProp = Emote, + AttachedToProp = null, + SnapMountProp = 0, + SinFlagsProp = 0, + SinFactionsAcquiredByProp = null, + SinTeamsAcquiredByProp = null, + ArmyGUIDProp = 0, + OwnerIdProp = 0, + NPCTypeProp = 0, + DockedParamsProp = DockedParams, + LookAtTargetProp = null, + WaterLevelAndDescProp = 0, + CarryableObjects_0Prop = null, + CarryableObjects_1Prop = null, + CarryableObjects_2Prop = null, + RespawnTimesProp = null, + SinCardTypeProp = 0, + SinCardFields_0Prop = null, + SinCardFields_1Prop = null, + SinCardFields_2Prop = null, + SinCardFields_3Prop = null, + SinCardFields_4Prop = null, + SinCardFields_5Prop = null, + SinCardFields_6Prop = null, + SinCardFields_7Prop = null, + SinCardFields_8Prop = null, + SinCardFields_9Prop = null, + SinCardFields_10Prop = null, + SinCardFields_11Prop = null, + SinCardFields_12Prop = null, + SinCardFields_13Prop = null, + SinCardFields_14Prop = null, + SinCardFields_15Prop = null, + SinCardFields_16Prop = null, + SinCardFields_17Prop = null, + SinCardFields_18Prop = null, + SinCardFields_19Prop = null, + SinCardFields_20Prop = null, + SinCardFields_21Prop = null, + SinCardFields_22Prop = null, + AssetOverridesProp = AssetOverrides + }; + Character_EquipmentView = new EquipmentView + { + VisualOverridesProp = VisualOverrides, + CurrentEquipmentProp = CurrentEquipment, + LevelProp = 1, + CurrentDurabilityPctProp = 100, + CharacterStatsProp = CharacterStats, + ScalingLevelProp = 1, + PvPRankProp = 0, + EliteLevelProp = 0 + }; + Character_CombatView = new CombatView + { + FireMode_0Prop = FireMode_0, + FireMode_1Prop = FireMode_1, + WeaponIndexProp = WeaponIndex, + WeaponAgilityModProp = 1.0f, + CombatFlagsProp = new CombatFlagsData { Value = 0, Time = Shard.CurrentTime }, + MimicParentProp = new EntityId { Backing = 0 }, + MimicOffsetProp = Vector3.Zero, + + ClipEmptyBeginProp = Shard.CurrentTime, + ClipEmptyEndProp = Shard.CurrentTime, + WeaponBurstFiredProp = Shard.CurrentTime, + WeaponBurstEndedProp = Shard.CurrentTime, + WeaponBurstCancelledProp = Shard.CurrentTime, + WeaponReloadedProp = Shard.CurrentTime, + WeaponReloadCancelledProp = Shard.CurrentTime, + AbilityCooldownEndMs_0Prop = Shard.CurrentTime, + AbilityCooldownEndMs_1Prop = Shard.CurrentTime, + AbilityCooldownEndMs_2Prop = Shard.CurrentTime, + AbilityCooldownEndMs_3Prop = Shard.CurrentTime, + EquipmentLoadTimeProp = Shard.CurrentTime, + Ammo_0Prop = 88, + Ammo_1Prop = 88, + AltAmmo_0Prop = 52, + AltAmmo_1Prop = 52, + }; + Character_MovementView = new MovementView + { + MovementProp = new AeroMessages.GSS.V66.Character.MovementData + { + Position = Position, + Rotation = Rotation, + Aim = AimDirection, + MovementState = (ushort)MovementState, + Time = Shard.CurrentTime + } + }; + } + + private void RefreshMovementView() + { + Character_MovementView.MovementProp = new MovementData + { + Position = Position, + Rotation = Rotation, + Aim = AimDirection, + MovementState = (ushort)MovementState, + Time = Shard.CurrentTime + }; } } \ No newline at end of file diff --git a/UdpHosts/GameServer/Entities/Character/MovementStateContainer.cs b/UdpHosts/GameServer/Entities/Character/MovementStateContainer.cs index a9bc8dd4..517adf7b 100644 --- a/UdpHosts/GameServer/Entities/Character/MovementStateContainer.cs +++ b/UdpHosts/GameServer/Entities/Character/MovementStateContainer.cs @@ -6,6 +6,7 @@ namespace GameServer.Entities.Character; /// /// Allows eased access to the several flags /// +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "Document when overhauling this")] internal class MovementStateContainer { public CharMovementState MovementState; @@ -13,7 +14,6 @@ internal class MovementStateContainer /// /// The set flags which are not specified in the enum /// - /// public CharMovementState InvalidFlags => MovementState ^ ValidFlags; /// @@ -79,7 +79,7 @@ public bool Unk /// /// Print the currently set valid and invalid flags /// - /// + /// string public override string ToString() { return $"MovementState value: {MovementState,5} | {GetBinaryString(MovementState)}\n" + @@ -93,8 +93,8 @@ public override string ToString() /// /// Get a binary-number string representation of the passed value /// - /// - /// + /// state + /// string private static string GetBinaryString(CharMovementState state) { return Convert.ToString((short)state, 2).PadLeft(16, '0'); @@ -103,8 +103,8 @@ private static string GetBinaryString(CharMovementState state) /// /// Get a flag /// - /// - /// + /// state + /// string private bool GetValue(CharMovementState state) { return (MovementState & state) == state; @@ -113,7 +113,7 @@ private bool GetValue(CharMovementState state) /// /// Set or unset the flag, depending on the value /// - /// + /// state /// if true the specified flag will be set, else it will be unset private void SetValue(CharMovementState state, bool value) { diff --git a/UdpHosts/GameServer/Entities/IEntity.cs b/UdpHosts/GameServer/Entities/IEntity.cs index 3382ef0e..13ecd162 100644 --- a/UdpHosts/GameServer/Entities/IEntity.cs +++ b/UdpHosts/GameServer/Entities/IEntity.cs @@ -5,8 +5,10 @@ namespace GameServer.Entities; public interface IEntity { ulong EntityId { get; } - IShard Owner { get; } + IShard Shard { get; } IDictionary ControllerRefMap { get; } + bool IsInteractable(); + void RegisterController(Enums.GSS.Controllers controller); } \ No newline at end of file diff --git a/UdpHosts/GameServer/Enums/ControlPacketType.cs b/UdpHosts/GameServer/Enums/ControlPacketType.cs index 16b29e32..105904d9 100644 --- a/UdpHosts/GameServer/Enums/ControlPacketType.cs +++ b/UdpHosts/GameServer/Enums/ControlPacketType.cs @@ -1,5 +1,6 @@ namespace GameServer.Enums; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Control")] public enum ControlPacketType : byte { CloseConnection = 0, diff --git a/UdpHosts/GameServer/Enums/GSS/AreaVisualData/Events.cs b/UdpHosts/GameServer/Enums/GSS/AreaVisualData/Events.cs index 58679565..c06f4ab1 100644 --- a/UdpHosts/GameServer/Enums/GSS/AreaVisualData/Events.cs +++ b/UdpHosts/GameServer/Enums/GSS/AreaVisualData/Events.cs @@ -1,9 +1,10 @@ namespace GameServer.Enums.GSS.AreaVisualData; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-AreaVisualData#events")] internal enum Events { - PartialUpdate = 1, - KeyFrame = 4, + PartialUpdate = 1, // Delete + KeyFrame = 4, // Delete LootObjectCollected = 83, AudioEmitterSpawned = 84, ParticleEffectSpawned = 85 diff --git a/UdpHosts/GameServer/Enums/GSS/Character/Commands.cs b/UdpHosts/GameServer/Enums/GSS/Character/Commands.cs index 70c98c57..7c7a52b1 100644 --- a/UdpHosts/GameServer/Enums/GSS/Character/Commands.cs +++ b/UdpHosts/GameServer/Enums/GSS/Character/Commands.cs @@ -1,5 +1,6 @@ namespace GameServer.Enums.GSS.Character; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Character#commands")] internal enum Commands : byte { UiQueryResponse = 59, diff --git a/UdpHosts/GameServer/Enums/GSS/Character/Events.cs b/UdpHosts/GameServer/Enums/GSS/Character/Events.cs index 04837c0e..4955ae74 100644 --- a/UdpHosts/GameServer/Enums/GSS/Character/Events.cs +++ b/UdpHosts/GameServer/Enums/GSS/Character/Events.cs @@ -1,9 +1,10 @@ namespace GameServer.Enums.GSS.Character; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Character#events")] internal enum Events { - PartialUpdate = 1, - KeyFrame = 4, + PartialUpdate = 1, // Delete + KeyFrame = 4, // Delete MarketRequestComplete = 83, ReceiveWeaponTweaks = 84, TookDebugWeaponHitPublic = 85, diff --git a/UdpHosts/GameServer/Enums/GSS/Controllers.cs b/UdpHosts/GameServer/Enums/GSS/Controllers.cs index 2236c325..62ca6dab 100644 --- a/UdpHosts/GameServer/Enums/GSS/Controllers.cs +++ b/UdpHosts/GameServer/Enums/GSS/Controllers.cs @@ -1,5 +1,9 @@ namespace GameServer.Enums.GSS; +/// +/// Typecodes for network views. +/// +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Views%2C-Updates-and-Keyframes")] public enum Controllers : byte { Generic = 0, @@ -55,5 +59,5 @@ public enum Controllers : byte LootStoreExtension_LootObjectView = 53, - Generic2 = 251 + GenericShard = 251 } \ No newline at end of file diff --git a/UdpHosts/GameServer/Enums/GSS/Deployable/Events.cs b/UdpHosts/GameServer/Enums/GSS/Deployable/Events.cs index 42ea94a9..564339d8 100644 --- a/UdpHosts/GameServer/Enums/GSS/Deployable/Events.cs +++ b/UdpHosts/GameServer/Enums/GSS/Deployable/Events.cs @@ -1,9 +1,10 @@ namespace GameServer.Enums.GSS.Deployable; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Deployable#events")] internal enum Events { - PartialUpdate = 1, - KeyFrame = 4, + PartialUpdate = 1, // Delete + KeyFrame = 4, // Delete TookHit = 83, AbilityProjectileFired = 84, PublicCombatLog = 85 diff --git a/UdpHosts/GameServer/Enums/GSS/Generic/Commands.cs b/UdpHosts/GameServer/Enums/GSS/Generic/Commands.cs index 7343fe7a..a634b474 100644 --- a/UdpHosts/GameServer/Enums/GSS/Generic/Commands.cs +++ b/UdpHosts/GameServer/Enums/GSS/Generic/Commands.cs @@ -1,5 +1,6 @@ namespace GameServer.Enums.GSS.Generic; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Generic#client")] public enum Commands : byte { UIToEncounterMessage = 17, diff --git a/UdpHosts/GameServer/Enums/GSS/Generic/Events.cs b/UdpHosts/GameServer/Enums/GSS/Generic/Events.cs index 1ebbcf2b..59ac2069 100644 --- a/UdpHosts/GameServer/Enums/GSS/Generic/Events.cs +++ b/UdpHosts/GameServer/Enums/GSS/Generic/Events.cs @@ -1,5 +1,6 @@ namespace GameServer.Enums.GSS.Generic; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Generic#server")] public enum Events : byte { EncounterToUIMessage = 32, diff --git a/UdpHosts/GameServer/Enums/GSS/LootStoreExtensions/Events.cs b/UdpHosts/GameServer/Enums/GSS/LootStoreExtensions/Events.cs index ef67be60..55e58dd9 100644 --- a/UdpHosts/GameServer/Enums/GSS/LootStoreExtensions/Events.cs +++ b/UdpHosts/GameServer/Enums/GSS/LootStoreExtensions/Events.cs @@ -1,8 +1,9 @@ namespace GameServer.Enums.GSS.LootStoreExtensions; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-LootStoreExtension#events")] internal enum Events { - PartialUpdate = 1, - KeyFrame = 4, + PartialUpdate = 1, // Delete + KeyFrame = 4, // Delete LootObjectCollected = 83 } \ No newline at end of file diff --git a/UdpHosts/GameServer/Enums/GSS/Turret/Commands.cs b/UdpHosts/GameServer/Enums/GSS/Turret/Commands.cs index cbca35d9..3fe218ce 100644 --- a/UdpHosts/GameServer/Enums/GSS/Turret/Commands.cs +++ b/UdpHosts/GameServer/Enums/GSS/Turret/Commands.cs @@ -1,5 +1,6 @@ namespace GameServer.Enums.GSS.Turret; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Turret#commands")] internal enum Commands { PoseUpdate = 83, diff --git a/UdpHosts/GameServer/Enums/GSS/Turret/Events.cs b/UdpHosts/GameServer/Enums/GSS/Turret/Events.cs index ac9906e6..bb34b690 100644 --- a/UdpHosts/GameServer/Enums/GSS/Turret/Events.cs +++ b/UdpHosts/GameServer/Enums/GSS/Turret/Events.cs @@ -1,8 +1,9 @@ namespace GameServer.Enums.GSS.Turret; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Turret#events")] internal enum Events { - PartialUpdate = 1, - KeyFrame = 4, + PartialUpdate = 1, // Delete + KeyFrame = 4, // Delete WeaponProjectileFired = 83 } \ No newline at end of file diff --git a/UdpHosts/GameServer/Enums/GSS/Vehicle/Commands.cs b/UdpHosts/GameServer/Enums/GSS/Vehicle/Commands.cs index 98d8e34c..ecea10dc 100644 --- a/UdpHosts/GameServer/Enums/GSS/Vehicle/Commands.cs +++ b/UdpHosts/GameServer/Enums/GSS/Vehicle/Commands.cs @@ -1,5 +1,6 @@ namespace GameServer.Enums.GSS.Vehicle; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Vehicle#commands")] internal enum Commands { MovementInput = 83, diff --git a/UdpHosts/GameServer/Enums/GSS/Vehicle/Events.cs b/UdpHosts/GameServer/Enums/GSS/Vehicle/Events.cs index 4c4a9190..f34e0a9d 100644 --- a/UdpHosts/GameServer/Enums/GSS/Vehicle/Events.cs +++ b/UdpHosts/GameServer/Enums/GSS/Vehicle/Events.cs @@ -1,9 +1,10 @@ namespace GameServer.Enums.GSS.Vehicle; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Vehicle#events")] internal enum Events { - PartialUpdate = 1, - KeyFrame = 4, + PartialUpdate = 1, // Delete + KeyFrame = 4, // Delete AbilityActivated = 83, AbilityFailed = 84, PublicCombatLog = 85, diff --git a/UdpHosts/GameServer/Enums/MatrixPacketType.cs b/UdpHosts/GameServer/Enums/MatrixPacketType.cs index 68b4044c..d0376e52 100644 --- a/UdpHosts/GameServer/Enums/MatrixPacketType.cs +++ b/UdpHosts/GameServer/Enums/MatrixPacketType.cs @@ -1,5 +1,6 @@ namespace GameServer.Enums; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Messages-Matrix")] public enum MatrixPacketType : byte { Login = 17, diff --git a/UdpHosts/GameServer/Enums/Visuals/PaletteType.cs b/UdpHosts/GameServer/Enums/Visuals/PaletteType.cs index 779f8442..9ebae540 100644 --- a/UdpHosts/GameServer/Enums/Visuals/PaletteType.cs +++ b/UdpHosts/GameServer/Enums/Visuals/PaletteType.cs @@ -1,11 +1,21 @@ namespace GameServer.Enums.Visuals; +/// +/// Types of Warpaint Palettes for VisualsPaletteBlock. +/// +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "https://github.com/themeldingwars/Documentation/wiki/Warpaint-Palette-Types")] public enum PaletteType : byte { FullBody = 0x0, + Armor = 0x01, + BodySuit = 0x02, + Glow = 0x03, WeaponA = 0x4, WeaponB = 0x5, - UnkType7 = 0x07, // Biscuit uses these - UnkType8 = 0x08, // Biscuit uses these - UnkType9 = 0x09 // Biscuit uses these + Skin = 0x06, + Hair = 0x07, + FacialHair = 0x08, + Eye = 0x09, + WeaponAExtra = 0x0a, + WeaponBExtra = 0x0b, } \ No newline at end of file diff --git a/UdpHosts/GameServer/Extensions/GamePacket.cs b/UdpHosts/GameServer/Extensions/GamePacket.cs index b027d3fb..848718d7 100644 --- a/UdpHosts/GameServer/Extensions/GamePacket.cs +++ b/UdpHosts/GameServer/Extensions/GamePacket.cs @@ -9,8 +9,8 @@ internal static class GamePacketExtensions /// Unpack the into the provided class /// /// The class to unpack into - /// - /// + /// The packet + /// The unpacked class /// Do not call after other reading operations during endpoint execution! Relies heavily on to work correctly internal static T Unpack(this GamePacket packet) where T : class, IAero, new() diff --git a/UdpHosts/GameServer/Extensions/IAero.cs b/UdpHosts/GameServer/Extensions/IAero.cs index 3040f9b5..cd4d69ed 100644 --- a/UdpHosts/GameServer/Extensions/IAero.cs +++ b/UdpHosts/GameServer/Extensions/IAero.cs @@ -9,8 +9,8 @@ internal static class IAeroExtensions /// /// Serialize the instance to a region in memory /// - /// - /// + /// Any AeroMessage class implementing AeroMessageId + /// Output internal static void SerializeToMemory(this IAero aero, out Memory serializedData) { serializedData = new Memory(new byte[aero.GetPackedSize()]); @@ -20,8 +20,8 @@ internal static void SerializeToMemory(this IAero aero, out Memory seriali /// /// Serialize the changes to a region in memory /// - /// - /// + /// View or Controller implementing AeroMessageId + /// Output internal static void SerializeChangesToMemory(this IAeroViewInterface aero, out Memory serializedData) { serializedData = new Memory(new byte[aero.GetPackedChangesSize()]); diff --git a/UdpHosts/GameServer/GameServer.cs b/UdpHosts/GameServer/GameServer.cs index 396c5130..ba8bbd1c 100644 --- a/UdpHosts/GameServer/GameServer.cs +++ b/UdpHosts/GameServer/GameServer.cs @@ -6,9 +6,12 @@ using System.Threading.Tasks.Dataflow; using GameServer.Controllers; using GameServer.Test; +using Grpc.Net.Client; using Serilog; using Shared.Udp; +using GrpcGameServerAPIClient; + namespace GameServer; internal class GameServer : PacketServer @@ -63,7 +66,6 @@ protected override void HandlePacket(Packet packet, CancellationToken ct) /// Generate the Server Id /// TODO: Incorporate the Sql Node Number as per https://gist.github.com/SilentCLD/881839a9f45578f1618db012fc789a71 /// - /// private static ulong GenerateServerId() { Span ranSpan = stackalloc byte[8]; @@ -106,7 +108,7 @@ private IShard GetNextShard(CancellationToken ct) private IShard NewShard(CancellationToken ct) { - var id = _serverId | (uint)(_nextShardId++ << 8); + var id = _serverId | (uint)(_nextShardId++ << 8) | (byte)Enums.GSS.Controllers.GenericShard; var shard = _shards.AddOrUpdate(id, new Shard(GameTickRate, id, this), (_, old) => old); shard.Run(ct); diff --git a/UdpHosts/GameServer/GameServer.csproj b/UdpHosts/GameServer/GameServer.csproj index 70d8f04d..823148d1 100644 --- a/UdpHosts/GameServer/GameServer.csproj +++ b/UdpHosts/GameServer/GameServer.csproj @@ -1,40 +1,39 @@  - - - Exe - true - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - - - - + + Exe + true + net8.0 + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + PreserveNewest + + + + + + + + + \ No newline at end of file diff --git a/UdpHosts/GameServer/GameServerSettings.cs b/UdpHosts/GameServer/GameServerSettings.cs index e9394eea..5f6a5e3e 100644 --- a/UdpHosts/GameServer/GameServerSettings.cs +++ b/UdpHosts/GameServer/GameServerSettings.cs @@ -3,7 +3,7 @@ namespace GameServer; /// -/// Holds the settings for the server +/// Holds the settings for the server` /// public class GameServerSettings { @@ -16,4 +16,9 @@ public class GameServerSettings /// UDP port the game server should be listening on /// public ushort Port { get; set; } = 25001; + + /// + /// GrpcChannelAddress + /// + public string GrpcChannelAddress { get; set; } = "http://localhost:5201"; } \ No newline at end of file diff --git a/UdpHosts/GameServer/INetworkClient.cs b/UdpHosts/GameServer/INetworkClient.cs index 80819684..4b0ae7e0 100644 --- a/UdpHosts/GameServer/INetworkClient.cs +++ b/UdpHosts/GameServer/INetworkClient.cs @@ -6,6 +6,7 @@ namespace GameServer; +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "Still figuring it out")] public enum Status { Unknown = 0, diff --git a/UdpHosts/GameServer/IPlayer.cs b/UdpHosts/GameServer/IPlayer.cs index d9bdf607..62534dc4 100644 --- a/UdpHosts/GameServer/IPlayer.cs +++ b/UdpHosts/GameServer/IPlayer.cs @@ -6,6 +6,7 @@ namespace GameServer; public interface IPlayer { + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:EnumerationItemsMustBeDocumented", Justification = "Still figuring it out")] public enum PlayerStatus { Invalid = -1, @@ -41,6 +42,7 @@ public enum PlayerStatus void Init(IShard shard); void Login(ulong characterId); + void EnterZoneAck(); void Ready(); void Respawn(); void Jump(); diff --git a/UdpHosts/GameServer/IShard.cs b/UdpHosts/GameServer/IShard.cs index 13b736d3..caea824c 100644 --- a/UdpHosts/GameServer/IShard.cs +++ b/UdpHosts/GameServer/IShard.cs @@ -9,8 +9,11 @@ namespace GameServer; public interface IShard : IInstance, IPacketSender { IDictionary Clients { get; } + IDictionary Entities { get; } PhysicsEngine Physics { get; } AIEngine AI { get; } + MovementRelay Movement { get; } + EntityManager EntityMan { get; } int CurrentPlayers => Clients.Count; diff --git a/UdpHosts/GameServer/NetworkClient.cs b/UdpHosts/GameServer/NetworkClient.cs index 70dcf341..87ae5995 100644 --- a/UdpHosts/GameServer/NetworkClient.cs +++ b/UdpHosts/GameServer/NetworkClient.cs @@ -164,6 +164,7 @@ private void Matrix_PacketAvailable(GamePacket packet) break; case MatrixPacketType.EnterZoneAck: Factory.Get().Init(this, Player, AssignedShard, Logger); + Player.EnterZoneAck(); break; case MatrixPacketType.KeyframeRequest: packet.Unpack(); @@ -172,16 +173,16 @@ private void Matrix_PacketAvailable(GamePacket packet) break; case MatrixPacketType.ClientStatus: NetChannels[ChannelType.Matrix].SendIAero(new MatrixStatus - { - MatrixBytesPerSecond = 0, - GameShapedBytes = 0, - PacketUploss = 0, - PacketDownloss = 0, - Unk5 = 0, - IsEverlastingGobsocket = 0, - HaveUnk7 = 0, - Unk8 = Array.Empty() - }); + { + MatrixBytesPerSecond = 0, + GameShapedBytes = 0, + PacketUploss = 0, + PacketDownloss = 0, + Unk5 = 0, + IsEverlastingGobsocket = 0, + HaveUnk7 = 0, + Unk8 = Array.Empty() + }); break; case MatrixPacketType.LogInstrumentation: // Ignore diff --git a/UdpHosts/GameServer/NetworkPlayer.cs b/UdpHosts/GameServer/NetworkPlayer.cs index 4ccb3803..f6ae14fc 100644 --- a/UdpHosts/GameServer/NetworkPlayer.cs +++ b/UdpHosts/GameServer/NetworkPlayer.cs @@ -13,6 +13,8 @@ using Serilog; using Character = GameServer.Entities.Character.Character; using Loadout = AeroMessages.GSS.V66.Character.Event.Loadout; +using GrpcGameServerAPIClient; +using Grpc.Net.Client; namespace GameServer; @@ -42,12 +44,26 @@ public void Init(IShard shard) Init(this, shard, shard); } - public void Login(ulong characterId) + public async void Login(ulong characterId) { PlayerId = 0x4658281c142e9f00ul; - CharacterId = characterId; - CharacterEntity = new Character(AssignedShard, characterId & 0xffffffffffffff00); - CharacterEntity.Load(characterId); + var guid = AssignedShard.EntityMan.GetNextGuid() & 0xffffffffffffff00; // At the moment, instead of using the character id, we generate a new guid so that we can support multiple people of the same character guid connecting. This has to be removed later because it makes it difficult to associate the webapi calls. + CharacterId = guid; + CharacterEntity = new Character(AssignedShard, guid); + using var channel = GrpcChannel.ForAddress("http://localhost:5201"); + var client = new GameServerAPI.GameServerAPIClient(channel); + var reply = await client.GetCharacterAndBattleframeVisualsAsync(new CharacterID { ID = (long)characterId }); + if (reply != null) + { + CharacterEntity.LoadRemote(reply); + } + else + { + CharacterEntity.Load(characterId); + } + + CharacterEntity.SetControllingPlayer(this); + CharacterEntity.SetCharacterState(CharacterStateData.CharacterStatus.Spawning, AssignedShard.CurrentTime); Status = IPlayer.PlayerStatus.LoggedIn; // WelcomeToTheMatrix @@ -58,203 +74,225 @@ public void Login(ulong characterId) Logger.Verbose("Zone {0}", zone); + // Ensure character entity is placed at the respawn point + var pointOfInterestPosition = DataUtils.GetZone(zone).POIs["spawn"]; + CharacterEntity.SetPosition(pointOfInterestPosition); + CharacterEntity.SetSpawnPose(); + EnterZone(DataUtils.GetZone(zone)); } + public void EnterZoneAck() + { + AssignedShard.EntityMan.Add(CharacterEntity.EntityId, CharacterEntity); + AssignedShard.EntityMan.ScopeIn(this, CharacterEntity); + } + public void Respawn() { var pointOfInterestPosition = DataUtils.GetZone(CurrentZone.ID).POIs["spawn"]; - CharacterEntity.Position = pointOfInterestPosition; - + CharacterEntity.SetPosition(pointOfInterestPosition); + CharacterEntity.SetSpawnTime(AssignedShard.CurrentTime); var forcedMove = new ForcedMovement - { - Data = new ForcedMovementData - { - Type = 1, - Unk1 = 0, - HaveUnk2 = 0, - Params1 = new ForcedMovementType1Params { Position = pointOfInterestPosition, Direction = CharacterEntity.AimDirection, Velocity = Vector3.Zero, Time = AssignedShard.CurrentTime } - }, - ShortTime = AssignedShard.CurrentShortTime - }; + { + Data = new ForcedMovementData + { + Type = 1, + Unk1 = 0, + HaveUnk2 = 0, + Params1 = new ForcedMovementType1Params { Position = pointOfInterestPosition, Direction = CharacterEntity.AimDirection, Velocity = Vector3.Zero, Time = AssignedShard.CurrentTime + 1 } + }, + ShortTime = AssignedShard.CurrentShortTime + }; NetChannels[ChannelType.ReliableGss].SendIAero(forcedMove, CharacterEntity.EntityId); var respawnMsg = new Respawned { ShortTime = AssignedShard.CurrentShortTime, Unk1 = 0, Unk2 = 0 }; NetChannels[ChannelType.ReliableGss].SendIAero(respawnMsg, CharacterEntity.EntityId); - var baseController = new BaseController - { - CharacterStateProp = new CharacterStateData { State = CharacterStateData.CharacterStatus.Living, Time = AssignedShard.CurrentTime }, - CurrentHealthProp = CharacterEntity.CharData.MaxHealth, - MaxHealthProp = new MaxVital { Value = CharacterEntity.CharData.MaxHealth, Time = AssignedShard.CurrentTime }, - CurrentShieldsProp = 0, - RegionUnlocksProp = 0xFFFFFFFFFFFFFFFFUL, - GibVisualsIdProp = new GibVisuals { Id = 0, Time = AssignedShard.CurrentTime }, - TimedDailyRewardProp = new TimedDailyRewardData { Unk2 = 1, Unk4 = 1, Unk5 = AssignedShard.CurrentTime }, - PersonalFactionStanceProp = new PersonalFactionStanceData - { - Unk1 = new PersonalFactionStanceBitfield { NumFactions = 50, Bitfield = new byte[] { 0x09, 0x0e, 0x5d, 0xff, 0x5f, 0x08, 0x00, 0x00 } }, - Unk2 = new PersonalFactionStanceBitfield { NumFactions = 50, Bitfield = new byte[] { 0xf2, 0x00, 0x20, 0x00, 0x00, 0xf2, 0x00, 0x00 } } - }, - RespawnTimesProp = null - }; + var baseController = CharacterEntity.Character_BaseController; + + // Update 1 + CharacterEntity.SetSpawnTime(AssignedShard.CurrentTime); // Aegis style ho! + CharacterEntity.SetCharacterState(CharacterStateData.CharacterStatus.Respawning, AssignedShard.CurrentTime); + CharacterEntity.SetSpawnPose(); + baseController.RespawnTimesProp = new RespawnTimesData(); // And you know what it is + baseController.RespawnTimesProp = null; // Dirt + baseController.TimedDailyRewardProp = new TimedDailyRewardData { State = TimedDailyRewardData.TimedDailyRewardState.ROLLED, MaxRolls = 1, CountdownToTime = AssignedShard.CurrentTime }; + NetChannels[ChannelType.ReliableGss].SendIAeroChanges(baseController, CharacterEntity.EntityId); + + // Update 2 + CharacterEntity.SetCharacterState(CharacterStateData.CharacterStatus.Living, AssignedShard.CurrentTime + 1); + baseController.GibVisualsIdProp = new GibVisuals { Id = 0, Time = AssignedShard.CurrentTime + 1 }; + baseController.RespawnTimesProp = new RespawnTimesData(); // Shake it up + baseController.RespawnTimesProp = null; // It's dirt + baseController.CurrentHealthProp = CharacterEntity.CharData.MaxHealth; + baseController.MaxHealthProp = new MaxVital { Value = CharacterEntity.CharData.MaxHealth, Time = AssignedShard.CurrentTime }; + baseController.CurrentShieldsProp = 0; + baseController.RegionUnlocksProp = 0xFFFFFFFFFFFFFFFFUL; + baseController.PersonalFactionStanceProp = new PersonalFactionStanceData + { + Unk1 = new PersonalFactionStanceBitfield { NumFactions = 50, Bitfield = new byte[] { 0x09, 0x0e, 0x5d, 0xff, 0x5f, 0x08, 0x00, 0x00 } }, + Unk2 = new PersonalFactionStanceBitfield { NumFactions = 50, Bitfield = new byte[] { 0xf2, 0x00, 0x20, 0x00, 0x00, 0xf2, 0x00, 0x00 } } + }; NetChannels[ChannelType.ReliableGss].SendIAeroChanges(baseController, CharacterEntity.EntityId); var combatController = new CombatController - { - CombatTimer_0Prop = AssignedShard.CurrentTime, - StatusEffectsChangeTime_10Prop = AssignedShard.CurrentShortTime, - StatusEffects_10Prop = new StatusEffectData { Id = 986, Time = AssignedShard.CurrentTime, Initiator = new EntityId { Backing = Player.CharacterEntity.EntityId }, MoreDataFlag = 0 }, - StatusEffectsChangeTime_11Prop = AssignedShard.CurrentShortTime, - StatusEffects_11Prop = new StatusEffectData { Id = 472, Time = AssignedShard.CurrentTime, Initiator = new EntityId { Backing = Player.CharacterEntity.EntityId }, MoreDataFlag = 0 } - }; + { + CombatTimer_0Prop = AssignedShard.CurrentTime, + StatusEffectsChangeTime_10Prop = AssignedShard.CurrentShortTime, + StatusEffects_10Prop = new StatusEffectData { Id = 986, Time = AssignedShard.CurrentTime, Initiator = new EntityId { Backing = Player.CharacterEntity.EntityId }, MoreDataFlag = 0 }, + StatusEffectsChangeTime_11Prop = AssignedShard.CurrentShortTime, + StatusEffects_11Prop = new StatusEffectData { Id = 472, Time = AssignedShard.CurrentTime, Initiator = new EntityId { Backing = Player.CharacterEntity.EntityId }, MoreDataFlag = 0 } + }; NetChannels[ChannelType.ReliableGss].SendIAeroChanges(combatController, CharacterEntity.EntityId); // InventoryUpdate + // What are you doing here? var inventoryUpdate = new InventoryUpdate - { - ClearExistingData = 1, - ItemsPart1Length = 2, - ItemsPart1 = new Item[] - { - new() - { - Unk1 = 0, - SdbId = 76331, - GUID = 744961712419132925ul, - SubInventory = 4, - Unk2 = 0x22BEA256, - DynamicFlags = 0, - Durability = 0, - Unk3 = 0, - Unk4 = 0, - Unk5 = 1, - Unk6 = Array.Empty(), - Unk7 = 0, - Modules = Array.Empty() - }, - new() - { - Unk1 = 0, - SdbId = 76331, - GUID = 9181641073530142461ul, - SubInventory = 4, - Unk2 = 0x964C1352, - DynamicFlags = 0, - Durability = 0, - Unk3 = 0, - Unk4 = 0, - Unk5 = 1, - Unk6 = Array.Empty(), - Unk7 = 0, - Modules = Array.Empty() - } - }, - ItemsPart2Length = 0, - ItemsPart2 = Array.Empty(), - ItemsPart3Length = 0, - ItemsPart3 = Array.Empty(), - Resources = Array.Empty(), - Loadouts = new Loadout[] - { - new() - { - FrameLoadoutId = 184538131, - Unk = 1535539622, - LoadoutName = "ODM \"Mammoth\"", - LoadoutType = "battleframe", - ChassisID = 76331, - LoadoutConfigs = new LoadoutConfig[] - { - new() - { - ConfigID = 0, - ConfigName = "pve", - Items = Array.Empty(), - Visuals = new LoadoutConfig_Visual[] - { - new() - { - ItemSdbId = 10000, - VisualType = LoadoutConfig_Visual.LoadoutVisualType.Decal, - Data1 = 0, - Data2 = 4294967295, - Transform = new[] - { - 0.052F, 0.020F, 0.000F, 0.007F, -0.020F, -0.052F, 0.018F, -0.048F, 0.021F, 0.108F, -0.105F, 1.495F - } - }, - new() - { - ItemSdbId = 81423, - VisualType = LoadoutConfig_Visual.LoadoutVisualType.Glider, - Data1 = 0, - Data2 = 0, - Transform = Array.Empty() - }, - new() - { - ItemSdbId = 77087, - VisualType = LoadoutConfig_Visual.LoadoutVisualType.Vehicle, - Data1 = 0, - Data2 = 0, - Transform = Array.Empty() - }, - new() - { - ItemSdbId = 85163, - VisualType = LoadoutConfig_Visual.LoadoutVisualType.Palette, - Data1 = 0, - Data2 = 0, - Transform = Array.Empty() - }, - new() - { - ItemSdbId = 10022, - VisualType = LoadoutConfig_Visual.LoadoutVisualType.Pattern, - Data1 = 1, - Data2 = 0, - Transform = new[] { 0.000F, 13572.00F, 0.000F, 1963.000F } - } - }, - Perks = new uint[] { 85817, 85818, 85956, 85976, 86067, 86137, 86139, 118819, 124247, 140713 }, - Unk1 = 1464475061, // Did I... mess up the bandwidth? :thinking: - PerkBandwidth = 0, - PerkRespecLockRemainingSeconds = 0, - HaveExtraData = 0 - }, - new() - { - ConfigID = 1, - ConfigName = "pvp", - Items = Array.Empty(), - Visuals = Array.Empty(), - Perks = Array.Empty(), - Unk1 = 0, - PerkBandwidth = 0, - PerkRespecLockRemainingSeconds = 0, - HaveExtraData = 0 - } - } - } - }, - Unk = 1, - SecondItems = Array.Empty(), - SecondResources = Array.Empty() - }; + { + ClearExistingData = 1, + ItemsPart1Length = 2, + ItemsPart1 = new Item[] + { + new() + { + Unk1 = 0, + SdbId = 76331, + GUID = 744961712419132925ul, + SubInventory = 4, + Unk2 = 0x22BEA256, + DynamicFlags = 0, + Durability = 0, + Unk3 = 0, + Unk4 = 0, + Unk5 = 1, + Unk6 = Array.Empty(), + Unk7 = 0, + Modules = Array.Empty() + }, + new() + { + Unk1 = 0, + SdbId = 76331, + GUID = 9181641073530142461ul, + SubInventory = 4, + Unk2 = 0x964C1352, + DynamicFlags = 0, + Durability = 0, + Unk3 = 0, + Unk4 = 0, + Unk5 = 1, + Unk6 = Array.Empty(), + Unk7 = 0, + Modules = Array.Empty() + } + }, + ItemsPart2Length = 0, + ItemsPart2 = Array.Empty(), + ItemsPart3Length = 0, + ItemsPart3 = Array.Empty(), + Resources = Array.Empty(), + Loadouts = new Loadout[] + { + new() + { + FrameLoadoutId = 184538131, + Unk = 1535539622, + LoadoutName = "ODM \"Mammoth\"", + LoadoutType = "battleframe", + ChassisID = 76331, + LoadoutConfigs = new LoadoutConfig[] + { + new() + { + ConfigID = 0, + ConfigName = "pve", + Items = Array.Empty(), + Visuals = new LoadoutConfig_Visual[] + { + new() + { + ItemSdbId = 10000, + VisualType = LoadoutConfig_Visual.LoadoutVisualType.Decal, + Data1 = 0, + Data2 = 4294967295, + Transform = new[] + { + 0.052F, 0.020F, 0.000F, 0.007F, -0.020F, -0.052F, 0.018F, -0.048F, 0.021F, 0.108F, -0.105F, 1.495F + } + }, + new() + { + ItemSdbId = 81423, + VisualType = LoadoutConfig_Visual.LoadoutVisualType.Glider, + Data1 = 0, + Data2 = 0, + Transform = Array.Empty() + }, + new() + { + ItemSdbId = 77087, + VisualType = LoadoutConfig_Visual.LoadoutVisualType.Vehicle, + Data1 = 0, + Data2 = 0, + Transform = Array.Empty() + }, + new() + { + ItemSdbId = 85163, + VisualType = LoadoutConfig_Visual.LoadoutVisualType.Palette, + Data1 = 0, + Data2 = 0, + Transform = Array.Empty() + }, + new() + { + ItemSdbId = 10022, + VisualType = LoadoutConfig_Visual.LoadoutVisualType.Pattern, + Data1 = 1, + Data2 = 0, + Transform = new[] { 0.000F, 13572.00F, 0.000F, 1963.000F } + } + }, + Perks = new uint[] { 85817, 85818, 85956, 85976, 86067, 86137, 86139, 118819, 124247, 140713 }, + Unk1 = 1464475061, // Did I... mess up the bandwidth? :thinking: + PerkBandwidth = 0, + PerkRespecLockRemainingSeconds = 0, + HaveExtraData = 0 + }, + new() + { + ConfigID = 1, + ConfigName = "pvp", + Items = Array.Empty(), + Visuals = Array.Empty(), + Perks = Array.Empty(), + Unk1 = 0, + PerkBandwidth = 0, + PerkRespecLockRemainingSeconds = 0, + HaveExtraData = 0 + } + } + } + }, + Unk = 1, + SecondItems = Array.Empty(), + SecondResources = Array.Empty() + }; NetChannels[ChannelType.ReliableGss].SendIAero(inventoryUpdate, CharacterEntity.EntityId); + + AssignedShard.EntityMan.ScopeInAll(this); // Move elsewhere when adding actual respawning as we only need to call this once + CharacterEntity.Alive = true; // Accept MovementInputs only after Respawn } public void Ready() { Status = IPlayer.PlayerStatus.Playing; - CharacterEntity.Alive = true; } public void Jump() { - // NetChannels[ChannelType.UnreliableGss].SendGSSClass( new JumpActioned { JumpTime = AssignedShard.CurrentShortTime }, CharacterEntity.EntityID, Enums.GSS.Controllers.Character_BaseController ); - CharacterEntity.TimeSinceLastJump = AssignedShard.CurrentShortTime; + CharacterEntity.TimeSinceLastJump = -1; } public void Tick(double deltaTime, ulong currentTime, CancellationToken ct) @@ -285,32 +323,32 @@ private void EnterZone(Zone z) CharacterEntity.Position = CurrentZone.POIs["spawn"]; var msg = new EnterZone - { - InstanceId = AssignedShard.InstanceId, - ZoneId = CurrentZone.ID, - ZoneTimestamp = CurrentZone.Timestamp, - PreviewModeFlag = 0, - ZoneOwner = "r5_exec", - StreamingProtocol = 0x4c5f, - Unk1_2 = 0x0c9f5, - HotfixLevel = 0, - MatchId = 0, - Unk2 = 0, - Unk3_Millis = 0x63e2db5e, - ZoneName = CurrentZone.Name, - HaveDevZoneInfo = 0, - ZoneTimeSyncInfo = new ZoneTimeSyncData { FictionDateTimeOffsetMicros = 0, DayLengthFactor = 12.0F, DayPhaseOffset = 0.896445870399F }, - GameClockInfo = new GameClockInfoData - { - MicroUnix_1 = 1478970208392232, - MicroUnix_2 = 1478774752697322, - Timescale = 1.0, - Unk3 = 0, - Unk4 = 0, - Paused = 0 - }, - SpectatorModeFlag = 0 - }; + { + InstanceId = AssignedShard.InstanceId, + ZoneId = CurrentZone.ID, + ZoneTimestamp = CurrentZone.Timestamp, + PreviewModeFlag = 0, + ZoneOwner = "r5_exec", + StreamingProtocol = 0x4c5f, + Unk1_2 = 0x0c9f5, + HotfixLevel = 0, + MatchId = 0, + Unk2 = 0, + Unk3_Millis = 0x63e2db5e, + ZoneName = CurrentZone.Name, + HaveDevZoneInfo = 0, + ZoneTimeSyncInfo = new ZoneTimeSyncData { FictionDateTimeOffsetMicros = 0, DayLengthFactor = 12.0F, DayPhaseOffset = 0.896445870399F }, + GameClockInfo = new GameClockInfoData + { + MicroUnix_1 = 1478970208392232, + MicroUnix_2 = 1478774752697322, + Timescale = 1.0, + Unk3 = 0, + Unk4 = 0, + Paused = 0 + }, + SpectatorModeFlag = 0 + }; NetChannels[ChannelType.Matrix].SendIAero(msg); diff --git a/UdpHosts/GameServer/Protos/GameServerAPI.proto b/UdpHosts/GameServer/Protos/GameServerAPI.proto new file mode 100644 index 00000000..79fedb1c --- /dev/null +++ b/UdpHosts/GameServer/Protos/GameServerAPI.proto @@ -0,0 +1,76 @@ +syntax = "proto3"; +option csharp_namespace = "GrpcGameServerAPIClient"; +package RIN; +import "google/protobuf/timestamp.proto"; + +message BasicCharacterInfo { + string Name = 1; + uint32 Race = 2; + uint32 Gender = 3; + int32 TitleId = 4; + uint32 CurrentBattleframeId = 5; +} +message CharacterAndBattleframeVisuals { + BasicCharacterInfo CharacterInfo = 1; + CharacterVisuals CharacterVisuals = 2; + PlayerBattleframeVisuals BattleframeVisuals = 3; +} +message CharacterID { + int64 ID = 1; +} +message CharacterVisuals { + int32 id = 1; + int32 race = 2; + int32 gender = 3; + WebIdValueColor skin_color = 4; + WebId voice_set = 5; + WebId head = 6; + WebIdValueColor eye_color = 7; + WebIdValueColor lip_color = 8; + WebIdValueColor hair_color = 9; + WebIdValueColor facial_hair_color = 10; + repeated WebIdValueColor head_accessories = 11; + repeated WebId ornaments = 12; + WebId eyes = 13; + WebIdValueColorId hair = 14; + WebIdValueColorId facial_hair = 15; + WebId glider = 16; + WebId vehicle = 17; +} +message PingReq { + .google.protobuf.Timestamp SentTime = 1; +} +message PingResp { + .google.protobuf.Timestamp ClientSentTime = 1; + .google.protobuf.Timestamp ServerReciveTime = 2; +} +message PlayerBattleframeVisuals { + repeated WebId decals = 1; + int32 warpaint_id = 2; + repeated uint32 warpaint = 3 [packed = false]; + repeated int32 decalgradients = 4 [packed = false]; + repeated int32 warpaint_patterns = 5 [packed = false]; + repeated int32 visual_overrides = 6 [packed = false]; +} +message WebColor { + uint32 color = 1; +} +message WebColorId { + int32 id = 1; + uint32 value = 2; +} +message WebId { + int32 id = 1; +} +message WebIdValueColor { + int32 id = 1; + WebColor value = 2; +} +message WebIdValueColorId { + int32 id = 1; + WebColorId color = 2; +} +service GameServerAPI { + rpc GetCharacterAndBattleframeVisuals (CharacterID) returns (CharacterAndBattleframeVisuals); + rpc Ping (PingReq) returns (PingResp); +} diff --git a/UdpHosts/GameServer/Shard.cs b/UdpHosts/GameServer/Shard.cs index 7bc18a24..7018a062 100644 --- a/UdpHosts/GameServer/Shard.cs +++ b/UdpHosts/GameServer/Shard.cs @@ -25,6 +25,8 @@ public Shard(double gameTickRate, ulong instanceId, IPacketSender sender) Entities = new ConcurrentDictionary(); Physics = new PhysicsEngine(gameTickRate); AI = new AIEngine(); + Movement = new MovementRelay(this); + EntityMan = new EntityManager(this); InstanceId = instanceId; Sender = sender; EntityRefMap = new ConcurrentDictionary>(); @@ -36,8 +38,12 @@ public Shard(double gameTickRate, ulong instanceId, IPacketSender sender) public IDictionary Clients { get; } public PhysicsEngine Physics { get; } public AIEngine AI { get; } + public MovementRelay Movement { get; } + public EntityManager EntityMan { get; } public ulong InstanceId { get; } public ulong CurrentTimeLong { get; private set; } + public uint CurrentTime => unchecked((uint)CurrentTimeLong); + public ushort CurrentShortTime => unchecked((ushort)CurrentTime); public IDictionary> EntityRefMap { get; } private IPacketSender Sender { get; } @@ -61,6 +67,7 @@ public bool Tick(double deltaTime, ulong currentTime, CancellationToken ct) AI.Tick(deltaTime, currentTime, ct); Physics.Tick(deltaTime, currentTime, ct); + EntityMan.Tick(deltaTime, currentTime, ct); return true; } @@ -80,8 +87,7 @@ public bool MigrateIn(INetworkPlayer player) player.Init(this); Clients.Add(player.SocketId, player); - - // Entities.Add(player.CharacterEntity.EntityID, player.CharacterEntity); + return true; } @@ -118,7 +124,8 @@ private void RunThread(CancellationToken ct) while (!ct.IsCancellationRequested) { - var currentUnixTimestamp = (ulong)(DateTime.Now.UnixTimestamp() * 1000); + var currentUnixTimestamp = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + //(ulong)(DateTime.Now.UnixTimestamp() * 1000); var currentTime = unchecked((ulong)stopwatch.Elapsed.TotalMilliseconds); var delta = currentTime - lastTime; diff --git a/UdpHosts/GameServer/Systems/EntityManager/EntityManager.cs b/UdpHosts/GameServer/Systems/EntityManager/EntityManager.cs new file mode 100644 index 00000000..e0003c41 --- /dev/null +++ b/UdpHosts/GameServer/Systems/EntityManager/EntityManager.cs @@ -0,0 +1,209 @@ +using System; +using System.Threading; +using Aero.Gen; +using AeroMessages.Common; +using AeroMessages.GSS.V66.Character.Command; +using AeroMessages.GSS.V66.Character.Event; +using GameServer.Entities; +using GameServer.Extensions; + +namespace GameServer; + +public class EntityManager +{ + private const byte ServerId = 31; + private Shard Shard; + private uint Counter = 0; + + private ulong LastUpdateFlush = 0; + private ulong UpdateFlushIntervalMs = 5; + + public EntityManager(Shard shard) + { + Shard = shard; + } + + public void Tick(double deltaTime, ulong currentTime, CancellationToken ct) + { + if (currentTime > LastUpdateFlush + UpdateFlushIntervalMs) + { + LastUpdateFlush = currentTime; + foreach (var entity in Shard.Entities.Values) + { + FlushChanges(entity); + } + } + } + + public ulong GetNextGuid() + { + return new Core.Data.EntityGuid(ServerId, Shard.CurrentTime, Counter++, (byte)Enums.GSS.Controllers.Character).Full; + } + + public void Add(ulong guid, Entities.IEntity entity) + { + Shard.Entities.Add(guid, entity); + OnAddedEntity(entity); + } + + public void Add(Entities.IEntity entity) + { + var guid = new Core.Data.EntityGuid(ServerId, Shard.CurrentTime, Counter++, (byte)Enums.GSS.Controllers.Character); + Shard.Entities.Add(guid.Full, entity); + OnAddedEntity(entity); + } + + public void Remove(Entities.IEntity entity) + { + Remove(entity.EntityId); + } + + public void Remove(Core.Data.EntityGuid guid) + { + Remove(guid.Full); + } + + public void Remove(ulong guid) + { + Shard.Entities.Remove(guid); + } + + public void ScopeInAll(INetworkPlayer player) + { + foreach (var entity in Shard.Entities.Values) + { + if (entity == player.CharacterEntity) + { + continue; // Hack: Prevent ScopeInAll from triggering keyframes for the local player's character. We have already sent down those in the login process, repeating is wasteful but also causes some issues if we don't properly persist the changes made to the views during respawn. + } + + ScopeIn(player, entity); + } + } + + public void ScopeIn(INetworkPlayer player, IEntity entity) + { + if (entity.GetType() == typeof(Entities.Character.Character)) + { + var character = entity as Entities.Character.Character; + + if (character.IsPlayerControlled && character.Player == player) + { + var baseController = character.Character_BaseController; + var combatController = character.Character_CombatController; + var missionController = character.Character_MissionAndMarkerController; + var effectsController = character.Character_LocalEffectsController; + var specController = character.Character_SpectatorController; + + bool haveBaseController = baseController != null; + bool haveCombatController = combatController != null; + bool haveMissionController = missionController != null; + bool haveEffectsController = effectsController != null; + bool haveSpecController = specController != null; + + if (haveBaseController && haveCombatController && haveMissionController && haveEffectsController) + { + player.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(baseController, entity.EntityId, player.PlayerId); + player.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(combatController, entity.EntityId, player.PlayerId); + player.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(effectsController, entity.EntityId, player.PlayerId); + player.NetChannels[ChannelType.ReliableGss].SendIAeroControllerKeyframe(missionController, entity.EntityId, player.PlayerId); + player.NetChannels[ChannelType.ReliableGss].SendIAero(new CharacterLoaded(), entity.EntityId); + } + } + + var observer = character.Character_ObserverView; + var equipment = character.Character_EquipmentView; + var combat = character.Character_CombatView; + var movement = character.Character_MovementView; + var tinyobject = character.Character_TinyObjectView; + + bool haveObserver = observer != null; + bool haveEquipment = equipment != null; + bool haveCombat = combat != null; + bool haveMovement = movement != null; + bool haveTinyObject = tinyobject != null; + + if (haveObserver && haveEquipment && haveCombat && haveMovement) + { + player.NetChannels[ChannelType.ReliableGss].SendIAero(observer, entity.EntityId, 3); + player.NetChannels[ChannelType.ReliableGss].SendIAero(equipment, entity.EntityId, 3); + player.NetChannels[ChannelType.ReliableGss].SendIAero(combat, entity.EntityId, 3); + player.NetChannels[ChannelType.ReliableGss].SendIAero(movement, entity.EntityId, 3); + } + + if (haveTinyObject) + { + player.NetChannels[ChannelType.ReliableGss].SendIAero(tinyobject, entity.EntityId, 3); + } + } + } + + public void FlushChanges(IEntity entity) + { + if (entity.GetType() == typeof(Entities.Character.Character)) + { + var character = entity as Entities.Character.Character; + + if (character.IsPlayerControlled) + { + FlushViewChangesToPlayer(character.Character_BaseController, character.EntityId, character.Player); + FlushViewChangesToPlayer(character.Character_CombatController, character.EntityId, character.Player); + FlushViewChangesToPlayer(character.Character_MissionAndMarkerController, character.EntityId, character.Player); + FlushViewChangesToPlayer(character.Character_LocalEffectsController, character.EntityId, character.Player); + FlushViewChangesToPlayer(character.Character_SpectatorController, character.EntityId, character.Player); + } + + // We don't flush Character_MovementView as those changes are basically handled entirely by CurrentPoseUpdate + FlushViewChangesToEveryone(character.Character_ObserverView, character.EntityId); + FlushViewChangesToEveryone(character.Character_EquipmentView, character.EntityId); + FlushViewChangesToEveryone(character.Character_CombatView, character.EntityId); + FlushViewChangesToEveryone(character.Character_TinyObjectView, character.EntityId); + } + } + + public void FlushViewChangesToPlayer(TPacket view, ulong entityId, INetworkPlayer player) + where TPacket : class, IAeroViewInterface + { + // Just to reduce repetition + bool shouldFlush = view != null && view.GetPackedChangesSize() > 0; + bool shouldSend = player.Status.Equals(IPlayer.PlayerStatus.Playing) || player.Status.Equals(IPlayer.PlayerStatus.Loading); + if (shouldFlush && shouldSend) + { + player.NetChannels[ChannelType.ReliableGss].SendIAeroChanges(view, entityId); + } + } + + public void FlushViewChangesToEveryone(TPacket view, ulong entityId) + where TPacket : class, IAeroViewInterface + { + // We can only call SerializeChangesToMemory once but we need to send to multiple players. + bool shouldFlush = view != null && view.GetPackedChangesSize() > 0; + if (shouldFlush) + { + view.SerializeChangesToMemory(out var update); + foreach (var client in Shard.Clients.Values) + { + Console.WriteLine($"FlushedViewChanges:{client.SocketId} {typeof(TPacket).FullName} {entityId}"); + + bool shouldSend = client.Status.Equals(IPlayer.PlayerStatus.Playing) || client.Status.Equals(IPlayer.PlayerStatus.Loading); + if (shouldSend) + { + client.NetChannels[ChannelType.UnreliableGss].SendIAeroChanges(view, entityId, update); + } + } + } + } + + private void OnAddedEntity(Entities.IEntity entity) + { + // TEMP: Hack to introduce new entities to connected players. This should be replaced with tick logic that sends down entities based on scope and distance. + foreach (var client in Shard.Clients.Values) + { + // We don't want to inform players that are still in the early steps of connecting + if (client.Status.Equals(IPlayer.PlayerStatus.Playing) || client.Status.Equals(IPlayer.PlayerStatus.Loading)) + { + ScopeIn(client, entity); + } + } + } +} \ No newline at end of file diff --git a/UdpHosts/GameServer/Systems/MovementRelay/MovementRelay.cs b/UdpHosts/GameServer/Systems/MovementRelay/MovementRelay.cs new file mode 100644 index 00000000..17805d20 --- /dev/null +++ b/UdpHosts/GameServer/Systems/MovementRelay/MovementRelay.cs @@ -0,0 +1,92 @@ +using System; +using System.Threading; +using AeroMessages.Common; +using AeroMessages.GSS.V66.Character; +using AeroMessages.GSS.V66.Character.Event; +using GameServer.Entities; + +namespace GameServer; + +public class MovementRelay +{ + private Shard Shard; + + public MovementRelay(Shard shard) + { + Shard = shard; + } + + public void CharacterMovementInput(INetworkClient client, IEntity entity, AeroMessages.GSS.V66.Character.Command.MovementInput input) + { + var character = entity as Entities.Character.Character; + + // Update our data based on the clients input + var poseData = input.PoseData; + var posRotState = poseData.PosRotState; + character.SetPoseData(poseData, input.ShortTime); + + bool sendJumpActioned = poseData.TimeSinceLastJump < character.TimeSinceLastJump; // Compare the old value before updating + character.TimeSinceLastJump = poseData.TimeSinceLastJump; + + // TODO: Fix or remove this container, refer to https://github.com/themeldingwars/Documentation/wiki/Character-State + var movementStateValue = posRotState.MovementState; + character.MovementStateContainer.MovementState = (Entities.Character.CharMovementState)movementStateValue; + + if (character.MovementStateContainer.InvalidFlags != Entities.Character.CharMovementState.None) + { + // FIXME: Sorry, don't have a logger here, inject pls + // _logger.Error($"Unmapped {nameof(CharMovementState)} encountered! \n{player.CharacterEntity.MovementStateContainer}"); + } + + // Confirm the pose with the client + var confirmedPose = new ConfirmedPoseUpdate + { + PoseData = new MovementPoseData + { + ShortTime = input.ShortTime, + MovementType = MovementDataType.PosRotState, + MovementUnk3 = 0, // ToDo: Find out why this has to be 0; What does it control? + PosRotState = new MovementPosRotState + { + Pos = character.Position, + Rot = character.Rotation, + MovementState = (short)character.MovementStateContainer.MovementState // ToDo: This was ushort previously! + }, + Velocity = character.Velocity, + JetpackEnergy = poseData.JetpackEnergy, + GroundTimePositiveAirTimeNegative = poseData.GroundTimePositiveAirTimeNegative, // Somehow affects gravity + TimeSinceLastJump = poseData.TimeSinceLastJump, + HaveDebugData = 0 + }, + NextShortTime = unchecked((ushort)(input.ShortTime + 90)) // This value has to be in the future, nobody cares why. + }; + client.NetChannels[ChannelType.UnreliableGss].SendIAero(confirmedPose, character.EntityId, 0, typeof(Enums.GSS.Character.Events)); + + // Forward update to remote clients + var currentPose = new CurrentPoseUpdate + { + Data = new AeroMessages.GSS.V66.CurrentPoseUpdateData + { + Flags = 0x00, + ShortTime = character.MovementShortTime, + UnkAlwaysPresent = 0x79, + MovementState = (ushort)character.MovementState, + Position = character.Position, + Rotation = character.Rotation, + Aim = character.AimDirection, + } + }; + foreach (var remoteClient in Shard.Clients.Values) + { + if (remoteClient.Status.Equals(IPlayer.PlayerStatus.Playing)) + { + if (sendJumpActioned) + { + remoteClient.NetChannels[ChannelType.UnreliableGss].SendIAero(new JumpActioned { ShortTime = input.ShortTime }, character.EntityId); + } + + remoteClient.NetChannels[ChannelType.UnreliableGss].SendIAero(currentPose, character.EntityId); + } + } + } +} \ No newline at end of file