From 797c2389f1ce017a28ad7c2f1b77f60e064a581b Mon Sep 17 00:00:00 2001 From: Extremelyd1 <10898310+Extremelyd1@users.noreply.github.com> Date: Sat, 20 Jul 2024 20:37:54 +0200 Subject: [PATCH] Initial work for sound/music networking --- .../Client/Entity/Component/MusicComponent.cs | 148 ++++++++++++++++++ HKMP/HKMP.csproj | 1 + HKMP/Resource/music-data.json | 104 ++++++++++++ 3 files changed, 253 insertions(+) create mode 100644 HKMP/Game/Client/Entity/Component/MusicComponent.cs create mode 100644 HKMP/Resource/music-data.json diff --git a/HKMP/Game/Client/Entity/Component/MusicComponent.cs b/HKMP/Game/Client/Entity/Component/MusicComponent.cs new file mode 100644 index 0000000..dc1cfbc --- /dev/null +++ b/HKMP/Game/Client/Entity/Component/MusicComponent.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using Hkmp.Networking.Client; +using Hkmp.Networking.Packet.Data; +using Hkmp.Util; +using HutongGames.PlayMaker.Actions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using UnityEngine; +using UnityEngine.Audio; +using Logger = Hkmp.Logging.Logger; + +namespace Hkmp.Game.Client.Entity.Component; + +// TODO: document all fields and methods +/// +/// This component manages the music that plays for boss fights. +internal class MusicComponent : EntityComponent { + /// + /// The file path of the embedded resource file for music data. + /// + private const string MusicDataFilePath = "Hkmp.Resource.music-data.json"; + + private static readonly List MusicCueDataList; + private static readonly List SnapshotDataList; + + static MusicComponent() { + var dataPair = FileUtil.LoadObjectFromEmbeddedJson< + (List, List) + >(MusicDataFilePath); + + MusicCueDataList = dataPair.Item1; + SnapshotDataList = dataPair.Item2; + + On.PlayMakerFSM.OnEnable += OnFsmEnable; + } + + public MusicComponent( + NetClient netClient, + ushort entityId, + HostClientPair gameObject + ) : base(netClient, entityId, gameObject) { + // TODO: register hooks for entering ApplyMusicCue and TransitionToAudioSnapshot actions + // TODO: these hooks should network changes in Music and Audio to the server if the player is scene host + } + + /// + public override void InitializeHost() { + } + + /// + public override void Update(EntityNetworkData data) { + // TODO: handle receiving Music and Audio updates from the server and applying them + } + + /// + public override void Destroy() { + } + + private static void OnFsmEnable(On.PlayMakerFSM.orig_OnEnable orig, PlayMakerFSM self) { + orig(self); + + foreach (var state in self.FsmStates) { + foreach (var action in state.Actions) { + if (action is ApplyMusicCue applyMusicCue) { + var musicCue = applyMusicCue.musicCue.Value as MusicCue; + if (musicCue == null) { + continue; + } + + Logger.Debug($"Found music cue '{musicCue.name}' in FSM '{self.Fsm.Name}', '{state.Name}'"); + + if (GetMusicCueData( + data => data.Name.Equals(musicCue.name), + out var musicCueData + )) { + Logger.Debug($" Adding to data with type: {musicCueData.Type}"); + musicCueData.MusicCue = musicCue; + } + } else if (action is TransitionToAudioSnapshot snapshotAction) { + var snapshot = snapshotAction.snapshot.Value as AudioMixerSnapshot; + if (snapshot == null) { + continue; + } + + Logger.Debug($"Found audio mixer snapshot '{snapshot.name}' in FSM '{self.Fsm.Name}', '{state.Name}'"); + } + } + } + } + + private static bool GetMusicCueData(Func predicate, out MusicCueData musicCueData) { + foreach (var data in MusicCueDataList) { + if (predicate.Invoke(data)) { + musicCueData = data; + return true; + } + } + + musicCueData = null; + return false; + } + + private class MusicCueData { + public MusicCueType Type { get; set; } + public string Name { get; set; } + public byte Index { get; set; } + [JsonIgnore] + public MusicCue MusicCue { get; set; } + } + + private class AudioMixerSnapshotData { + public AudioMixerSnapshotType Type { get; set; } + public string Name { get; set; } + public byte Index { get; set; } + [JsonIgnore] + public AudioMixerSnapshot Snapshot { get; set; } + } + + [JsonConverter(typeof(StringEnumConverter))] + private enum MusicCueType { + None, + FalseKnight, + Hornet, + GGHornet, + MantisLords, + SoulMaster, + SoulMaster2, + GGHeavy, + EnemyBattle, + DreamFight, + Hive, + HiveKnight, + DungDefender, + BrokenVessel, + Nosk, + TheHollowKnight, + Greenpath, + Waterways + } + + [JsonConverter(typeof(StringEnumConverter))] + private enum AudioMixerSnapshotType { + Silent, + None, + Off, + } +} diff --git a/HKMP/HKMP.csproj b/HKMP/HKMP.csproj index 76902f0..dc66cd4 100644 --- a/HKMP/HKMP.csproj +++ b/HKMP/HKMP.csproj @@ -23,6 +23,7 @@ + diff --git a/HKMP/Resource/music-data.json b/HKMP/Resource/music-data.json new file mode 100644 index 0000000..e9669f4 --- /dev/null +++ b/HKMP/Resource/music-data.json @@ -0,0 +1,104 @@ +{ + "Item1": [ + { + "Type": "None", + "Name": "None", + "Index": 0 + }, + { + "Type": "FalseKnight", + "Name": "Boss1", + "Index": 1 + }, + { + "Type": "Hornet", + "Name": "BossHornet", + "Index": 2 + }, + { + "Type": "GGHornet", + "Name": "GGHornet", + "Index": 3 + }, + { + "Type": "MantisLords", + "Name": "BossMantisLords", + "Index": 4 + }, + { + "Type": "SoulMaster", + "Name": "BossMageLord", + "Index": 5 + }, + { + "Type": "SoulMaster2", + "Name": "MageLord2", + "Index": 6 + }, + { + "Type": "GGHeavy", + "Name": "GG Heavy", + "Index": 7 + },{ + "Type": "DreamFight", + "Name": "DreamFight", + "Index": 8 + }, + { + "Type": "Hive", + "Name": "Hive", + "Index": 9 + }, + { + "Type": "HiveKnight", + "Name": "HiveKnight", + "Index": 10 + }, + { + "Type": "DungDefender", + "Name": "DungDefender", + "Index": 11 + }, + { + "Type": "BrokenVessel", + "Name": "BossIK", + "Index": 12 + }, + { + "Type": "Nosk", + "Name": "MimicSpider", + "Index": 13 + },{ + "Type": "TheHollowKnight", + "Name": "HKBattle", + "Index": 14 + }, + { + "Type": "Greenpath", + "Name": "Greenpath", + "Index": 15 + }, + { + "Type": "Waterways", + "Name": "Waterways", + "Index": 16 + } + ], + "Item2": [ + { + "Type": "Silent", + "Name": "Silent", + "Index": 0 + }, + { + "Type": "None", + "Name": "None", + "Index": 1 + }, + { + "Type": "Off", + "Name": "Off", + "Index": 2 + } + ] +}