using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using H3MP;
using H3MP.Networking;
using H3MP.Scripts;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("Niko666")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("PlayerFootsteps but synced in H3MP! All clients need to install this in order to sync. Can also be used in singleplayer.")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0")]
[assembly: AssemblyProduct("Niko666.H3MPFootstep")]
[assembly: AssemblyTitle("H3MP Footsteps")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace BepInEx
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class BepInAutoPluginAttribute : Attribute
{
public BepInAutoPluginAttribute(string id = null, string name = null, string version = null)
{
}
}
}
namespace BepInEx.Preloader.Core.Patching
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class PatcherAutoPluginAttribute : Attribute
{
public PatcherAutoPluginAttribute(string id = null, string name = null, string version = null)
{
}
}
}
namespace Niko666.H3MPFootstep
{
public static class FootstepAudioBank
{
private static readonly string[] PackMaterials = new string[18]
{
"Concrete", "Generic", "Grass", "Gravel", "MetalThick", "MetalThin", "Mud", "Meat", "SoftMaterial", "Water",
"WoodHeavy", "WoodLight", "Sand", "Brick", "Rock", "Ice", "Glass", "SnowNorthestDakota"
};
private static readonly Dictionary<string, List<AudioClip>> _clips = new Dictionary<string, List<AudioClip>>();
private static bool _loadStarted;
private static bool _loadDone;
private static AudioSource[] _pool;
private static int _poolIndex;
private const int PoolSize = 8;
public static bool LoadDone => _loadDone;
public static bool HasClips(string material)
{
if (_clips.TryGetValue(material, out var value) && value != null)
{
return value.Count > 0;
}
return false;
}
public static AudioClip RandomClip(string material)
{
if (!_clips.TryGetValue(material, out var value) || value == null || value.Count == 0)
{
return null;
}
return value[Random.Range(0, value.Count)];
}
public static void StartLoad()
{
if (!_loadStarted)
{
_loadStarted = true;
H3MPFootstepPlugin instance = H3MPFootstepPlugin.Instance;
if (!((Object)(object)instance == (Object)null))
{
((MonoBehaviour)instance).StartCoroutine(LoadAll());
}
}
}
public static string MaterialFor(BulletImpactSoundType t)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Expected I4, but got Unknown
switch ((int)t)
{
case 10:
return "Grass";
case 25:
return "WoodHeavy";
case 26:
case 27:
return "WoodLight";
case 11:
return "Gravel";
case 17:
return "Mud";
case 13:
return "Meat";
case 24:
return "Water";
case 23:
return "SoftMaterial";
case 21:
case 22:
return "Sand";
case 15:
return "MetalThick";
case 1:
case 2:
case 14:
case 16:
return "MetalThin";
case 3:
return "Brick";
case 20:
return "Rock";
case 12:
return "Ice";
case 6:
case 7:
case 8:
case 9:
return "Glass";
case 4:
return "Concrete";
default:
return "Generic";
}
}
private static IEnumerator LoadAll()
{
_clips.Clear();
int loaded = 0;
string[] packMaterials = PackMaterials;
foreach (string mat in packMaterials)
{
List<AudioClip> list = new List<AudioClip>();
_clips[mat] = list;
string pluginPath = Paths.PluginPath;
if (Directory.Exists(pluginPath))
{
string[] files = Directory.GetFiles(pluginPath, "PlayerFootsteps_" + mat + "*.wav", SearchOption.AllDirectories);
string[] array = files;
foreach (string path in array)
{
Action<AudioClip> onDone = delegate(AudioClip clip)
{
if ((Object)(object)clip != (Object)null)
{
list.Add(clip);
int num = loaded;
loaded = num + 1;
}
};
yield return LoadClip(path, onDone);
}
}
if (list.Count != 0)
{
continue;
}
string text = PluginResourcePath(mat + ".wav");
if (!File.Exists(text))
{
continue;
}
string path2 = text;
Action<AudioClip> onDone2 = delegate(AudioClip clip)
{
if ((Object)(object)clip != (Object)null)
{
list.Add(clip);
int num = loaded;
loaded = num + 1;
}
};
yield return LoadClip(path2, onDone2);
}
_loadDone = true;
H3MPFootstepPlugin.Log.LogInfo((object)$"Footstep audio bank loaded: {loaded} clips.");
}
private static string PluginResourcePath(string file)
{
H3MPFootstepPlugin instance = H3MPFootstepPlugin.Instance;
if ((Object)(object)instance == (Object)null || ((BaseUnityPlugin)instance).Info == null)
{
return file;
}
string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)instance).Info.Location);
string text = Path.Combine(directoryName, file);
if (File.Exists(text))
{
return text;
}
return Path.Combine(Path.Combine(directoryName, "resources"), file);
}
public static void Play(BulletImpactSoundType matType, Vector3 pos, float volume, float pitch, AudioImpactIntensity intensity, float maxDist)
{
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
H3MPFootstepPlugin instance = H3MPFootstepPlugin.Instance;
AudioClip val = null;
if ((Object)(object)instance != (Object)null && instance.ConfigCustomAudio.Value)
{
string text = MaterialFor(matType);
if (text == "Generic" && (Object)(object)GM.TNH_Manager != (Object)null && GM.TNH_Manager.LevelName == "NorthestDakota")
{
text = "SnowNorthestDakota";
}
val = RandomClip(text);
}
if ((Object)(object)val != (Object)null)
{
PlayPooled(val, pos, Mathf.Clamp(volume, 0.02f, 2f), pitch);
return;
}
MatSoundType val2 = MapToVanilla(matType);
SM.PlayImpactSound((ImpactType)30, val2, intensity, pos, (FVRPooledAudioType)41, maxDist, volume, pitch);
}
private static MatSoundType MapToVanilla(BulletImpactSoundType t)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Expected I4, but got Unknown
switch (t - 1)
{
case 24:
case 25:
case 26:
return (MatSoundType)1;
case 0:
case 1:
case 13:
case 14:
case 15:
return (MatSoundType)4;
case 9:
case 10:
case 16:
case 20:
case 21:
return (MatSoundType)3;
case 12:
return (MatSoundType)8;
case 23:
return (MatSoundType)9;
case 11:
case 22:
return (MatSoundType)2;
default:
return (MatSoundType)0;
}
}
private static void PlayPooled(AudioClip clip, Vector3 pos, float volume, float pitch)
{
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
if (_pool == null)
{
CreatePool();
}
AudioSource val = _pool[_poolIndex];
_poolIndex = (_poolIndex + 1) % _pool.Length;
if ((Object)(object)val == (Object)null)
{
CreatePool();
val = _pool[_poolIndex];
_poolIndex = (_poolIndex + 1) % _pool.Length;
if ((Object)(object)val == (Object)null)
{
return;
}
}
((Component)val).transform.position = pos;
val.volume = volume;
val.pitch = pitch;
val.PlayOneShot(clip);
}
private static void CreatePool()
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Expected O, but got Unknown
_pool = (AudioSource[])(object)new AudioSource[8];
if (!((Object)(object)H3MPFootstepPlugin.Instance == (Object)null))
{
GameObject val = new GameObject("H3MPFootstep_AudioPool");
Object.DontDestroyOnLoad((Object)(object)val);
for (int i = 0; i < 8; i++)
{
AudioSource val2 = val.AddComponent<AudioSource>();
val2.playOnAwake = false;
val2.spatialBlend = 1f;
val2.dopplerLevel = 0f;
val2.loop = false;
val2.rolloffMode = (AudioRolloffMode)1;
val2.maxDistance = 60f;
val2.minDistance = 1f;
_pool[i] = val2;
}
}
}
private static IEnumerator LoadClip(string path, Action<AudioClip> onDone)
{
AudioClip clip = null;
UnityWebRequest www;
try
{
www = UnityWebRequest.GetAudioClip(path, (AudioType)20);
}
catch (Exception ex)
{
H3MPFootstepPlugin.Log.LogWarning((object)("Failed to load " + path + ": " + ex.Message));
onDone?.Invoke(null);
yield break;
}
yield return www.Send();
if (!www.isError)
{
clip = DownloadHandlerAudioClip.GetContent(www);
}
else
{
H3MPFootstepPlugin.Log.LogWarning((object)("Failed to load " + path + ": " + www.error));
}
www.Dispose();
onDone?.Invoke(clip);
}
}
public class FootstepLocalController : MonoBehaviour
{
private const float TeleportDist = 3f;
private const float MaxSprintSpeed = 6.5f;
private const float CrouchNeckHeight = 1.1f;
private Vector3 _lastPos;
private bool _hasLastPos;
private float _accumulatedDist;
private float _lastStepRealTime;
private bool _nextFootIsLeft = true;
private float _suppressUntil;
private void Update()
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_013b: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody;
if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.NeckJointTransform == (Object)null)
{
_hasLastPos = false;
return;
}
Vector3 headPositionFiltered = currentPlayerBody.headPositionFiltered;
float time = Time.time;
if (!_hasLastPos)
{
_lastPos = headPositionFiltered;
_hasLastPos = true;
return;
}
Vector3 val = headPositionFiltered - _lastPos;
_lastPos = headPositionFiltered;
if (((Vector3)(ref val)).magnitude > 3f)
{
_accumulatedDist = 0f;
_suppressUntil = time + 0.4f;
}
else
{
if (time < _suppressUntil)
{
return;
}
val.y = 0f;
float magnitude = ((Vector3)(ref val)).magnitude;
float num = Mathf.Max(Time.deltaTime, 0.0001f);
float num2 = magnitude / num;
if (num2 < H3MPFootstepPlugin.Instance.ConfigMinStepSpeed.Value)
{
_accumulatedDist = Mathf.Max(0f, _accumulatedDist - magnitude);
return;
}
_accumulatedDist += magnitude;
float num3 = ComputeStepLength(num2);
if (_accumulatedDist >= num3 && time - _lastStepRealTime >= MinInterval())
{
_accumulatedDist = 0f;
_lastStepRealTime = time;
if (H3MPFootstepPlugin.ProbeFloor(headPositionFiltered, out var mat, out var floorPos))
{
FireStep(headPositionFiltered, floorPos, mat, num2);
}
}
}
}
private static float MinInterval()
{
return Mathf.Max(0.15f, H3MPFootstepPlugin.Instance.ConfigSprintStepInterval.Value * 0.8f);
}
private float ComputeStepLength(float speed)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
_ = H3MPFootstepPlugin.Instance;
float num = Mathf.Clamp01(speed / 6.5f);
float num2 = Mathf.Lerp(0.85f, 1.05f, num);
Vector3 headPositionFiltered = GM.CurrentPlayerBody.headPositionFiltered;
if (H3MPFootstepPlugin.ProbeFloor(headPositionFiltered, out var _, out var floorPos) && headPositionFiltered.y - floorPos.y < 1.1f)
{
num2 *= 0.65f;
}
return num2;
}
private void FireStep(Vector3 neckPos, Vector3 floorPos, BulletImpactSoundType mat, float speed)
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
H3MPFootstepPlugin instance = H3MPFootstepPlugin.Instance;
float num = Mathf.Clamp01(speed / 6.5f);
float value = instance.ConfigBaseLoudness.Value;
value *= Mathf.Lerp(1f, instance.ConfigSprintLoudnessMult.Value, num);
bool flag = neckPos.y - floorPos.y < 1.1f;
if (flag)
{
value *= instance.ConfigCrouchLoudnessMult.Value;
}
float num2 = (_nextFootIsLeft ? 0.96f : 1.04f);
_nextFootIsLeft = !_nextFootIsLeft;
float num3 = value * num2;
float pitch = Random.Range(0.9f, 1.1f);
if (instance.ConfigPlayLocalFootsteps.Value)
{
FootstepAudioBank.Play(mat, floorPos, num3 * 0.8f, pitch, (AudioImpactIntensity)((num > 0.66f) ? 2 : ((num > 0.25f) ? 1 : 0)), instance.ConfigMaxHearingDistance.Value);
}
if (FootstepNet.IsNetworkReady())
{
FootstepNet.SendFootstep(floorPos, mat, num3, speed, flag);
}
H3MPFootstepPlugin.LogDebug($"[local] step: mat={mat} loud={num3:F2} speed={speed:F2} crouch={flag} net={FootstepNet.IsNetworkReady()}");
}
}
public class FootstepNet : MonoBehaviour
{
[CompilerGenerated]
private static class <>O
{
public static CustomPacketHandlerReceivedDelegate <0>__OnCustomPacketHandlerReceived;
public static CustomPacketHandler <1>__OnFootstepPacketReceived;
}
private const byte ProtocolVersion = 1;
private const string PacketID = "Niko666.H3MPFootstep.Footstep";
private const byte FlagCrouched = 1;
private static FootstepNet _instance;
private static int _footstepPacketID = -1;
private static bool _awaitingRegistrationResponse;
private static float _registrationRequestTime;
private const float RegistrationRetryDelay = 5f;
private static readonly Dictionary<int, float> _remotes = new Dictionary<int, float>();
private static bool IsHost => ThreadManager.host;
private void Awake()
{
_instance = this;
}
private void OnDestroy()
{
if ((Object)(object)_instance == (Object)(object)this)
{
_instance = null;
}
}
public static bool IsNetworkReady()
{
return (Object)(object)Mod.managerObject != (Object)null;
}
private void Update()
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
if (!IsNetworkReady())
{
if (_footstepPacketID >= 0 || _awaitingRegistrationResponse)
{
_footstepPacketID = -1;
_awaitingRegistrationResponse = false;
object obj = <>O.<0>__OnCustomPacketHandlerReceived;
if (obj == null)
{
CustomPacketHandlerReceivedDelegate val = OnCustomPacketHandlerReceived;
<>O.<0>__OnCustomPacketHandlerReceived = val;
obj = (object)val;
}
Mod.CustomPacketHandlerReceived -= (CustomPacketHandlerReceivedDelegate)obj;
}
}
else
{
TryRegisterPacket();
PruneStaleRemotes();
}
}
private static void TryRegisterPacket()
{
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Expected O, but got Unknown
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Expected O, but got Unknown
if (_footstepPacketID >= 0)
{
return;
}
if (Mod.registeredCustomPacketIDs.TryGetValue("Niko666.H3MPFootstep.Footstep", out var value))
{
_footstepPacketID = value;
HookHandler(value);
H3MPFootstepPlugin.Log.LogInfo((object)$"Footstep packet already registered as index {value}.");
return;
}
if (_awaitingRegistrationResponse)
{
if (Time.time - _registrationRequestTime < 5f)
{
return;
}
_awaitingRegistrationResponse = false;
H3MPFootstepPlugin.Log.LogWarning((object)"Footstep packet registration timed out - retrying.");
}
if (IsHost)
{
_footstepPacketID = Server.RegisterCustomPacketType("Niko666.H3MPFootstep.Footstep", 0);
HookHandler(_footstepPacketID);
H3MPFootstepPlugin.Log.LogInfo((object)$"Host registered footstep packet as index {_footstepPacketID}.");
}
else if ((Object)(object)Client.singleton != (Object)null && Client.singleton.isConnected)
{
object obj = <>O.<0>__OnCustomPacketHandlerReceived;
if (obj == null)
{
CustomPacketHandlerReceivedDelegate val = OnCustomPacketHandlerReceived;
<>O.<0>__OnCustomPacketHandlerReceived = val;
obj = (object)val;
}
Mod.CustomPacketHandlerReceived -= (CustomPacketHandlerReceivedDelegate)obj;
object obj2 = <>O.<0>__OnCustomPacketHandlerReceived;
if (obj2 == null)
{
CustomPacketHandlerReceivedDelegate val2 = OnCustomPacketHandlerReceived;
<>O.<0>__OnCustomPacketHandlerReceived = val2;
obj2 = (object)val2;
}
Mod.CustomPacketHandlerReceived += (CustomPacketHandlerReceivedDelegate)obj2;
ClientSend.RegisterCustomPacketType("Niko666.H3MPFootstep.Footstep");
_awaitingRegistrationResponse = true;
_registrationRequestTime = Time.time;
H3MPFootstepPlugin.Log.LogInfo((object)"Requested footstep packet registration from server.");
}
}
private static void OnCustomPacketHandlerReceived(string id, int index)
{
if (!(id != "Niko666.H3MPFootstep.Footstep"))
{
_footstepPacketID = index;
HookHandler(index);
H3MPFootstepPlugin.Log.LogInfo((object)$"Server assigned footstep packet index {index}.");
}
}
private static void HookHandler(int index)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
if (index >= 0 && index < Mod.customPacketHandlers.Length)
{
CustomPacketHandler[] customPacketHandlers = Mod.customPacketHandlers;
object obj = <>O.<1>__OnFootstepPacketReceived;
if (obj == null)
{
CustomPacketHandler val = OnFootstepPacketReceived;
<>O.<1>__OnFootstepPacketReceived = val;
obj = (object)val;
}
customPacketHandlers[index] = (CustomPacketHandler)obj;
}
}
public static void SendFootstep(Vector3 footPos, BulletImpactSoundType mat, float loudness, float speed, bool crouched)
{
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
if (!IsNetworkReady() || _footstepPacketID < 0)
{
return;
}
if (IsHost)
{
List<int> list = new List<int>();
foreach (KeyValuePair<int, PlayerManager> player in GameManager.players)
{
if (player.Key != GameManager.ID && !((Object)(object)player.Value == (Object)null) && !(player.Value.scene != GameManager.scene) && player.Value.instance == GameManager.instance)
{
list.Add(player.Key);
}
}
if (list.Count > 0)
{
SendDirectToClients(BuildPacket(GameManager.ID, mat, loudness, speed, footPos, crouched), list);
H3MPFootstepPlugin.LogDebug($"[net] host sent footstep to {list.Count} clients (loud {loudness:F2})");
}
}
else
{
Packet val = BuildPacket(GameManager.ID, mat, loudness, speed, footPos, crouched);
try
{
ClientSend.SendUDPData(val, true);
}
finally
{
((IDisposable)val)?.Dispose();
}
H3MPFootstepPlugin.LogDebug("[net] client sent footstep to server");
}
}
private static Packet BuildPacket(int senderID, BulletImpactSoundType mat, float loudness, float speed, Vector3 footPos, bool crouched)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Expected O, but got Unknown
Packet val = new Packet(_footstepPacketID);
val.Write((byte)1);
val.Write(senderID);
val.Write(crouched ? ((byte)1) : ((byte)0));
val.Write(loudness);
val.Write(speed);
val.Write((byte)mat);
val.Write(footPos);
return val;
}
private static void OnFootstepPacketReceived(int clientID, Packet packet)
{
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
try
{
if (packet.ReadByte(true) != 1)
{
return;
}
int num = packet.ReadInt(true);
byte b = packet.ReadByte(true);
float loudness = packet.ReadFloat(true);
float speed = packet.ReadFloat(true);
byte b2 = packet.ReadByte(true);
Vector3 footPos = packet.ReadVector3(true);
if (b2 > 28)
{
return;
}
BulletImpactSoundType mat = (BulletImpactSoundType)b2;
int num2 = (IsHost ? clientID : num);
bool crouched = (b & 1) != 0;
if (num2 != GameManager.ID)
{
if (IsHost)
{
RelayToOthers(num2, mat, loudness, speed, footPos, crouched);
}
PlayRemoteFootstep(num2, footPos, mat, loudness, speed, crouched);
}
}
catch (Exception ex)
{
H3MPFootstepPlugin.Log.LogWarning((object)$"Bad footstep packet from {clientID}: {ex.Message}");
}
}
private static void RelayToOthers(int senderID, BulletImpactSoundType mat, float loudness, float speed, Vector3 footPos, bool crouched)
{
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
if (_footstepPacketID < 0)
{
return;
}
List<int> list = new List<int>();
foreach (KeyValuePair<int, PlayerManager> player in GameManager.players)
{
if (player.Key != senderID && !((Object)(object)player.Value == (Object)null) && !(player.Value.scene != GameManager.scene) && player.Value.instance == GameManager.instance)
{
list.Add(player.Key);
}
}
if (list.Count != 0)
{
Packet val = BuildPacket(senderID, mat, loudness, speed, footPos, crouched);
try
{
SendDirectToClients(val, list);
}
finally
{
((IDisposable)val)?.Dispose();
}
H3MPFootstepPlugin.LogDebug($"[net] host relayed footstep from {senderID} to {list.Count} clients");
}
}
private static void SendDirectToClients(Packet packet, List<int> clientIDs)
{
byte[] bytes = BitConverter.GetBytes(BitConverter.ToInt32(new byte[4]
{
packet.buffer[0],
packet.buffer[1],
packet.buffer[2],
packet.buffer[3]
}, 0) * -1 - 2);
for (int i = 0; i < 4; i++)
{
packet.buffer[i] = bytes[i];
}
packet.WriteLength();
foreach (int clientID in clientIDs)
{
if (Server.clients.TryGetValue(clientID, out var value) && value != null && value.udp != null)
{
Server.SendUDPData(value.udp.endPoint, packet);
}
}
}
private static void PlayRemoteFootstep(int clientID, Vector3 footPos, BulletImpactSoundType mat, float loudness, float speed, bool crouched)
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
if (loudness <= 0.01f)
{
return;
}
float time = Time.time;
if (_remotes.TryGetValue(clientID, out var value) && time - value < 0.15f)
{
return;
}
_remotes[clientID] = time;
FVRPlayerBody currentPlayerBody = GM.CurrentPlayerBody;
if ((Object)(object)currentPlayerBody == (Object)null || (Object)(object)currentPlayerBody.Head == (Object)null)
{
return;
}
float num = Vector3.Distance(footPos, currentPlayerBody.Head.position);
float value2 = H3MPFootstepPlugin.Instance.ConfigMaxHearingDistance.Value;
if (!(num > value2))
{
float num2 = Mathf.Clamp01(speed / 4.5f);
AudioImpactIntensity intensity = (AudioImpactIntensity)((num2 > 0.66f) ? 2 : ((num2 > 0.25f) ? 1 : 0));
if (crouched)
{
intensity = (AudioImpactIntensity)0;
}
float pitch = Random.Range(0.92f, 1.1f);
float num3 = loudness * H3MPFootstepPlugin.Instance.ConfigMasterVolume.Value * Random.Range(0.9f, 1.1f);
FootstepAudioBank.Play(mat, footPos, num3, pitch, intensity, value2);
H3MPFootstepPlugin.LogDebug($"[remote {clientID}] play: mat={mat} vol={num3:F2} dist={num:F1}");
}
}
private static void PruneStaleRemotes()
{
if (_remotes.Count == 0)
{
return;
}
List<int> list = null;
foreach (KeyValuePair<int, float> remote in _remotes)
{
if (!GameManager.players.ContainsKey(remote.Key))
{
if (list == null)
{
list = new List<int>();
}
list.Add(remote.Key);
}
}
if (list == null)
{
return;
}
foreach (int item in list)
{
_remotes.Remove(item);
}
}
}
public class FootstepSceneWatcher : MonoBehaviour
{
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
H3MPFootstepPlugin.Log.LogInfo((object)("Scene '" + ((Scene)(ref scene)).name + "' loaded - footstep net standing by."));
}
}
[BepInProcess("h3vr.exe")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("Niko666.H3MPFootstep", "H3MP Footsteps", "0.3.0")]
public class H3MPFootstepPlugin : BaseUnityPlugin
{
internal static ManualLogSource Log;
internal static H3MPFootstepPlugin Instance;
internal ConfigEntry<float> ConfigMasterVolume;
internal ConfigEntry<float> ConfigBaseLoudness;
internal ConfigEntry<float> ConfigSprintLoudnessMult;
internal ConfigEntry<float> ConfigCrouchLoudnessMult;
internal ConfigEntry<float> ConfigSpeedStepInterval;
internal ConfigEntry<float> ConfigSprintStepInterval;
internal ConfigEntry<float> ConfigMinStepSpeed;
internal ConfigEntry<float> ConfigMaxHearingDistance;
internal ConfigEntry<bool> ConfigPlayLocalFootsteps;
internal ConfigEntry<bool> ConfigCustomAudio;
internal ConfigEntry<bool> ConfigDebugLogging;
private GameObject _managerObj;
private FootstepLocalController _localController;
public const string Id = "Niko666.H3MPFootstep";
public static string Name => "H3MP Footsteps";
public static string Version => "0.3.0";
private void Awake()
{
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
BindConfigs();
Harmony.CreateAndPatchAll(typeof(ReloadConfigPatch), (string)null);
Log.LogMessage((object)string.Format("{0} {1} loaded. H3MP present: {2}", "Niko666.H3MPFootstep", Version, IsH3MPAvailable()));
}
private void BindConfigs()
{
ConfigMasterVolume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MasterVolume", 1f, "Master volume multiplier for remote players' footstep sounds.");
ConfigBaseLoudness = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BaseLoudness", 1f, "Base loudness of a walking footstep.");
ConfigSprintLoudnessMult = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SprintLoudnessMult", 2f, "Loudness multiplier when the remote player is sprinting.");
ConfigCrouchLoudnessMult = ((BaseUnityPlugin)this).Config.Bind<float>("General", "CrouchLoudnessMult", 0.05f, "Loudness multiplier when the remote player is crouched (slow movement).");
ConfigSpeedStepInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Cadence", "WalkStepInterval", 0.65f, "Seconds between footsteps at normal walking speed.");
ConfigSprintStepInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Cadence", "SprintStepInterval", 0.35f, "Seconds between footsteps when sprinting.");
ConfigMinStepSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Cadence", "MinStepSpeed", 0.5f, "Minimum horizontal speed (m/s) for footsteps to trigger. Lower = more sensitive.");
ConfigMaxHearingDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxHearingDistance", 60f, "Max distance (m) at which remote footsteps can be heard.");
ConfigPlayLocalFootsteps = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "PlayLocalFootsteps", true, "Play your own footsteps (local, non-networked). When enabled this Mod also works as a standalone footstep mod without H3MP.");
ConfigCustomAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "CustomAudio", true, "Use custom footstep wavs (Kodeman PlayerFootsteps compatible packs). When disabled, remote footsteps fall back to H3VR's built-in impact sounds.");
ConfigDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLogging", false, "Print verbose footstep network logs.");
}
private void Start()
{
FootstepAudioBank.StartLoad();
TryWireManager();
}
private bool IsH3MPAvailable()
{
return Chainloader.PluginInfos.ContainsKey("VIP.TommySoucy.H3MP");
}
private void TryWireManager()
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
if (!((Object)(object)_managerObj != (Object)null))
{
_managerObj = new GameObject("H3MPFootstep_Manager");
Object.DontDestroyOnLoad((Object)(object)_managerObj);
_localController = _managerObj.AddComponent<FootstepLocalController>();
_managerObj.AddComponent<FootstepSceneWatcher>();
if (IsH3MPAvailable())
{
_managerObj.AddComponent<FootstepNet>();
Log.LogMessage((object)"H3MPFootstep manager created (networked). Monitoring player movement.");
}
else
{
Log.LogWarning((object)"H3MP (VIP.TommySoucy.H3MP) not found - remote footstep sync disabled. Local footsteps still work; install H3MP on all clients to enable multiplayer sync.");
}
}
}
private void Update()
{
if ((Object)(object)_managerObj == (Object)null)
{
TryWireManager();
}
}
internal static bool ProbeFloor(Vector3 fromNeck, out BulletImpactSoundType mat, out Vector3 floorPos)
{
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Expected I4, but got Unknown
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Expected I4, but got Unknown
mat = (BulletImpactSoundType)5;
floorPos = default(Vector3);
int num = 528384;
RaycastHit val = default(RaycastHit);
if (Physics.Raycast(fromNeck, Vector3.down, ref val, 2.2f, num, (QueryTriggerInteraction)1))
{
floorPos = ((RaycastHit)(ref val)).point;
PMat val2 = ((Component)((RaycastHit)(ref val)).collider).GetComponent<PMat>();
if ((Object)(object)val2 == (Object)null)
{
val2 = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<PMat>();
}
if ((Object)(object)val2 != (Object)null && (Object)(object)val2.MatDef != (Object)null)
{
mat = (BulletImpactSoundType)(int)val2.MatDef.BulletImpactSound;
}
else
{
MatDef matDef_Default = ManagerSingleton<PM>.Instance.MatDef_Default;
if ((Object)(object)matDef_Default != (Object)null)
{
mat = (BulletImpactSoundType)(int)matDef_Default.BulletImpactSound;
}
}
return true;
}
return false;
}
internal static void LogDebug(string msg)
{
if ((Object)(object)Instance != (Object)null && Instance.ConfigDebugLogging.Value)
{
Log.LogInfo((object)msg);
}
}
}
internal static class ReloadConfigPatch
{
[HarmonyPatch(typeof(SteamVR_LoadLevel), "Begin")]
[HarmonyPrefix]
public static bool BeginPatch()
{
((BaseUnityPlugin)H3MPFootstepPlugin.Instance).Config.Reload();
H3MPFootstepPlugin.LogDebug("Configuration reloaded.");
return true;
}
}
}