using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; using UnityEngine.Video; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("1 in 10000")] [assembly: AssemblyProduct("1 in 10000")] [assembly: AssemblyCompany("ModdedWolf")] [assembly: AssemblyDescription("1 in 10000 chance of a Foxy jumpscare in Valheim.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyVersion("1.0.0.0")] [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 OneIn10000 { internal static class ChromaKey { internal static Color32[] Apply(RenderTexture source, Texture2D destination, Color32[] buffer) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0140: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || (Object)(object)destination == (Object)null) { return buffer; } int num = ((Texture)destination).width * ((Texture)destination).height; RenderTexture active = RenderTexture.active; try { RenderTexture.active = source; destination.ReadPixels(new Rect(0f, 0f, (float)((Texture)destination).width, (float)((Texture)destination).height), 0, 0, false); } finally { RenderTexture.active = active; } buffer = destination.GetPixels32(); if (buffer == null || buffer.Length != num) { return buffer; } int num2 = 32; for (int i = 0; i < num; i++) { Color32 val = buffer[i]; int num3 = ((val.r > val.b) ? val.r : val.b); int num4 = val.g - num3; if (num4 >= 72) { buffer[i] = new Color32((byte)0, (byte)0, (byte)0, (byte)0); } else if (num4 > num2) { int num5 = 255 - (num4 - num2) * 255 / 40; if (num5 < 0) { num5 = 0; } if (val.g > num3) { val.g = (byte)num3; } val.a = (byte)num5; buffer[i] = val; } else { if (val.g > num3 + 10) { val.g = (byte)(num3 + 10); } val.a = byte.MaxValue; buffer[i] = val; } } destination.SetPixels32(buffer); destination.Apply(false, false); return buffer; } } internal static class EmbeddedVideo { internal const string ResourceName = "foxy.mp4"; internal static bool TryExtract(out string path, out string error) { Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("foxy.mp4"); if (manifestResourceStream == null) { path = null; error = "1 in 10000: embedded foxy.mp4 is missing from the DLL."; return false; } try { string text = Path.Combine(Path.GetTempPath(), "OneIn10000"); Directory.CreateDirectory(text); path = Path.Combine(text, "foxy.mp4"); if (!File.Exists(path) || new FileInfo(path).Length != manifestResourceStream.Length) { using FileStream destination = File.Create(path); manifestResourceStream.CopyTo(destination); } error = null; return true; } catch (IOException ex) { path = null; error = "1 in 10000: failed to unpack the embedded video. " + ex.Message; return false; } finally { manifestResourceStream.Dispose(); } } } internal static class FoxyNet { internal const string PlayRpc = "ModdedWolf_OneIn10000_Play"; internal const string SettingsRpc = "ModdedWolf_OneIn10000_Settings"; internal const string HelloRpc = "ModdedWolf_OneIn10000_Hello"; private static ZRoutedRpc _registeredOn; internal static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } internal static bool IsDedicated() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsDedicated(); } return false; } internal static bool IsWorldHost() { return IsServer(); } internal static bool IsInPlayableWorld() { if ((Object)(object)ZNet.instance == (Object)null) { return false; } if (IsDedicated()) { return true; } return (Object)(object)Player.m_localPlayer != (Object)null; } internal static void TryRegister(Action onPlay, Action onSettings, Action onHello) { if (ZRoutedRpc.instance != null && _registeredOn != ZRoutedRpc.instance) { ZRoutedRpc.instance.Register("ModdedWolf_OneIn10000_Play", onPlay); ZRoutedRpc.instance.Register("ModdedWolf_OneIn10000_Settings", onSettings); ZRoutedRpc.instance.Register("ModdedWolf_OneIn10000_Hello", onHello); _registeredOn = ZRoutedRpc.instance; OneIn10000Plugin.Log.LogInfo((object)"Registered jumpscare RPCs."); } } internal static bool IsRegistered() { if (_registeredOn != null) { return _registeredOn == ZRoutedRpc.instance; } return false; } internal static void Unregister() { _registeredOn = null; } internal static void SendPlay(long peerId) { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "ModdedWolf_OneIn10000_Play", Array.Empty()); } } internal static void SendSettings(long peerId, float chancePercent) { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(peerId, "ModdedWolf_OneIn10000_Settings", new object[1] { chancePercent }); } } internal static void SendHello(string version) { if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null)) { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null && serverPeer.m_uid != 0L) { ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "ModdedWolf_OneIn10000_Hello", new object[1] { version }); } } } internal static void KickPeer(ZNetPeer peer, string reason) { if (peer == null || (Object)(object)ZNet.instance == (Object)null) { return; } OneIn10000Plugin.Log.LogWarning((object)$"Kicking peer {peer.m_uid} ({peer.m_playerName}): {reason}"); try { string playerName = peer.m_playerName; if (!string.IsNullOrEmpty(playerName)) { ZNet.instance.Kick(playerName); return; } MethodInfo method = typeof(ZNet).GetMethod("InternalKick", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(ZNetPeer) }, null); if (method != null) { method.Invoke(ZNet.instance, new object[1] { peer }); return; } typeof(ZNet).GetMethod("SendDisconnect", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { typeof(ZNetPeer) }, null)?.Invoke(ZNet.instance, new object[1] { peer }); ZNet.instance.Disconnect(peer); } catch (Exception ex) { OneIn10000Plugin.Log.LogError((object)$"Failed to kick peer {peer.m_uid}: {ex.Message}"); } } internal static ZNetPeer FindPeer(long peerId) { List remotePeers = GetRemotePeers(); for (int i = 0; i < remotePeers.Count; i++) { ZNetPeer val = remotePeers[i]; if (val != null && val.m_uid == peerId) { return val; } } return null; } internal static List GetRemotePeers() { if ((Object)(object)ZNet.instance == (Object)null) { return new List(); } List connectedPeers = ZNet.instance.GetConnectedPeers(); if (connectedPeers != null && connectedPeers.Count > 0) { return connectedPeers; } return ZNet.instance.GetPeers() ?? new List(); } internal static bool IsTrustedSender(long sender) { if ((Object)(object)ZNet.instance == (Object)null) { return false; } if (IsServer()) { return true; } if (sender == 0L) { return true; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer == null) { return true; } return sender == serverPeer.m_uid; } } internal sealed class JumpscareController { private const float PrepareTimeoutSeconds = 8f; private const float StallTimeoutSeconds = 8f; private GameObject _root; private RawImage _image; private AspectRatioFitter _aspectFitter; private VideoPlayer _player; private AudioSource _audio; private RenderTexture _videoRt; private Texture2D _keyed; private Color32[] _chromaBuffer; private bool _started; private bool _framePending; private bool _finished; private bool _endFramePresented; private bool _loggedPlayError; private float _lastFrameTime; private int _playId; internal bool IsPlaying { get; private set; } internal void EnsureOverlay() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Expected O, but got Unknown //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Expected O, but got Unknown //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Expected O, but got Unknown if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("OneIn10000Overlay", new Type[1] { typeof(RectTransform) }); Object.DontDestroyOnLoad((Object)(object)_root); Canvas obj = _root.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 32767; obj.pixelPerfect = false; CanvasScaler obj2 = _root.AddComponent(); obj2.uiScaleMode = (ScaleMode)1; obj2.referenceResolution = new Vector2(1920f, 1080f); obj2.screenMatchMode = (ScreenMatchMode)0; obj2.matchWidthOrHeight = 0.5f; GameObject val = new GameObject("Foxy", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_root.transform, false); _image = val.AddComponent(); ((Graphic)_image).raycastTarget = false; ((Graphic)_image).color = Color.white; _image.uvRect = new Rect(0f, 0f, 1f, 1f); ((Behaviour)_image).enabled = false; RectTransform rectTransform = ((Graphic)_image).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.pivot = new Vector2(0.5f, 0.5f); rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; _aspectFitter = val.AddComponent(); _aspectFitter.aspectMode = (AspectMode)3; _audio = _root.AddComponent(); _audio.playOnAwake = false; _audio.loop = false; _audio.spatialBlend = 0f; _audio.ignoreListenerPause = true; _audio.bypassListenerEffects = true; _player = _root.AddComponent(); _player.playOnAwake = false; _player.isLooping = false; _player.waitForFirstFrame = true; _player.skipOnDrop = false; _player.playbackSpeed = 1f; _player.timeUpdateMode = (VideoTimeUpdateMode)2; _player.sendFrameReadyEvents = true; _player.source = (VideoSource)1; _player.renderMode = (VideoRenderMode)2; _player.aspectRatio = (VideoAspectRatio)3; _player.audioOutputMode = (VideoAudioOutputMode)1; _player.controlledAudioTrackCount = 1; _player.frameReady += new FrameReadyEventHandler(OnFrameReady); _player.loopPointReached += new EventHandler(OnFinished); _player.errorReceived += new ErrorEventHandler(OnError); } } internal IEnumerator Play(string path) { EnsureOverlay(); Stop(); int playId = ++_playId; IsPlaying = true; _loggedPlayError = false; _audio.volume = 1f; _player.url = path; _player.Prepare(); float timeout = Time.realtimeSinceStartup + 8f; while (IsPlaying && playId == _playId && !_player.isPrepared && Time.realtimeSinceStartup < timeout) { yield return null; } if (!IsPlaying || playId != _playId) { yield break; } if (!_player.isPrepared) { OneIn10000Plugin.Log.LogError((object)("Video failed to prepare: " + path)); Stop(); yield break; } EnsureTargets(); if (_player.audioTrackCount > 0) { _player.EnableAudioTrack((ushort)0, true); _player.SetTargetAudioSource((ushort)0, _audio); } _started = true; _lastFrameTime = Time.realtimeSinceStartup; _player.Play(); } internal void TickPlayback() { if (IsPlaying && _started) { if (_finished && _endFramePresented) { Stop(); } else if (!_finished && Time.realtimeSinceStartup - _lastFrameTime >= 8f) { OneIn10000Plugin.Log.LogError((object)"1 in 10000: video playback stalled."); Stop(); } } } internal void TickChroma() { if (IsPlaying && _framePending && !((Object)(object)_videoRt == (Object)null) && !((Object)(object)_keyed == (Object)null)) { _framePending = false; _chromaBuffer = ChromaKey.Apply(_videoRt, _keyed, _chromaBuffer); _image.texture = (Texture)(object)_keyed; ((Behaviour)_image).enabled = true; if (_finished) { _endFramePresented = true; } } } internal void Dispose() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown Stop(); if ((Object)(object)_player != (Object)null) { _player.frameReady -= new FrameReadyEventHandler(OnFrameReady); _player.loopPointReached -= new EventHandler(OnFinished); _player.errorReceived -= new ErrorEventHandler(OnError); } ReleaseTargets(); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } } private void EnsureTargets() { //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Expected O, but got Unknown int num = (int)((_player.width != 0) ? _player.width : 640); int num2 = (int)((_player.height != 0) ? _player.height : 360); float num3 = Mathf.Min(1f, 640f / (float)Mathf.Max(num, num2)); int num4 = Mathf.Max(1, Mathf.RoundToInt((float)num * num3)); int num5 = Mathf.Max(1, Mathf.RoundToInt((float)num2 * num3)); float num6 = ((_player.pixelAspectRatioNumerator != 0 && _player.pixelAspectRatioDenominator != 0) ? ((float)_player.pixelAspectRatioNumerator / (float)_player.pixelAspectRatioDenominator) : 1f); _aspectFitter.aspectRatio = (float)num * num6 / (float)num2; if ((Object)(object)_videoRt != (Object)null && (((Texture)_videoRt).width != num4 || ((Texture)_videoRt).height != num5)) { ReleaseTargets(); } if ((Object)(object)_videoRt == (Object)null) { _videoRt = new RenderTexture(num4, num5, 0, (RenderTextureFormat)0) { name = "OneIn10000RT", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1, antiAliasing = 1 }; _videoRt.Create(); _keyed = new Texture2D(num4, num5, (TextureFormat)4, false) { name = "OneIn10000Keyed", wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; } _player.targetTexture = _videoRt; _image.texture = (Texture)(object)_keyed; } private void ReleaseTargets() { if ((Object)(object)_player != (Object)null) { _player.targetTexture = null; } if ((Object)(object)_videoRt != (Object)null) { _videoRt.Release(); Object.Destroy((Object)(object)_videoRt); _videoRt = null; } if ((Object)(object)_keyed != (Object)null) { Object.Destroy((Object)(object)_keyed); _keyed = null; } _chromaBuffer = null; } private void OnFrameReady(VideoPlayer _, long frame) { if (IsPlaying && _started) { _lastFrameTime = Time.realtimeSinceStartup; _framePending = true; } } private void OnFinished(VideoPlayer _) { if (IsPlaying && _started) { _finished = true; _framePending = true; } } private void OnError(VideoPlayer _, string message) { if (!_loggedPlayError) { _loggedPlayError = true; OneIn10000Plugin.Log.LogError((object)("1 in 10000 video error: " + message)); } Stop(); } private void Stop() { _playId++; IsPlaying = false; _started = false; _framePending = false; _finished = false; _endFramePresented = false; if ((Object)(object)_image != (Object)null) { ((Behaviour)_image).enabled = false; } if ((Object)(object)_player != (Object)null) { _player.Stop(); } if ((Object)(object)_audio != (Object)null) { _audio.Stop(); } } } [BepInPlugin("ModdedWolf.OneIn10000", "1 in 10000", "1.0.0")] public class OneIn10000Plugin : BaseUnityPlugin { public const string GUID = "ModdedWolf.OneIn10000"; public const string NAME = "1 in 10000"; public const string VERSION = "1.0.0"; internal const int ProcessWidth = 640; private const float RollIntervalSeconds = 1f; private const float PerPlayerCooldown = 2f; private const float SettingsSyncInterval = 3f; private const float HelloResendInterval = 3f; private const float HelloGraceSeconds = 20f; internal static OneIn10000Plugin Instance; internal static ManualLogSource Log; internal static ConfigEntry ChancePercent; private JumpscareController _jumpscare; private float _nextRollTime; private float _nextSettingsSync; private float _nextHelloSend; private bool _loggedMissingVideo; private float _appliedChance = 0.01f; private readonly Dictionary _cooldownUntil = new Dictionary(); private readonly HashSet _verifiedPeers = new HashSet(); private readonly Dictionary _peerFirstSeen = new Dictionary(); internal const float PlaybackVolume = 1f; private void Awake() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ChancePercent = ((BaseUnityPlugin)this).Config.Bind("General", "ChancePercent", 0.01f, new ConfigDescription("Host/server only. Percent chance each second, rolled separately for every player. Client values are ignored.", (AcceptableValueBase)(object)new AcceptableValueRange(0.01f, 100f), Array.Empty())); _appliedChance = ChancePercent.Value; _jumpscare = new JumpscareController(); _nextRollTime = Time.unscaledTime + 5f; _nextSettingsSync = Time.unscaledTime + 2f; _nextHelloSend = 0f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"1 in 10000 v1.0.0 loaded. Host/server owns ChancePercent. Volume is fixed. Matching client version is required."); } private void Update() { FoxyNet.TryRegister(RPC_Play, RPC_Settings, RPC_Hello); if (!FoxyNet.IsDedicated()) { _jumpscare.TickPlayback(); } if (!FoxyNet.IsWorldHost()) { TrySendHello(); } else if (FoxyNet.IsInPlayableWorld()) { EnforceClientVersions(); if (Time.unscaledTime >= _nextSettingsSync) { _nextSettingsSync = Time.unscaledTime + 3f; SyncSettingsToPeers(); } if (!(Time.unscaledTime < _nextRollTime)) { _nextRollTime = Time.unscaledTime + 1f; RollEveryPlayer(); } } } private void LateUpdate() { if (!FoxyNet.IsDedicated() && _jumpscare.IsPlaying) { _jumpscare.TickChroma(); } } private void OnDestroy() { FoxyNet.Unregister(); _jumpscare?.Dispose(); if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void TrySendHello() { if (!FoxyNet.IsRegistered() || (Object)(object)ZNet.instance == (Object)null || FoxyNet.IsWorldHost()) { return; } if (FoxyNet.IsInPlayableWorld()) { if (!float.IsPositiveInfinity(_nextHelloSend)) { FoxyNet.SendHello("1.0.0"); _nextHelloSend = float.PositiveInfinity; } return; } if (float.IsPositiveInfinity(_nextHelloSend)) { _nextHelloSend = 0f; } if (!(Time.unscaledTime < _nextHelloSend)) { _nextHelloSend = Time.unscaledTime + 3f; FoxyNet.SendHello("1.0.0"); } } private void EnforceClientVersions() { List remotePeers = FoxyNet.GetRemotePeers(); HashSet hashSet = new HashSet(); for (int i = 0; i < remotePeers.Count; i++) { ZNetPeer val = remotePeers[i]; if (val == null || !IsPeerConnected(val)) { continue; } hashSet.Add(val.m_uid); if (!IsPeerReadyForHandshake(val)) { continue; } if (!_peerFirstSeen.ContainsKey(val.m_uid)) { _peerFirstSeen[val.m_uid] = Time.unscaledTime; } if (!_verifiedPeers.Contains(val.m_uid)) { float num = _peerFirstSeen[val.m_uid]; if (!(Time.unscaledTime - num < 20f)) { FoxyNet.KickPeer(val, string.Format("missing {0} v{1} handshake (no Hello within {2:0}s)", "1 in 10000", "1.0.0", 20f)); _peerFirstSeen.Remove(val.m_uid); } } } PrunePeerTracking(hashSet); } private static bool IsPeerReadyForHandshake(ZNetPeer peer) { if (!string.IsNullOrEmpty(peer.m_playerName)) { return true; } try { return peer.IsReady(); } catch { return false; } } private void PrunePeerTracking(HashSet present) { List list = null; foreach (long key in _peerFirstSeen.Keys) { if (!present.Contains(key)) { if (list == null) { list = new List(); } list.Add(key); } } if (list != null) { for (int i = 0; i < list.Count; i++) { long num = list[i]; _peerFirstSeen.Remove(num); _verifiedPeers.Remove(num); _cooldownUntil.Remove(num); } } } private void SyncSettingsToPeers() { List remotePeers = FoxyNet.GetRemotePeers(); for (int i = 0; i < remotePeers.Count; i++) { ZNetPeer val = remotePeers[i]; if (val != null && IsPeerConnected(val) && _verifiedPeers.Contains(val.m_uid)) { FoxyNet.SendSettings(val.m_uid, ChancePercent.Value); } } } private void RollEveryPlayer() { if (!FoxyNet.IsDedicated() && (Object)(object)Player.m_localPlayer != (Object)null) { long uID = ZNet.GetUID(); if (RolledHit(uID)) { Log.LogInfo((object)$"Jumpscare hit for host at {ChancePercent.Value}%."); PlayLocal(); } } List remotePeers = FoxyNet.GetRemotePeers(); for (int i = 0; i < remotePeers.Count; i++) { ZNetPeer val = remotePeers[i]; if (val != null && IsPeerConnected(val) && _verifiedPeers.Contains(val.m_uid) && RolledHit(val.m_uid)) { Log.LogInfo((object)$"Jumpscare hit for peer {val.m_uid} at {ChancePercent.Value}%."); FoxyNet.SendPlay(val.m_uid); } } } private static bool IsPeerConnected(ZNetPeer peer) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (peer.m_uid == 0L) { return false; } try { if (!peer.IsReady()) { return peer.m_characterID != ZDOID.None; } } catch { } return true; } private bool RolledHit(long playerId) { if (_cooldownUntil.TryGetValue(playerId, out var value) && Time.unscaledTime < value) { return false; } if (Random.Range(0f, 100f) >= ChancePercent.Value) { return false; } _cooldownUntil[playerId] = Time.unscaledTime + 2f; return true; } private void RPC_Play(long sender) { if (!FoxyNet.IsTrustedSender(sender)) { Log.LogWarning((object)$"Ignored jumpscare RPC from untrusted sender {sender}."); return; } Log.LogInfo((object)"Received jumpscare from the server."); PlayLocal(); } private void RPC_Settings(long sender, float chancePercent) { if (!FoxyNet.IsWorldHost() && FoxyNet.IsTrustedSender(sender)) { chancePercent = Mathf.Clamp(chancePercent, 0.01f, 100f); if (!(Mathf.Abs(_appliedChance - chancePercent) < 0.0001f)) { _appliedChance = chancePercent; Log.LogInfo((object)$"Server jumpscare chance is {chancePercent}% per player each second."); } } } private void RPC_Hello(long sender, string version) { if (!FoxyNet.IsWorldHost()) { return; } if (string.IsNullOrEmpty(version)) { version = "(empty)"; } ZNetPeer val = FoxyNet.FindPeer(sender); if (val == null) { Log.LogWarning((object)$"Hello from unknown peer {sender} with version {version}."); return; } if (!_peerFirstSeen.ContainsKey(sender)) { _peerFirstSeen[sender] = Time.unscaledTime; } if (version != "1.0.0") { FoxyNet.KickPeer(val, "need 1 in 10000 v1.0.0, got " + version); _verifiedPeers.Remove(sender); _peerFirstSeen.Remove(sender); } else if (_verifiedPeers.Add(sender)) { Log.LogInfo((object)string.Format("Peer {0} verified with {1} v{2}.", sender, "1 in 10000", "1.0.0")); FoxyNet.SendSettings(sender, ChancePercent.Value); } } private void PlayLocal() { if (FoxyNet.IsDedicated() || _jumpscare.IsPlaying) { return; } if (!EmbeddedVideo.TryExtract(out var path, out var error)) { if (!_loggedMissingVideo) { Log.LogWarning((object)error); _loggedMissingVideo = true; } } else { _loggedMissingVideo = false; ((MonoBehaviour)this).StartCoroutine(_jumpscare.Play(path)); } } } }