Decompiled source of repo webcam v1.2.1
RepoWebcamMod.dll
Decompiled 13 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using RepoWebcamMod.Logging; using RepoWebcamMod.Networking; using RepoWebcamMod.Networking.Steam; using RepoWebcamMod.Video; using Steamworks; using Steamworks.Data; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RepoWebcamMod")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.1.0")] [assembly: AssemblyInformationalVersion("1.2.1+2349fc2bd2b4c47bd15a67007d712f0d2e03af2a")] [assembly: AssemblyProduct("RepoWebcamMod")] [assembly: AssemblyTitle("RepoWebcamMod")] [assembly: AssemblyVersion("1.2.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 RepoWebcamMod { public static class PluginConstants { public const string PluginGuid = "com.skycheg.webcam"; public const string PluginName = "REPO Webcam Mod"; public const string PluginVersion = "1.2.1"; public const int SteamTransportVirtualPort = 31573; } public static class RepoWebcamBootstrap { private static readonly object Sync = new object(); private static ManualLogSource? _logger; private static ConfigFile? _config; private static bool _initialized; public static void Initialize(ManualLogSource logger, ConfigFile config) { //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) lock (Sync) { if (_initialized) { return; } _logger = logger; _config = config; SceneManager.activeSceneChanged += OnActiveSceneChanged; SceneManager.sceneLoaded += OnSceneLoaded; _initialized = true; } Scene activeScene = SceneManager.GetActiveScene(); TryEnsureRuntime("initialize", ((Scene)(ref activeScene)).name); } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { TryEnsureRuntime("scene-loaded", ((Scene)(ref scene)).name); } private static void OnActiveSceneChanged(Scene previousScene, Scene nextScene) { TryEnsureRuntime("scene-changed", ((Scene)(ref nextScene)).name); } private static void TryEnsureRuntime(string reason, string? sceneName) { if (!string.IsNullOrWhiteSpace(sceneName) && !((Object)(object)Object.FindObjectOfType<RepoWebcamRuntime>() != (Object)null) && _config != null) { RepoWebcamRuntime.EnsureInstance(_config); ManualLogSource? logger = _logger; if (logger != null) { logger.LogInfo((object)("Runtime created via bootstrap (" + reason + ") in scene '" + sceneName + "'.")); } } } } [BepInPlugin("com.skycheg.webcam", "REPO Webcam Mod", "1.2.1")] public sealed class RepoWebcamPlugin : BaseUnityPlugin { private void Awake() { RepoWebcamBootstrap.Initialize(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config); ((BaseUnityPlugin)this).Logger.LogInfo((object)"REPO Webcam Mod bootstrap initialized."); } } internal sealed class ConfigurationManagerAttributes { public bool? Browsable { get; set; } public string? Category { get; set; } public object? DefaultValue { get; set; } public bool? DispName { get; set; } public int? Order { get; set; } public bool? ReadOnly { get; set; } public bool? IsAdvanced { get; set; } public Action? CustomDrawer { get; set; } public bool? HideDefaultButton { get; set; } public bool? HideSettingName { get; set; } } public sealed class RepoWebcamRuntime : MonoBehaviour { private sealed class RemotePeerState { public string PlayerId { get; } public GameObject GameObject { get; } public MeshRenderer Renderer { get; } public Material Material { get; } public Texture2D? Texture { get; set; } public Transform? Anchor { get; set; } public Transform? AliveAnchor { get; set; } public Transform? DeathAnchor { get; set; } public RemoteAnchorSource AnchorSource { get; set; } public RemotePlayerLifecycleState LifecycleState { get; set; } public int LastSequence { get; set; } public float LastFrameAt { get; set; } public float NextResolveAt { get; set; } public bool Mirror { get; set; } public float Zoom { get; set; } = 1f; public float OffsetX { get; set; } public float OffsetY { get; set; } public float NextAnchorWarningAt { get; set; } public float NextDiagnosticLogAt { get; set; } public float NextFallbackLogAt { get; set; } public float LastAnchorSeenAt { get; set; } public bool UsingAnchorFallback { get; set; } public Vector3? LastKnownPosition { get; set; } public float NextPositionLogAt { get; set; } public RemotePeerState(string playerId, GameObject gameObject, MeshRenderer renderer, Material material) { PlayerId = playerId; GameObject = gameObject; Renderer = renderer; Material = material; LastFrameAt = Time.unscaledTime; NextResolveAt = 0f; LastAnchorSeenAt = float.NegativeInfinity; AnchorSource = RemoteAnchorSource.None; LifecycleState = RemotePlayerLifecycleState.Unknown; } } private enum RemoteAnchorSource { None, Direct, NameFallbackTrusted, ComponentFallback, NameFallback } private enum RemotePlayerLifecycleState { Unknown, Alive, Dead, AwaitingAliveAnchor } private const string RuntimeObjectName = "RepoWebcamRuntime"; private const string ProbeLobbyId = "repowebcam-probe-v1"; private const string VideoLobbyId = "repowebcam-video-v1"; private const string CameraSectionName = "Camera"; private const string RemoteCameraSectionName = "Remote Webcam"; private const string DefaultCameraOption = "Default"; private const string PreviewModeOff = "Off"; private const string PreviewModeAboveHead = "AboveHead"; private const string PreviewModeScreenDebug = "ScreenDebug"; private const string PreviewModeScreenOverlay = "ScreenOverlay"; private const int CameraRequestedWidth = 640; private const int CameraRequestedHeight = 360; private const int CameraRequestedFps = 30; private const string PreviewObjectName = "RepoWebcamPreview"; private const float PreviewHeightOffset = 0.35f; private const float PreviewPlaneHeight = 0.22f; private const float AnchorResolveIntervalSeconds = 1.25f; private const float CameraFallbackDistance = 0.9f; private const float CameraFallbackVerticalOffset = -0.08f; private const float ScreenDebugDistance = 0.7f; private const float ScreenDebugHorizontalOffset = 0.28f; private const float ScreenDebugVerticalOffset = -0.16f; private const float ScreenDebugPlaneHeight = 0.18f; private const float OverlayMarginPixels = 20f; private const float OverlayWidthPixels = 200f; private const float OverlayMinHeightPixels = 140f; private const float LocalFrameSendIntervalSeconds = 1f / 24f; private const int LocalFrameTargetWidth = 360; private const int LocalFrameTargetHeight = 360; private const int LocalFrameJpegQuality = 50; private const int LocalFrameMaxPayloadLength = 200000; private const float RemoteFrameTimeoutSeconds = 12f; private const float RemotePeerDisposeSeconds = 35f; private const float PlayerDisconnectDetectionSeconds = 18f; private const float RemoteBillboardHeightOffset = 1.75f; private const float RemoteBillboardCameraOffset = 0.24f; private const float RemoteBillboardPlaneHeight = 0.56f; private const float RemoteResolveIntervalSeconds = 1.2f; private const float RemoteAnchorFallbackMaxSeconds = 4f; private const float RemoteAnchorParkingCoordinateThreshold = 1000f; private const float RecoveryCheckIntervalSeconds = 3f; public const string PreviewPosTopLeft = "TopLeft"; public const string PreviewPosTopCenter = "TopCenter"; public const string PreviewPosTopRight = "TopRight"; public const string PreviewPosMiddleLeft = "MiddleLeft"; public const string PreviewPosMiddleRight = "MiddleRight"; public const string PreviewPosBottomLeft = "BottomLeft"; public const string PreviewPosBottomRight = "BottomRight"; private const bool RemoteDebugForceVisible = false; private const float RemoteDebugPositionLogIntervalSeconds = 5f; private const string MoreHeadHeadNodePath = "[RIG]/code_lean/code_tilt/ANIM BOT/_____________________________________/ANIM BODY BOT/_____________________________________/ANIM BODY TOP/code_body_top_up/code_body_top_side/_____________________________________/ANIM HEAD BOT/code_head_bot_up/code_head_bot_side/_____________________________________/ANIM HEAD TOP/code_head_top"; private const float SceneRecoveryGraceSeconds = 2.5f; private const float SteamTransportWarmupSeconds = 3f; private const float NetworkRecoveryCooldownSeconds = 12f; private const float LobbyMembershipReprobeDelaySeconds = 1.5f; private const int SoftRecoveryMaxAttempts = 3; private const int LocalCaptureFailureLimit = 3; private const float LocalCaptureSuppressionSeconds = 120f; private const float RemoteAnchorWarningIntervalSeconds = 8f; private const float RemoteFallbackLogIntervalSeconds = 5f; private const int OverlaySortingOrder = 32000; private const string OverlayCanvasObjectName = "RepoWebcamOverlayCanvas"; private const string OverlayPanelObjectName = "RepoWebcamOverlayPanel"; private const string OverlayRawImageObjectName = "RepoWebcamOverlayImage"; private static readonly Color OverlayPanelColor = new Color(0f, 0f, 0f, 0.35f); private const KeyCode LocalPreviewToggleKey = 289; private static RepoWebcamRuntime? _instance; private static ConfigFile? _bootstrapConfig; private static bool _applicationQuitting; private ConfigFile? _config; private FileLogger? _fileLogger; private ISteamP2PTransport? _steamTransport; private ISignalingChannel? _signalingChannel; private IDisposable? _signalingDisposable; private IVideoChannel? _videoChannel; private IDisposable? _videoDisposable; private ConfigEntry<bool>? _cameraEnabled; private ConfigEntry<bool>? _cameraMirror; private ConfigEntry<string>? _cameraDevice; private ConfigEntry<bool>? _showLocalPreview; private ConfigEntry<string>? _localPreviewPosition; private ConfigEntry<float>? _cameraZoom; private ConfigEntry<float>? _cameraOffsetX; private ConfigEntry<float>? _cameraOffsetY; private ConfigEntry<float>? _overlaySize; private ConfigEntry<float>? _remoteHeightOffset; private ConfigEntry<float>? _remoteDepthOffset; private ConfigEntry<float>? _remoteScale; private WebCamTexture? _localCameraTexture; private string? _activeCameraDevice; private Coroutine? _cameraStartupCoroutine; private bool _suppressCameraConfigEvents; private GameObject? _previewObject; private MeshRenderer? _previewRenderer; private Material? _previewMaterial; private GameObject? _overlayCanvasObject; private RectTransform? _overlayRectTransform; private RawImage? _overlayRawImage; private int _overlayScreenWidth; private int _overlayScreenHeight; private Transform? _localPlayerRoot; private Transform? _headAnchor; private float _nextAnchorResolveAt; private bool _anchorLogged; private bool _fallbackLogged; private int _previewRenderLayer = -1; private int _softRecoveryAttempts; private float _lastSceneChangeAt; private float _nextCameraRecoveryAt; private float _nextPreviewDiagnosticsAt; private float _nextTransportDiagnosticsAt; private int _localCaptureFailureCount; private bool _localCaptureSuppressed; private float _localCaptureSuppressedUntil; private bool _requiredRuntimeModuleReady; private bool _requiredRuntimeModuleBlockedLogged; private string? _localCaptureRetryBlockedDevice; private bool _localCaptureRetryBlockLogged; private Coroutine? _localFrameBroadcastCoroutine; private OutboundVideoFramePublisher? _outboundVideoPublisher; private InboundVideoFrameReceiver? _inboundVideoReceiver; private int _inboundVideoFrameCount; private long _inboundVideoBytes; private int _inboundProbeCount; private int _inboundUnknownPayloadCount; private readonly HashSet<string> _inboundVideoSources = new HashSet<string>(StringComparer.Ordinal); private readonly Dictionary<string, RemotePeerState> _remotePeers = new Dictionary<string, RemotePeerState>(StringComparer.Ordinal); private readonly HashSet<string> _peersMarkedDisconnected = new HashSet<string>(StringComparer.Ordinal); private int _probeSequence; private Coroutine? _probeCoroutine; private bool _probeSent; private string _lastLobbyMemberSignature = string.Empty; private float _scheduledLobbyMembershipReprobeAt = -1f; private bool _outboundNetworkWarmupPending = true; private float _outboundNetworkWarmupUntil; private string _outboundNetworkWarmupReason = "startup"; private float _nextNetworkRecoveryAt; private Shader? _cachedPreviewShader; private bool _sceneShadersLogged; private Sprite? _circleSprite; public static void EnsureInstance(ConfigFile config) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown _bootstrapConfig = config; if (!((Object)(object)_instance != (Object)null)) { RepoWebcamRuntime repoWebcamRuntime = Object.FindObjectOfType<RepoWebcamRuntime>(); if ((Object)(object)repoWebcamRuntime != (Object)null) { _instance = repoWebcamRuntime; return; } GameObject val = new GameObject("RepoWebcamRuntime"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent<RepoWebcamRuntime>(); } } private void Awake() { //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; _applicationQuitting = false; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); _config = _bootstrapConfig; _fileLogger = new FileLogger("REPO Webcam Mod"); InitializeCameraConfig(); RecreateNetworkingComponents(out var usingSteamTransport); SceneManager.activeSceneChanged += OnActiveSceneChanged; ArmOutboundNetworkWarmup("startup"); AttachRequiredRuntimeModules(); _fileLogger.WriteInfo("Plugin initialized."); _fileLogger.WriteInfo(usingSteamTransport ? "Signaling channel: Steam relay transport." : "Signaling channel: unavailable."); FileLogger? fileLogger = _fileLogger; object arg = ((Object)this).GetInstanceID(); string name = ((Object)((Component)this).gameObject).name; Scene scene = ((Component)this).gameObject.scene; fileLogger.WriteInfo($"Lifecycle: instanceId={arg}, gameObject='{name}', scene='{((Scene)(ref scene)).name}'."); } private void Start() { _fileLogger?.WriteInfo("Start invoked. Preparing deferred signaling probe."); if (CanUseCameraRuntime("start", logIfBlocked: true)) { TryApplyCameraState("start"); EnsureLocalFrameBroadcastRoutine(); EnsureProbeRoutineStarted("startup"); } } private void Update() { if (CanUseCameraRuntime("update")) { _steamTransport?.Tick(); TrackLobbyMembershipChanges(); EnsureCameraPlaybackHealthy(); HandleHotkeys(); ResolveLocalAnchorIfNeeded(); UpdatePreviewTransform(); UpdateRemotePeers(); UpdateOverlayLayoutIfNeeded(); LogPreviewDiagnosticsTick(); LogTransportDiagnosticsTick(); } } private void TrackLobbyMembershipChanges() { IReadOnlyList<ulong> lobbyMemberIds = SteamLobbyAccessor.GetLobbyMemberIds(); string text = ((lobbyMemberIds.Count == 0) ? string.Empty : string.Join(",", lobbyMemberIds.OrderBy((ulong id) => id))); if (string.Equals(text, _lastLobbyMemberSignature, StringComparison.Ordinal)) { if (_scheduledLobbyMembershipReprobeAt > 0f && Time.unscaledTime >= _scheduledLobbyMembershipReprobeAt) { _scheduledLobbyMembershipReprobeAt = -1f; TriggerLobbyMembershipReprobe(); } return; } if (!string.IsNullOrEmpty(_lastLobbyMemberSignature)) { _fileLogger?.WriteInfo("Lobby membership changed: '" + _lastLobbyMemberSignature + "' -> '" + text + "'. Scheduling reprobe."); _scheduledLobbyMembershipReprobeAt = Time.unscaledTime + 1.5f; } _lastLobbyMemberSignature = text; } private void TriggerLobbyMembershipReprobe() { if (_signalingChannel == null) { return; } _probeSent = false; _fileLogger?.WriteInfo("Triggering lobby membership reprobe."); EnsureProbeRoutineStarted("lobby-membership-change"); try { SendTransformUpdate(_cameraZoom?.Value ?? 1f, _cameraOffsetX?.Value ?? 0f, _cameraOffsetY?.Value ?? 0f); } catch (Exception exception) { _fileLogger?.WriteError("Failed to send transform update during lobby membership reprobe.", exception); } } private void EnsureProbeRoutineStarted(string reason) { if (!_probeSent && _probeCoroutine == null) { _probeCoroutine = ((MonoBehaviour)this).StartCoroutine(ProbeWhenReadyRoutine(reason)); } } private void RecreateNetworkingComponents(out bool usingSteamTransport) { if (_signalingChannel != null) { _signalingChannel.PayloadReceived -= OnSignalingPayloadReceived; } if (_videoChannel != null) { _videoChannel.FrameReceived -= OnVideoFrameReceived; } _signalingDisposable?.Dispose(); _signalingDisposable = null; _videoDisposable?.Dispose(); _videoDisposable = null; (_steamTransport as IDisposable)?.Dispose(); _steamTransport = null; _steamTransport = new SteamP2PTransport(_fileLogger, 31573); usingSteamTransport = true; _signalingChannel = SignalingChannelFactory.Create(_steamTransport); _signalingDisposable = _signalingChannel as IDisposable; _signalingChannel.PayloadReceived += OnSignalingPayloadReceived; _videoChannel = new SteamP2PVideoChannel(_steamTransport); _videoDisposable = _videoChannel as IDisposable; _videoChannel.FrameReceived += OnVideoFrameReceived; _outboundVideoPublisher?.Dispose(); _outboundVideoPublisher = new OutboundVideoFramePublisher(_videoChannel, _fileLogger, 1f / 24f, 360, 360, 50); _inboundVideoReceiver = new InboundVideoFrameReceiver(); } private void ResetAllRemoteWebcamsAndReprobe(string reason) { float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextNetworkRecoveryAt) { _fileLogger?.WriteInfo($"Network recovery skipped due to cooldown. reason={reason}, retryIn={_nextNetworkRecoveryAt - unscaledTime:0.0}s."); return; } _nextNetworkRecoveryAt = unscaledTime + 12f; _fileLogger?.WriteWarning("Starting webcam network recovery. reason=" + reason + "."); if (_probeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_probeCoroutine); _probeCoroutine = null; } _probeSent = false; _probeSequence = 0; _inboundProbeCount = 0; _inboundUnknownPayloadCount = 0; _inboundVideoSources.Clear(); _peersMarkedDisconnected.Clear(); DisposeRemotePeers("network-recovery:" + reason); RecreateNetworkingComponents(out var usingSteamTransport); ArmOutboundNetworkWarmup("network-recovery:" + reason); if ((Object)(object)_localCameraTexture != (Object)null && _localCameraTexture.isPlaying) { EnsureLocalFrameBroadcastRoutine(); } EnsureProbeRoutineStarted("network-recovery:" + reason); _fileLogger?.WriteInfo("Webcam network recovery armed. signalingProvider=" + (usingSteamTransport ? "SteamRelay" : "disabled") + "."); } private IEnumerator ProbeWhenReadyRoutine(string reason) { int attempt = 0; while (_signalingChannel != null && !_signalingChannel.IsReady && attempt < 40) { attempt++; if (attempt == 1 || attempt % 10 == 0) { _fileLogger?.WriteInfo($"Signaling is not ready yet. Waiting before probe send. attempt={attempt}."); } yield return (object)new WaitForSecondsRealtime(0.5f); } _probeCoroutine = null; if (_signalingChannel == null || _probeSent) { yield break; } if (!_signalingChannel.IsReady) { _fileLogger?.WriteWarning("Signaling did not become ready before timeout. Probe send skipped."); yield break; } int warmupAttempts = 0; while (_signalingChannel != null && !CanSendTransportTraffic() && warmupAttempts < 80) { warmupAttempts++; yield return (object)new WaitForSecondsRealtime(0.25f); } if (_signalingChannel != null) { if (!CanSendTransportTraffic()) { _fileLogger?.WriteWarning("Steam transport warmup did not finish in time. Probe send skipped."); yield break; } _probeSent = true; SendProbeAsync(reason); } } private void ArmOutboundNetworkWarmup(string reason) { _outboundNetworkWarmupPending = true; _outboundNetworkWarmupUntil = 0f; _outboundNetworkWarmupReason = reason; } private bool CanSendTransportTraffic() { if (_signalingChannel == null || !_signalingChannel.IsReady) { _outboundNetworkWarmupUntil = 0f; return false; } if (!_outboundNetworkWarmupPending) { return true; } if (_outboundNetworkWarmupUntil <= 0f) { _outboundNetworkWarmupUntil = Time.unscaledTime + 3f; _fileLogger?.WriteInfo($"Steam transport warmup started: {3f:0.0}s before outbound packets. reason={_outboundNetworkWarmupReason}."); } if (Time.unscaledTime < _outboundNetworkWarmupUntil) { return false; } _outboundNetworkWarmupPending = false; _outboundNetworkWarmupUntil = 0f; _fileLogger?.WriteInfo("Steam transport warmup completed. Outbound packets unlocked."); return true; } private async Task SendProbeAsync(string reason) { if (_signalingChannel == null) { return; } try { int probeId = Interlocked.Increment(ref _probeSequence); string payload = $"{{\"type\":\"probe\",\"reason\":\"{reason}\",\"id\":{probeId},\"utc\":\"{DateTime.UtcNow:O}\"}}"; await _signalingChannel.SendToLobbyAsync("repowebcam-probe-v1", payload, CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false); _fileLogger?.WriteInfo(string.Format("Probe sent: id={0}, lobby={1}.", probeId, "repowebcam-probe-v1")); } catch (Exception exception) { _fileLogger?.WriteError("Failed to send signaling probe.", exception); } } private void OnVideoFrameReceived(object? sender, VideoFrameReceivedEventArgs eventArgs) { if (_inboundVideoReceiver != null && _inboundVideoReceiver.TryHandleVideo(eventArgs, out InboundVideoFrame frame) && frame != null) { _inboundVideoFrameCount++; _inboundVideoBytes += frame.JpegBytes.LongLength; if (_inboundVideoSources.Add(eventArgs.SourcePlayerId)) { _fileLogger?.WriteInfo($"Incoming video stream detected: from={eventArgs.SourcePlayerId}, seq={frame.Sequence}, size={frame.Width}x{frame.Height}, bytes={frame.JpegBytes.Length}."); } ApplyIncomingVideoFrame(eventArgs.SourcePlayerId, frame); } } private void OnSignalingPayloadReceived(object? sender, SignalingPayloadReceivedEventArgs eventArgs) { if (eventArgs.Payload is WebcamTransformState packet) { ApplyIncomingTransform(eventArgs.SourcePlayerId, packet); } else if (eventArgs.Payload is string text && text.IndexOf("\"type\":\"probe\"", StringComparison.OrdinalIgnoreCase) >= 0) { _inboundProbeCount++; _fileLogger?.WriteInfo("Probe received: lobby=" + eventArgs.LobbyId + ", from=" + eventArgs.SourcePlayerId + "."); if (_cameraEnabled != null && _cameraEnabled.Value) { float zoom = _cameraZoom?.Value ?? 1f; float offsetX = _cameraOffsetX?.Value ?? 0f; float offsetY = _cameraOffsetY?.Value ?? 0f; SendTransformUpdate(zoom, offsetX, offsetY); } } else if (eventArgs.Payload is string text2) { _inboundUnknownPayloadCount++; _fileLogger?.WriteInfo($"Signaling payload received: lobby={eventArgs.LobbyId}, from={eventArgs.SourcePlayerId}, len={text2.Length}."); } } private void OnActiveSceneChanged(Scene previousScene, Scene nextScene) { _fileLogger?.WriteInfo("Scene changed: '" + ((Scene)(ref previousScene)).name + "' -> '" + ((Scene)(ref nextScene)).name + "'."); _localPlayerRoot = null; _headAnchor = null; _anchorLogged = false; _lastSceneChangeAt = Time.unscaledTime; _nextCameraRecoveryAt = Time.unscaledTime + 2.5f; _softRecoveryAttempts = 0; ArmOutboundNetworkWarmup("scene-change"); _fileLogger?.WriteInfo($"Camera recovery grace period enabled: {2.5f:0.0}s."); EnsureProbeRoutineStarted("scene-change"); } private void OnApplicationQuit() { _applicationQuitting = true; _fileLogger?.WriteInfo("OnApplicationQuit invoked."); } private void OnDestroy() { //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } SceneManager.activeSceneChanged -= OnActiveSceneChanged; if (_signalingChannel != null) { _signalingChannel.PayloadReceived -= OnSignalingPayloadReceived; } if (_probeCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_probeCoroutine); _probeCoroutine = null; } if (_localFrameBroadcastCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_localFrameBroadcastCoroutine); _localFrameBroadcastCoroutine = null; } UnsubscribeCameraConfigEvents(); StopLocalCamera("destroy"); DestroyPreviewObject(); DestroyOverlayUi(); DisposeRemotePeers("destroy"); _outboundVideoPublisher?.Dispose(); _outboundVideoPublisher = null; _inboundVideoReceiver = null; _inboundVideoSources.Clear(); _signalingDisposable?.Dispose(); _signalingDisposable = null; _videoDisposable?.Dispose(); _videoDisposable = null; (_steamTransport as IDisposable)?.Dispose(); _steamTransport = null; _signalingChannel = null; _videoChannel = null; _fileLogger?.WriteInfo("Plugin disposed."); FileLogger? fileLogger = _fileLogger; if (fileLogger != null) { object arg = ((Object)this).GetInstanceID(); object arg2 = _applicationQuitting; Scene scene = ((Component)this).gameObject.scene; fileLogger.WriteInfo($"Lifecycle: OnDestroy instanceId={arg}, quitting={arg2}, scene='{((Scene)(ref scene)).name}'."); } _fileLogger?.Dispose(); } private void InitializeCameraConfig() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Expected O, but got Unknown //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Expected O, but got Unknown //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Expected O, but got Unknown //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Expected O, but got Unknown if (_config == null) { _fileLogger?.WriteWarning("ConfigFile was not provided to RepoWebcamRuntime. Camera settings were not created."); return; } string[] array = BuildCameraOptions(); string text = array[0]; _cameraEnabled = _config.Bind<bool>("Camera", "Enabled", true, new ConfigDescription("Enable the webcam module.", (AcceptableValueBase)null, Array.Empty<object>())); _cameraMirror = _config.Bind<bool>("Camera", "Mirror", false, new ConfigDescription("Mirror the local video feed.", (AcceptableValueBase)null, Array.Empty<object>())); _cameraDevice = _config.Bind<string>("Camera", "Device", text, new ConfigDescription("Webcam device used by the mod.", (AcceptableValueBase)(object)new AcceptableValueList<string>(array), Array.Empty<object>())); _showLocalPreview = _config.Bind<bool>("Camera", "ShowLocalPreview", true, new ConfigDescription("Show the local camera preview on screen as an overlay.", (AcceptableValueBase)null, Array.Empty<object>())); _localPreviewPosition = _config.Bind<string>("Camera", "LocalPreviewPosition", "TopCenter", new ConfigDescription("Position of the local preview on screen.", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[7] { "TopLeft", "TopCenter", "TopRight", "MiddleLeft", "MiddleRight", "BottomLeft", "BottomRight" }), Array.Empty<object>())); _cameraZoom = _config.Bind<float>("Camera", "Zoom", 1f, new ConfigDescription("Video zoom", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); _cameraOffsetX = _config.Bind<float>("Camera", "OffsetX", 0f, new ConfigDescription("Video offset on X", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>())); _cameraOffsetY = _config.Bind<float>("Camera", "OffsetY", 0f, new ConfigDescription("Video offset on Y", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>())); _overlaySize = _config.Bind<float>("Camera", "PreviewSize", 200f, new ConfigDescription("Local preview size in pixels", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 800f), Array.Empty<object>())); _remoteHeightOffset = _config.Bind<float>("Remote Webcam", "Height (Y)", 0.85f, new ConfigDescription("Vertical webcam offset above remote players (Y)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>())); _remoteDepthOffset = _config.Bind<float>("Remote Webcam", "Depth (Z)", 0.3f, new ConfigDescription("Remote webcam depth offset (Z)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), Array.Empty<object>())); _remoteScale = _config.Bind<float>("Remote Webcam", "Size (Scale)", 0.56f, new ConfigDescription("Remote webcam size", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>())); if (!array.Contains<string>(_cameraDevice.Value, StringComparer.Ordinal)) { _fileLogger?.WriteInfo("Camera " + _cameraDevice.Value + " is missing from options"); } NormalizePreviewPositionValue(); SubscribeCameraConfigEvents(); _fileLogger?.WriteInfo("Detected cameras: " + string.Join(", ", array) + "."); _fileLogger?.WriteInfo($"Current camera settings: Enabled={_cameraEnabled.Value}, Mirror={_cameraMirror.Value}, Device='{_cameraDevice.Value}', ShowLocalPreview={_showLocalPreview.Value}, LocalPreviewPosition='{_localPreviewPosition.Value}'."); } private static string[] BuildCameraOptions() { //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) WebCamDevice[] devices = WebCamTexture.devices; if (devices == null || devices.Length == 0) { return new string[1] { "Default" }; } List<string> list = new List<string>(devices.Length + 1) { "Default" }; WebCamDevice[] array = devices; for (int i = 0; i < array.Length; i++) { WebCamDevice val = array[i]; string name = ((WebCamDevice)(ref val)).name; if (!string.IsNullOrWhiteSpace(name) && !list.Contains<string>(name, StringComparer.Ordinal)) { list.Add(name); } } return list.ToArray(); } private void SubscribeCameraConfigEvents() { if (_cameraEnabled != null) { _cameraEnabled.SettingChanged += OnCameraEnabledSettingChanged; } if (_cameraMirror != null) { _cameraMirror.SettingChanged += OnCameraMirrorSettingChanged; } if (_cameraDevice != null) { _cameraDevice.SettingChanged += OnCameraDeviceSettingChanged; } if (_showLocalPreview != null) { _showLocalPreview.SettingChanged += OnLocalPreviewSettingChanged; } if (_localPreviewPosition != null) { _localPreviewPosition.SettingChanged += OnLocalPreviewSettingChanged; } if (_cameraZoom != null) { _cameraZoom.SettingChanged += OnCameraTransformSettingChanged; } if (_cameraOffsetX != null) { _cameraOffsetX.SettingChanged += OnCameraTransformSettingChanged; } if (_cameraOffsetY != null) { _cameraOffsetY.SettingChanged += OnCameraTransformSettingChanged; } if (_overlaySize != null) { _overlaySize.SettingChanged += OnLocalPreviewSettingChanged; } } private void UnsubscribeCameraConfigEvents() { if (_cameraEnabled != null) { _cameraEnabled.SettingChanged -= OnCameraEnabledSettingChanged; } if (_cameraMirror != null) { _cameraMirror.SettingChanged -= OnCameraMirrorSettingChanged; } if (_cameraDevice != null) { _cameraDevice.SettingChanged -= OnCameraDeviceSettingChanged; } if (_showLocalPreview != null) { _showLocalPreview.SettingChanged -= OnLocalPreviewSettingChanged; } if (_localPreviewPosition != null) { _localPreviewPosition.SettingChanged -= OnLocalPreviewSettingChanged; } if (_cameraZoom != null) { _cameraZoom.SettingChanged -= OnCameraTransformSettingChanged; } if (_cameraOffsetX != null) { _cameraOffsetX.SettingChanged -= OnCameraTransformSettingChanged; } if (_cameraOffsetY != null) { _cameraOffsetY.SettingChanged -= OnCameraTransformSettingChanged; } if (_overlaySize != null) { _overlaySize.SettingChanged -= OnLocalPreviewSettingChanged; } } private void OnCameraEnabledSettingChanged(object? sender, EventArgs eventArgs) { if (_cameraEnabled != null && !_suppressCameraConfigEvents) { _fileLogger?.WriteInfo($"Camera setting changed: Enabled={_cameraEnabled.Value}."); if (_cameraEnabled.Value) { ResetLocalCaptureSuppression("config-enabled"); } TryApplyCameraState("config-enabled"); } } private void OnCameraMirrorSettingChanged(object? sender, EventArgs eventArgs) { if (_cameraMirror != null && !_suppressCameraConfigEvents) { _fileLogger?.WriteInfo($"Camera setting changed: Mirror={_cameraMirror.Value}."); ApplyMirrorToPreviewMaterial(); } } private void OnCameraDeviceSettingChanged(object? sender, EventArgs eventArgs) { if (_cameraDevice != null && !_suppressCameraConfigEvents) { _fileLogger?.WriteInfo("Camera setting changed: Device='" + _cameraDevice.Value + "'."); ResetLocalCaptureSuppression("config-device"); TryApplyCameraState("config-device"); } } private void OnLocalPreviewSettingChanged(object? sender, EventArgs eventArgs) { if (!_suppressCameraConfigEvents) { NormalizePreviewPositionValue(); UpdatePreviewVisibilityState(); UpdateOverlayLayoutIfNeeded(force: true); } } private void OnCameraTransformSettingChanged(object? sender, EventArgs eventArgs) { if (!_suppressCameraConfigEvents) { ApplyMirrorToPreviewMaterial(); float zoom = _cameraZoom?.Value ?? 1f; float offsetX = _cameraOffsetX?.Value ?? 0f; float offsetY = _cameraOffsetY?.Value ?? 0f; SendTransformUpdate(zoom, offsetX, offsetY); } } private void SendTransformUpdate(float zoom, float offsetX, float offsetY) { ISignalingChannel signalingChannel = _signalingChannel; if (signalingChannel == null || !CanSendTransportTraffic() || string.IsNullOrEmpty("repowebcam-video-v1")) { return; } try { WebcamTransformState transform = new WebcamTransformState(zoom, offsetX, offsetY); signalingChannel.SendTransform("repowebcam-video-v1", transform); } catch (Exception exception) { _fileLogger?.WriteError("Failed to send webcam transform update.", exception); } } private void TryApplyCameraState(string reason) { if (_cameraEnabled != null && _cameraDevice != null) { if (!CanUseCameraRuntime(reason, logIfBlocked: true)) { StopLocalCamera("module-blocked:" + reason); } else if (!_cameraEnabled.Value) { StopLocalCamera("disabled:" + reason); } else { StartOrRestartLocalCamera(reason); } } } internal void NotifyRequiredRuntimeModuleReady() { _requiredRuntimeModuleReady = true; _requiredRuntimeModuleBlockedLogged = false; } private void AttachRequiredRuntimeModules() { try { ((Component)this).gameObject.AddComponent<RuntimeOverlayCoordinator>().Attach(this); } catch (Exception exception) { _requiredRuntimeModuleReady = false; _requiredRuntimeModuleBlockedLogged = false; _fileLogger?.WriteError("Failed to initialize required runtime module. Cameras are blocked in Release build.", exception); } } private bool CanUseCameraRuntime(string reason, bool logIfBlocked = false) { if (_requiredRuntimeModuleReady) { return true; } if (logIfBlocked && !_requiredRuntimeModuleBlockedLogged) { _requiredRuntimeModuleBlockedLogged = true; _fileLogger?.WriteWarning("Required runtime module is unavailable. Cameras are blocked in Release build. reason=" + reason + "."); } return false; } private void EnsureCameraPlaybackHealthy() { if (_cameraEnabled == null || !_cameraEnabled.Value || IsLocalCaptureSuppressedNow() || Time.unscaledTime - _lastSceneChangeAt < 2.5f || Time.unscaledTime < _nextCameraRecoveryAt) { return; } if ((Object)(object)_localCameraTexture == (Object)null) { RegisterLocalCaptureFailure("recovery-null"); if (!_localCaptureSuppressed) { _nextCameraRecoveryAt = Time.unscaledTime + 3f; _fileLogger?.WriteWarning("Local camera texture is missing. Trying to restore it."); _softRecoveryAttempts = 0; StartOrRestartLocalCamera("recovery-null"); } return; } if (_localCameraTexture.isPlaying) { _softRecoveryAttempts = 0; return; } if (_softRecoveryAttempts < 3) { _softRecoveryAttempts++; _nextCameraRecoveryAt = Time.unscaledTime + 3f; _fileLogger?.WriteWarning($"Local camera stopped (isPlaying=False). Soft recovery Play() attempt={_softRecoveryAttempts}/{3}."); try { _localCameraTexture.Play(); UpdatePreviewVisibilityState(); return; } catch (Exception exception) { _fileLogger?.WriteError("Soft local camera recovery failed.", exception); return; } } _softRecoveryAttempts = 0; _nextCameraRecoveryAt = Time.unscaledTime + 3f; _fileLogger?.WriteWarning("Local camera stopped (isPlaying=False). Hard recovery will recreate it."); StartOrRestartLocalCamera("recovery-hard"); } private void StartOrRestartLocalCamera(string reason) { //IL_008f: 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_0099: Expected O, but got Unknown if (_cameraDevice == null) { return; } string[] options = BuildCameraOptions(); string text = ResolveRequestedCamera(_cameraDevice.Value, options); if ((Object)(object)_localCameraTexture != (Object)null && _localCameraTexture.isPlaying && string.Equals(_activeCameraDevice, text, StringComparison.Ordinal)) { return; } StopLocalCamera("restart:" + reason); try { bool flag = string.Equals(text, "Default", StringComparison.Ordinal); _localCameraTexture = (flag ? new WebCamTexture(640, 360, 30) : new WebCamTexture(text, 640, 360, 30)); _activeCameraDevice = text; _localCameraTexture.Play(); BindPreviewToCurrentTexture(); EnsureLocalFrameBroadcastRoutine(); _fileLogger?.WriteInfo($"Starting local camera: Device='{text}', requested={640}x{360}@{30}, mirror={_cameraMirror?.Value ?? false}, reason={reason}."); if (_cameraStartupCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_cameraStartupCoroutine); } _cameraStartupCoroutine = ((MonoBehaviour)this).StartCoroutine(ValidateCameraStartedRoutine(_localCameraTexture, reason)); } catch (Exception exception) { _fileLogger?.WriteError("Failed to start local camera. Device='" + text + "'.", exception); StopLocalCamera("start-failed"); } } private IEnumerator ValidateCameraStartedRoutine(WebCamTexture cameraTexture, string reason) { yield return (object)new WaitForSecondsRealtime(1f); if (cameraTexture != _localCameraTexture) { yield break; } if (!cameraTexture.isPlaying) { _fileLogger?.WriteWarning("Local camera did not enter playing state. Device='" + _activeCameraDevice + "', reason=" + reason + "."); RegisterLocalCaptureFailure(reason); if (!_localCaptureSuppressed) { _nextCameraRecoveryAt = Time.unscaledTime + 3f; } _cameraStartupCoroutine = null; } else { ResetLocalCaptureSuppression("camera-started"); UpdatePreviewScaleFromTexture(cameraTexture); UpdatePreviewVisibilityState(); _fileLogger?.WriteInfo($"Local camera active: Device='{cameraTexture.deviceName}', actual={((Texture)cameraTexture).width}x{((Texture)cameraTexture).height}, didUpdate={cameraTexture.didUpdateThisFrame}."); _cameraStartupCoroutine = null; } } private bool IsLocalCaptureSuppressedNow() { if (!_localCaptureSuppressed) { return false; } if (Time.unscaledTime < _localCaptureSuppressedUntil) { return true; } if (IsLocalCaptureRetryBlockedForCurrentDevice()) { _localCaptureSuppressedUntil = Time.unscaledTime + 3f; if (!_localCaptureRetryBlockLogged) { _localCaptureRetryBlockLogged = true; _fileLogger?.WriteInfo("Local capture suppression remains active for device '" + _localCaptureRetryBlockedDevice + "'. Automatic retries stay blocked until camera settings change."); } return true; } _localCaptureSuppressed = false; _localCaptureSuppressedUntil = 0f; _localCaptureFailureCount = 0; _softRecoveryAttempts = 0; _fileLogger?.WriteInfo("Local capture suppression finished. Auto-recovery resumed."); return false; } private void RegisterLocalCaptureFailure(string reason) { _localCaptureFailureCount++; _softRecoveryAttempts = 0; if (_localCaptureFailureCount >= 3) { _localCaptureSuppressed = true; _localCaptureSuppressedUntil = Time.unscaledTime + 120f; _localCaptureRetryBlockedDevice = ResolveLocalCaptureRetryBlockedDevice(); _localCaptureRetryBlockLogged = false; _nextCameraRecoveryAt = _localCaptureSuppressedUntil; StopLocalCamera("receive-only-suppressed"); _fileLogger?.WriteWarning($"Local camera unavailable ({_localCaptureFailureCount} consecutive failures). " + $"Receive-only mode enabled for {120f:0}s. reason={reason}."); if (!string.IsNullOrWhiteSpace(_localCaptureRetryBlockedDevice)) { _fileLogger?.WriteInfo("Automatic retries blocked for device '" + _localCaptureRetryBlockedDevice + "' until camera settings change."); } } } private void ResetLocalCaptureSuppression(string reason) { bool localCaptureSuppressed = _localCaptureSuppressed; bool flag = _localCaptureFailureCount > 0 || _softRecoveryAttempts > 0 || !string.IsNullOrWhiteSpace(_localCaptureRetryBlockedDevice); _localCaptureSuppressed = false; _localCaptureSuppressedUntil = 0f; _localCaptureFailureCount = 0; _softRecoveryAttempts = 0; _localCaptureRetryBlockedDevice = null; _localCaptureRetryBlockLogged = false; if (localCaptureSuppressed || flag) { _fileLogger?.WriteInfo("Local capture recovery state reset. reason=" + reason + "."); } } private bool IsLocalCaptureRetryBlockedForCurrentDevice() { if (string.IsNullOrWhiteSpace(_localCaptureRetryBlockedDevice)) { return false; } string text = ResolveLocalCaptureRetryBlockedDevice(); if (string.IsNullOrWhiteSpace(text)) { return false; } return string.Equals(text, _localCaptureRetryBlockedDevice, StringComparison.Ordinal); } private string? ResolveLocalCaptureRetryBlockedDevice() { if (_cameraDevice != null) { string text = ResolveRequestedCamera(_cameraDevice.Value, BuildCameraOptions()); if (!string.IsNullOrWhiteSpace(text)) { return text; } } return _activeCameraDevice; } private void EnsureLocalFrameBroadcastRoutine() { if (_localFrameBroadcastCoroutine == null) { _localFrameBroadcastCoroutine = ((MonoBehaviour)this).StartCoroutine(LocalFrameBroadcastRoutine()); } } private IEnumerator LocalFrameBroadcastRoutine() { while (true) { yield return null; if (_cameraEnabled != null && _cameraEnabled.Value && _outboundVideoPublisher != null && !((Object)(object)_headAnchor == (Object)null) && !((Object)(object)_localCameraTexture == (Object)null) && CanSendTransportTraffic()) { float zoom = _cameraZoom?.Value ?? 1f; float offsetX = _cameraOffsetX?.Value ?? 0f; float offsetY = _cameraOffsetY?.Value ?? 0f; int recipientCount = Math.Max(0, SteamLobbyAccessor.GetLobbyMemberIds().Count - 1); _outboundVideoPublisher.TryPublish(_localCameraTexture, _cameraMirror?.Value ?? false, zoom, offsetX, offsetY, Time.unscaledTime, recipientCount); } } } private void ApplyIncomingVideoFrame(string sourcePlayerId, InboundVideoFrame frame) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) RemotePeerState orCreateRemotePeer = GetOrCreateRemotePeer(sourcePlayerId); if (orCreateRemotePeer.LastSequence >= frame.Sequence) { return; } if ((Object)(object)orCreateRemotePeer.Texture == (Object)null) { orCreateRemotePeer.Texture = new Texture2D(2, 2, (TextureFormat)3, false); } if (!ImageConversion.LoadImage(orCreateRemotePeer.Texture, frame.JpegBytes, false)) { return; } orCreateRemotePeer.LastSequence = frame.Sequence; orCreateRemotePeer.LastFrameAt = Time.unscaledTime; _peersMarkedDisconnected.Remove(sourcePlayerId); orCreateRemotePeer.Mirror = frame.Mirror; orCreateRemotePeer.Zoom = frame.Zoom; orCreateRemotePeer.OffsetX = frame.OffsetX; orCreateRemotePeer.OffsetY = frame.OffsetY; orCreateRemotePeer.Material.mainTexture = (Texture)(object)orCreateRemotePeer.Texture; string[] array = new string[3] { "_BaseMap", "_BaseColorMap", "_MainTex" }; foreach (string text in array) { if (orCreateRemotePeer.Material.HasProperty(text)) { orCreateRemotePeer.Material.SetTexture(text, (Texture)(object)orCreateRemotePeer.Texture); } } orCreateRemotePeer.Material.mainTextureScale = new Vector2(1f, 1f); orCreateRemotePeer.Material.mainTextureOffset = new Vector2(0f, 0f); if (orCreateRemotePeer.Material.HasProperty("_EmissionMap")) { orCreateRemotePeer.Material.SetTexture("_EmissionMap", (Texture)(object)orCreateRemotePeer.Texture); } } private RemotePeerState GetOrCreateRemotePeer(string playerId) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: 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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (_remotePeers.TryGetValue(playerId, out RemotePeerState value)) { return value; } GameObject val = new GameObject("RepoWebcamRemote_" + ShortId(playerId)); val.AddComponent<MeshFilter>().mesh = CreateCircularMesh(32); Object.DontDestroyOnLoad((Object)(object)val); Component component = val.GetComponent("Collider"); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } MeshRenderer val2 = val.AddComponent<MeshRenderer>(); Shader val3 = ResolvePreviewShader(); Material val4 = new Material(val3) { color = new Color(1f, 1f, 1f, 1f), name = "RepoWebcamRemoteMaterial_" + ShortId(playerId) }; val4.renderQueue = 4000; val4.SetInt("_ZTest", 8); if (val4.HasProperty("_RendererColor")) { val4.SetColor("_RendererColor", Color.white); } ((Renderer)val2).sharedMaterial = val4; ((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val2).receiveShadows = false; ((Renderer)val2).allowOcclusionWhenDynamic = false; val.SetActive(false); RemotePeerState remotePeerState = new RemotePeerState(playerId, val, val2, val4); _remotePeers[playerId] = remotePeerState; _fileLogger?.WriteInfo("Remote webcam peer created: playerId=" + playerId + ", shader='" + ((Object)val3).name + "'."); return remotePeerState; } private void ApplyIncomingTransform(string sourcePlayerId, WebcamTransformState packet) { //IL_0079: 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) RemotePeerState orCreateRemotePeer = GetOrCreateRemotePeer(sourcePlayerId); orCreateRemotePeer.Zoom = packet.Zoom; orCreateRemotePeer.OffsetX = packet.OffsetX; orCreateRemotePeer.OffsetY = packet.OffsetY; if ((Object)(object)orCreateRemotePeer.Texture != (Object)null && (Object)(object)orCreateRemotePeer.Material != (Object)null) { CalculateUV(orCreateRemotePeer.Mirror, orCreateRemotePeer.Zoom, orCreateRemotePeer.OffsetX, orCreateRemotePeer.OffsetY, (Texture?)(object)orCreateRemotePeer.Texture, out var scale, out var offset); orCreateRemotePeer.Material.mainTextureScale = scale; orCreateRemotePeer.Material.mainTextureOffset = offset; } } private void CalculateUV(bool mirror, float zoom, float offsetX, float offsetY, Texture? texture, out Vector2 scale, out Vector2 offset) { //IL_0096: 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) //IL_00ab: 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) float num = 1f; if ((Object)(object)texture != (Object)null && texture.height > 0) { num = (float)texture.width / (float)texture.height; } float num2 = 1f; float num3 = 1f; if (num > 1f) { num2 = 1f / num; } else if (num < 1f) { num3 = num; } if (zoom <= 0.01f) { zoom = 1f; } float num4 = num2 / zoom; float num5 = num3 / zoom; if (mirror) { num4 = 0f - num4; } float num6 = 0.5f - num4 / 2f; float num7 = 0.5f - num5 / 2f; scale = new Vector2(num4, num5); offset = new Vector2(num6 + offsetX, num7 + offsetY); } private Mesh CreateCircularMesh(int segments) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_002e: 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_0044: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) Mesh val = new Mesh { name = "WebcamCircleMesh" }; Vector3[] array = (Vector3[])(object)new Vector3[segments + 1]; Vector2[] array2 = (Vector2[])(object)new Vector2[segments + 1]; int[] array3 = new int[segments * 3]; array[0] = Vector3.zero; array2[0] = new Vector2(0.5f, 0.5f); float num = MathF.PI * 2f / (float)segments; for (int i = 0; i < segments; i++) { float num2 = (float)i * num; float num3 = Mathf.Cos(num2) * 0.5f; float num4 = Mathf.Sin(num2) * 0.5f; array[i + 1] = new Vector3(num3, num4, 0f); array2[i + 1] = new Vector2(num3 + 0.5f, num4 + 0.5f); int num5 = (i + 1) % segments + 1; int num6 = i * 3; array3[num6] = 0; array3[num6 + 1] = num5; array3[num6 + 2] = i + 1; } val.vertices = array; val.uv = array2; val.triangles = array3; val.RecalculateBounds(); val.RecalculateNormals(); return val; } private void UpdateRemotePeers() { if (_remotePeers.Count == 0) { return; } float unscaledTime = Time.unscaledTime; Camera val = ResolveTargetCamera(); List<string> list = new List<string>(); string text = null; foreach (KeyValuePair<string, RemotePeerState> remotePeer in _remotePeers) { RemotePeerState value = remotePeer.Value; float num = unscaledTime - value.LastFrameAt; if (num > 35f) { list.Add(remotePeer.Key); continue; } if (num > 12f) { value.GameObject.SetActive(false); if (num > 18f && ((Object)(object)value.Anchor == (Object)null || !((Component)value.Anchor).gameObject.activeInHierarchy) && text == null) { text = value.PlayerId; } continue; } if (unscaledTime >= value.NextResolveAt) { RefreshRemotePeerAnchors(value, unscaledTime); value.NextResolveAt = unscaledTime + 1.2f; if ((Object)(object)value.Anchor != (Object)null || (Object)(object)value.AliveAnchor != (Object)null || (Object)(object)value.DeathAnchor != (Object)null) { value.NextAnchorWarningAt = 0f; } } if ((Object)(object)value.Anchor != (Object)null && IsModInternalHierarchy(value.Anchor)) { _fileLogger?.WriteWarning("Remote anchor rejected as internal mod object: playerId=" + value.PlayerId + ", anchor='" + BuildTransformPath(value.Anchor) + "'."); value.Anchor = null; value.AnchorSource = RemoteAnchorSource.None; value.LifecycleState = RemotePlayerLifecycleState.Unknown; value.NextResolveAt = unscaledTime + 1.2f; } if ((Object)(object)value.Anchor != (Object)null && (Object)(object)val != (Object)null) { value.UsingAnchorFallback = false; value.LastAnchorSeenAt = unscaledTime; PositionRemotePeerAtAnchor(value, val); continue; } if ((Object)(object)value.Anchor == (Object)null && (Object)(object)val != (Object)null && CanUseAnchorFallback(value, unscaledTime)) { PositionRemotePeerAtFallback(value, val, unscaledTime); continue; } if ((Object)(object)value.Anchor == (Object)null && unscaledTime >= value.NextAnchorWarningAt) { value.NextAnchorWarningAt = unscaledTime + 8f; _fileLogger?.WriteWarning("Remote anchor not found: playerId=" + value.PlayerId + "."); } value.UsingAnchorFallback = false; value.GameObject.SetActive(false); } if (!string.IsNullOrWhiteSpace(text)) { HandlePeerApparentDisconnect(text); } else { if (list.Count == 0) { return; } foreach (string item in list) { if (_remotePeers.TryGetValue(item, out RemotePeerState value2)) { DisposeRemotePeer(value2); _remotePeers.Remove(item); } } } } private void RefreshRemotePeerAnchors(RemotePeerState peer, float now) { RemoteAnchorSource source; Transform val = ResolveAliveRemoteAnchor(peer.PlayerId, out source); RemoteAnchorSource source2; Transform val2 = ResolveDeadRemoteAnchor(peer.PlayerId, out source2); Transform val3 = TryReuseTrustedRemoteAnchor(peer.AliveAnchor, allowDeathAnchor: false); Transform val4 = TryReuseTrustedRemoteAnchor(peer.DeathAnchor, allowDeathAnchor: true); if ((Object)(object)val == (Object)null && (Object)(object)val3 != (Object)null && peer.LifecycleState == RemotePlayerLifecycleState.Alive && (Object)(object)val2 == (Object)null) { val = val3; source = RemoteAnchorSource.NameFallbackTrusted; } if ((Object)(object)val2 == (Object)null && (Object)(object)val4 != (Object)null) { val2 = val4; source2 = RemoteAnchorSource.NameFallbackTrusted; } if ((Object)(object)val2 == (Object)null && (Object)(object)val != (Object)null) { val2 = TryResolveDeathHeadFromAliveAnchor(val); if ((Object)(object)val2 != (Object)null) { source2 = RemoteAnchorSource.Direct; _fileLogger?.WriteInfo("Death anchor resolved via alive anchor fallback: playerId=" + peer.PlayerId + ", deathAnchor='" + ((Object)val2).name + "'."); } } peer.AliveAnchor = val; peer.DeathAnchor = val2; RemotePlayerLifecycleState remotePlayerLifecycleState = DetermineRemotePeerLifecycleState(peer, val, val2, now); if (remotePlayerLifecycleState != peer.LifecycleState) { _fileLogger?.WriteInfo(string.Format("Remote lifecycle changed: playerId={0}, {1} -> {2}, aliveAnchor={3}, deathAnchor={4}.", peer.PlayerId, peer.LifecycleState, remotePlayerLifecycleState, ((Object)(object)val != (Object)null) ? ((Object)val).name : "<null>", ((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "<null>")); peer.LifecycleState = remotePlayerLifecycleState; } switch (peer.LifecycleState) { case RemotePlayerLifecycleState.Alive: peer.Anchor = val; peer.AnchorSource = source; break; case RemotePlayerLifecycleState.Dead: peer.Anchor = val2; peer.AnchorSource = source2; break; case RemotePlayerLifecycleState.AwaitingAliveAnchor: peer.Anchor = val2; peer.AnchorSource = (((Object)(object)val2 != (Object)null) ? source2 : RemoteAnchorSource.None); break; default: peer.Anchor = null; peer.AnchorSource = RemoteAnchorSource.None; break; } if ((Object)(object)peer.Anchor != (Object)null) { peer.LastAnchorSeenAt = now; } if ((Object)(object)peer.Anchor == (Object)null && now >= peer.NextDiagnosticLogAt) { peer.NextDiagnosticLogAt = now + 30f; LogAnchorResolutionFailureDiagnostics(peer.PlayerId); } } private static RemotePlayerLifecycleState DetermineRemotePeerLifecycleState(RemotePeerState peer, Transform? aliveAnchor, Transform? deathAnchor, float now) { if ((Object)(object)aliveAnchor != (Object)null) { return RemotePlayerLifecycleState.Alive; } if ((Object)(object)deathAnchor != (Object)null) { return RemotePlayerLifecycleState.Dead; } if (peer.LifecycleState == RemotePlayerLifecycleState.Dead || peer.LifecycleState == RemotePlayerLifecycleState.AwaitingAliveAnchor) { return RemotePlayerLifecycleState.AwaitingAliveAnchor; } if (peer.LifecycleState == RemotePlayerLifecycleState.Alive && now - peer.LastAnchorSeenAt < 5f) { return RemotePlayerLifecycleState.Alive; } return RemotePlayerLifecycleState.Unknown; } private Transform? ResolveAliveRemoteAnchor(string remotePlayerId, out RemoteAnchorSource source) { return ResolveRemotePlayerAnchor(remotePlayerId, preferDeathAnchor: false, out source); } private Transform? ResolveDeadRemoteAnchor(string remotePlayerId, out RemoteAnchorSource source) { return ResolveRemotePlayerAnchor(remotePlayerId, preferDeathAnchor: true, out source); } private Transform? ResolveRemotePlayerAnchor(string remotePlayerId, bool preferDeathAnchor, out RemoteAnchorSource source) { source = RemoteAnchorSource.None; if (string.IsNullOrWhiteSpace(remotePlayerId)) { return null; } ulong? numericPlayerId = TryParseNumericPlayerId(remotePlayerId); Transform val = null; MonoBehaviour val2 = null; int num = int.MinValue; bool flag = false; MonoBehaviour[] array = Object.FindObjectsOfType<MonoBehaviour>(); foreach (MonoBehaviour val3 in array) { if ((Object)(object)val3 == (Object)null || !((Component)val3).gameObject.activeInHierarchy || !IsLikelyRemoteAnchorCarrier(val3) || (TryReadLocalFlag(val3, out var isLocal) && isLocal) || !TryMatchPlayerId(val3, remotePlayerId, numericPlayerId)) { continue; } Transform root = ((Component)val3).transform.root ?? ((Component)val3).transform; Transform val4 = (Transform)(preferDeathAnchor ? ((object)ResolveDeathHeadAnchor(root)) : ((object)ResolveAliveHeadAnchor(root))); if ((Object)(object)val4 == (Object)null || IsRejectedRemoteAnchorCandidate(val4, preferDeathAnchor) || IsLocalHierarchy(val4)) { continue; } bool flag2 = (Object)(object)val4 != (Object)(object)((Component)val3).transform; bool flag3 = IsDeathAnchorCandidate(val4, val3); if (preferDeathAnchor == flag3) { int num2 = ScoreRemoteAnchorCandidate(val4, val3, flag2); if (num2 > num) { num = num2; val = val4; val2 = val3; flag = flag2; } } } if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null) { source = RemoteAnchorSource.Direct; _fileLogger?.WriteInfo($"Remote anchor resolved: playerId={remotePlayerId}, anchor='{((Object)val).name}', isHead={flag}, root='{((Object)((Component)val2).gameObject).name}', score={num}."); return val; } return null; } private static bool IsDeathAnchorCandidate(Transform? anchor, MonoBehaviour behaviour) { string name = ((object)behaviour).GetType().Name; if ((Object)(object)anchor == (Object)null) { return name.IndexOf("PlayerDeathHead", StringComparison.OrdinalIgnoreCase) >= 0; } if (((Object)anchor).name.Equals("code_head_top", StringComparison.OrdinalIgnoreCase)) { return false; } string text = BuildTransformPath(anchor); if (text.IndexOf("Player Death Head", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("PlayerDeathHead", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("corpse", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("ragdoll", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("dead", StringComparison.OrdinalIgnoreCase) < 0) { return name.IndexOf("PlayerDeathHead", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static Transform? TryResolveDeathHeadFromAliveAnchor(Transform aliveAnchor) { Transform[] componentsInChildren = ((Component)(aliveAnchor.root ?? aliveAnchor)).GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { string name = ((Object)val).name; if (name.IndexOf("Player Death Head", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("PlayerDeathHead", StringComparison.OrdinalIgnoreCase) >= 0) { return val; } } return null; } private void LogAnchorResolutionFailureDiagnostics(string remotePlayerId) { ulong? numericPlayerId = TryParseNumericPlayerId(remotePlayerId); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; List<string> list = new List<string>(); MonoBehaviour[] array = Object.FindObjectsOfType<MonoBehaviour>(); foreach (MonoBehaviour val in array) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy || !IsLikelyRemoteAnchorCarrier(val)) { continue; } num++; string name = ((object)val).GetType().Name; string name2 = ((Object)((Component)val).gameObject).name; string text = (((Object)(object)((Component)val).transform.root != (Object)null) ? ((Object)((Component)val).transform.root).name : name2); if (TryReadLocalFlag(val, out var isLocal) && isLocal) { num4++; continue; } bool flag = TryMatchPlayerId(val, remotePlayerId, numericPlayerId); if (flag) { num2++; Transform root = ((Component)val).transform.root ?? ((Component)val).transform; Transform val2 = ResolveAliveHeadAnchor(root); Transform val3 = ResolveDeathHeadAnchor(root); bool flag2 = (Object)(object)val2 == (Object)null || IsRejectedRemoteAnchorCandidate(val2); bool flag3 = (Object)(object)val3 == (Object)null || IsRejectedRemoteAnchorCandidate(val3, skipParkingCheck: true); bool flag4 = ((Object)(object)val2 != (Object)null && IsLocalHierarchy(val2)) || ((Object)(object)val3 != (Object)null && IsLocalHierarchy(val3)); list.Insert(0, string.Format("MATCH: [{0}@'{1}' root='{2}' alive={3} rejectedAlive={4} death={5} rejectedDeath={6} local={7}]", name, name2, text, ((Object)(object)val2 != (Object)null) ? ((Object)val2).name : "null", flag2, ((Object)(object)val3 != (Object)null) ? ((Object)val3).name : "null", flag3, flag4)); if (flag2 && flag3) { num3++; } } else if (text.IndexOf("PlayerAvatar", StringComparison.OrdinalIgnoreCase) >= 0) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; object obj = ((object)val).GetType().GetProperty("photonView", bindingAttr)?.GetValue(val, null); if (obj != null) { object obj2 = obj.GetType().GetProperty("Owner", bindingAttr)?.GetValue(obj, null); if (obj2 != null) { object obj3 = obj2.GetType().GetProperty("UserId", bindingAttr)?.GetValue(obj2, null); object obj4 = obj2.GetType().GetProperty("ActorNumber", bindingAttr)?.GetValue(obj2, null); object obj5 = obj2.GetType().GetProperty("NickName", bindingAttr)?.GetValue(obj2, null); list.Add($"[FAILED: {name}@'{name2}' root='{text}' pv.Owner(User={obj3}, Actor={obj4}, Nick={obj5})]"); } } } else if (list.Count < 5) { list.Add($"[{name}@'{name2}' root='{text}' idMatch={flag}]"); } } IReadOnlyList<ulong> lobbyMemberIds = SteamLobbyAccessor.GetLobbyMemberIds(); _fileLogger?.WriteWarning($"Anchor resolution diagnostics: playerId={remotePlayerId}, carriers={num}, local={num4}, " + $"idMatched={num2}, rejected={num3}. " + "LobbyMembers: " + string.Join(", ", lobbyMemberIds) + ". " + ((list.Count > 0) ? ("Candidates: " + string.Join(", ", list)) : "No candidates found.")); } private static ulong? TryParseNumericPlayerId(string playerId) { if (!ulong.TryParse(playerId, out var result)) { return null; } return result; } private static bool TryMatchPlayerId(MonoBehaviour behaviour, string playerId, ulong? numericPlayerId) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; Type type = ((object)behaviour).GetType(); PropertyInfo[] properties = type.GetProperties(bindingAttr); foreach (PropertyInfo propertyInfo in properties) { if (!propertyInfo.CanRead) { continue; } try { if (propertyInfo.GetIndexParameters().Length != 0 || !IsMemberMatchingPlayerId(propertyInfo.GetValue(behaviour, null), playerId, numericPlayerId)) { continue; } return true; } catch { } } FieldInfo[] fields = type.GetFields(bindingAttr); foreach (FieldInfo fieldInfo in fields) { try { if (IsMemberMatchingPlayerId(fieldInfo.GetValue(behaviour), playerId, numericPlayerId)) { return true; } } catch { } } object obj3 = type.GetProperty("photonView", bindingAttr)?.GetValue(behaviour, null); if (obj3 != null && TryMatchPhotonOwnerId(obj3, playerId, numericPlayerId)) { return true; } try { MonoBehaviour[] componentsInParent = ((Component)behaviour).GetComponentsInParent<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInParent) { if ((Object)(object)val == (Object)null || val == behaviour) { continue; } Type type2 = ((object)val).GetType(); if (type2.Name != "PhotonView") { object obj4 = type2.GetProperty("photonView", bindingAttr)?.GetValue(val, null); if (obj4 != null && TryMatchPhotonOwnerId(obj4, playerId, numericPlayerId)) { return true; } } else if (TryMatchPhotonOwnerId(val, playerId, numericPlayerId)) { return true; } } } catch { } return false; } private Transform? TryReuseTrustedRemoteAnchor(Transform? anchor, bool allowDeathAnchor) { if ((Object)(object)anchor == (Object)null) { return null; } if (IsRejectedRemoteAnchorCandidate(anchor, allowDeathAnchor)) { return null; } if (IsLocalHierarchy(anchor)) { return null; } if (!allowDeathAnchor && IsTransientRemoteAnchor(anchor)) { return null; } return anchor; } private static bool TryMatchPhotonOwnerId(object photonView, string playerId, ulong? numericPlayerId) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; object obj = photonView.GetType().GetProperty("Owner", bindingAttr)?.GetValue(photonView, null); if (obj == null) { return false; } Type type = obj.GetType(); PropertyInfo property = type.GetProperty("UserId", bindingAttr); if (property != null && IsMemberMatchingPlayerId(property.GetValue(obj, null), playerId, numericPlayerId)) { return true; } PropertyInfo property2 = type.GetProperty("ActorNumber", bindingAttr); if (property2 != null && IsMemberMatchingPlayerId(property2.GetValue(obj, null), playerId, numericPlayerId)) { return true; } PropertyInfo property3 = type.GetProperty("NickName", bindingAttr); if (property3 != null && IsMemberMatchingPlayerId(property3.GetValue(obj, null), playerId, numericPlayerId)) { return true; } PropertyInfo property4 = type.GetProperty("CustomProperties", bindingAttr); if (property4 != null && property4.GetValue(obj, null) is IDictionary dictionary) { foreach (object value in dictionary.Values) { if (IsMemberMatchingPlayerId(value, playerId, numericPlayerId)) { return true; } } } return false; } private static bool IsMemberMatchingPlayerId(object? value, string playerId, ulong? numericPlayerId) { if (value == null) { return false; } if (!(value is string a)) { if (!(value is ulong num)) { if (!(value is long num2)) { if (!(value is uint num3)) { if (value is int num4) { if (numericPlayerId.HasValue) { return num4 == (int)numericPlayerId.Value; } return false; } return false; } if (numericPlayerId.HasValue) { return num3 == (uint)numericPlayerId.Value; } return false; } if (numericPlayerId.HasValue) { return num2 == (long)numericPlayerId.Value; } return false; } if (numericPlayerId.HasValue) { return num == numericPlayerId.Value; } return false; } return string.Equals(a, playerId, StringComparison.Ordinal); } private void DisposeRemotePeers(string reason) { foreach (RemotePeerState value in _remotePeers.Values) { DisposeRemotePeer(value); } _remotePeers.Clear(); if (!string.IsNullOrWhiteSpace(reason)) { _fileLogger?.WriteInfo("Remote peers cleared. reason=" + reason + "."); } } private static void DisposeRemotePeer(RemotePeerState peer) { if ((Object)(object)peer.GameObject != (Object)null) { Object.Destroy((Object)(object)peer.GameObject); } if ((Object)(object)peer.Material != (Object)null) { Object.Destroy((Object)(object)peer.Material); } if ((Object)(object)peer.Texture != (Object)null) { Object.Destroy((Object)(object)peer.Texture); peer.Texture = null; } } private void HandlePeerApparentDisconnect(string playerId) { if (_peersMarkedDisconnected.Add(playerId)) { _inboundVideoSources.Remove(playerId); _fileLogger?.WriteInfo("Peer " + playerId + " is not sending frames and is missing from the scene. Assuming disconnect. Resetting inbound video state and sending a probe for faster reconnection."); ResetAllRemoteWebcamsAndReprobe("peer-disconnect:" + playerId); } } private static string ShortId(string value) { if (string.IsNullOrEmpty(value)) { return "none"; } if (value.Length > 8) { return value.Substring(Math.Max(0, value.Length - 8), 8); } return value; } private string ResolveRequestedCamera(string requestedDevice, IReadOnlyList<string> options) { if (options.Count == 0) { return "Default"; } if (string.IsNullOrWhiteSpace(requestedDevice) || string.Equals(requestedDevice, "Default", StringComparison.Ordinal)) { return "Default"; } if (options.Contains<string>(requestedDevice, StringComparer.Ordinal)) { return requestedDevice; } _fileLogger?.WriteWarning("Requested camera '" + requestedDevice + "' is unavailable. 'Default' will be used instead."); SetCameraDeviceValueWithoutEvents("Default"); return "Default"; } private void SetCameraDeviceValueWithoutEvents(string value) { if (_cameraDevice == null) { return; } _suppressCameraConfigEvents = true; try { _cameraDevice.Value = value; } finally { _suppressCameraConfigEvents = false; } } private void StopLocalCamera(string reason) { if (_cameraStartupCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_cameraStartupCoroutine); _cameraStartupCoroutine = null; } if ((Object)(object)_localCameraTexture == (Object)null) { return; } try { if (_localCameraTexture.isPlaying) { _localCameraTexture.Stop(); } } catch (Exception exception) { _fileLogger?.WriteError("Failed to stop local camera.", exception); } finally { Object.Destroy((Object)(object)_localCameraTexture); _localCameraTexture = null; _activeCameraDevice = null; _outboundVideoPublisher?.ResetSchedule(); if ((Object)(object)_overlayRawImage != (Object)null) { _overlayRawImage.texture = null; } SetPreviewVisible(visible: false); SetOverlayVisible(visible: false); _fileLogger?.WriteInfo("Local camera stopped. reason=" + reason + "."); } } private void BindPreviewToCurrentTexture() { if (!((Object)(object)_localCameraTexture == (Object)null)) { EnsurePreviewObject(); EnsureOverlayUi(); if ((Object)(object)_previewMaterial != (Object)null) { _previewMaterial.mainTexture = (Texture)(object)_localCameraTexture; } if ((Object)(object)_overlayRawImage != (Object)null) { _overlayRawImage.texture = (Texture)(object)_localCameraTexture; } ApplyMirrorToPreviewMaterial(); UpdatePreviewVisibilityState(); } } private void EnsurePreviewObject() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //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_008c: 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_009c: Expected O, but got Unknown if (!((Object)(object)_previewObject != (Object)null)) { _previewObject = new GameObject("RepoWebcamPreview"); _previewObject.AddComponent<MeshFilter>().mesh = CreateCircularMesh(32); Object.DontDestroyOnLoad((Object)(object)_previewObject); Component component = _previewObject.GetComponent("Collider"); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } _previewRenderer = _previewObject.AddComponent<MeshRenderer>(); Shader val = ResolvePreviewShader(); _previewMaterial = new Material(val) { name = "RepoWebcamPreviewMaterial", color = Color.white }; ((Renderer)_previewRenderer).sharedMaterial = _previewMaterial; ((Renderer)_previewRenderer).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)_previewRenderer).receiveShadows = false; ((Renderer)_previewRenderer).allowOcclusionWhenDynamic = false; ApplyRenderLayer(_previewObject, ResolvePreferredRenderLayer(_headAnchor ?? _localPlayerRoot), isRemote: false, null); SetPreviewVisible(visible: false); _fileLogger?.WriteInfo("Preview object 'RepoWebcamPreview' created for webcam rendering."); } } private Shader ResolvePreviewShader() { if ((Object)(object)_cachedPreviewShader != (Object)null) { return _cachedPreviewShader; } LogSceneShaders(); string[] array = new string[4] { "Sprites/Default", "Unlit/Texture", "Universal Render Pipeline/Unlit", "Particles/Standard Unlit" }; foreach (string text in array) { Shader val = Shader.Find(text); if ((Object)(object)val != (Object)null) { _fileLogger?.WriteInfo("ResolvePreviewShader: found '" + text + "'."); _cachedPreviewShader = val; return val; } } Shader val2 = TryBorrowShaderFromScene(requireUnlit: true); if ((Object)(object)val2 != (Object)null) { _fileLogger?.WriteInfo("ResolvePreviewShader: using scene unlit shader '" + ((Object)val2).name + "'."); _cachedPreviewShader = val2; return val2; } Shader val3 = TryBorrowShaderFromScene(); if ((Object)(object)val3 != (Object)null) { _fileLogger?.WriteInfo("ResolvePreviewShader: using scene shader '" + ((Object)val3).name + "' (lit)."); _cachedPreviewShader = val3; return val3; } Shader val4 = Shader.Find("Standard"); if ((Object)(object)val4 != (Object)null) { _fileLogger?.WriteInfo("ResolvePreviewShader: using Standard (lit, last resort)."); _cachedPreviewShader = val4; return val4; } _fileLogger?.WriteWarning("ResolvePreviewShader: shader not found, fallback=Sprites/Default."); _cachedPreviewShader = Shader.Find("Sprites/Default"); return _cachedPreviewShader; } private void LogSceneShaders() { if (_sceneShadersLogged) { return; } _sceneShadersLogged = true; HashSet<string> hashSet = new HashSet<string>(); MeshRenderer[] array = Object.FindObjectsOfType<MeshRenderer>(); foreach (MeshRenderer val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Renderer)val).sharedMaterial == (Object)null) && !((Object)(object)((Renderer)val).sharedMaterial.shader == (Object)null)) { hashSet.Add(((Object)((Renderer)val).sharedMaterial.shader).name); } } _fileLogger?.WriteInfo(string.Format("Scene shaders ({0}): {1}.", hashSet.Count, string.Join(" | ", hashSet))); } private Shader? TryBorrowShaderFromScene(bool requireUnlit = false) { MeshRenderer[] array = Object.FindObjectsOfType<MeshRenderer>(); foreach (MeshRenderer val in array) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy || IsModInternalHierarchy(((Component)val).transform) || IsUiOrMenuHierarchy(((Component)val).transform)) { continue; } Material sharedMaterial = ((Renderer)val).sharedMaterial; if (!((Object)(object)sharedMaterial == (Object)null) && !((Object)(object)sharedMaterial.shader == (Object)null)) { string name = ((Object)sharedMaterial.shader).name; if (name.IndexOf("Sprite", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Particle", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Hidden", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Error", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("GUI", StringComparison.OrdinalIgnoreCase) < 0 && (!requireUnlit || name.IndexOf("Unlit", StringComparison.OrdinalIgnoreCase) >= 0)) { return sharedMaterial.shader; } } } return null; } private void SetPreviewVisible(bool visible) { if (!((Object)(object)_previewObject == (Object)null)) { _previewObject.SetActive(visible); } } private void UpdatePreviewVisibilityState() { bool overlayVisible = (Object)(object)_localCameraTexture != (Object)null && _localCameraTexture.isPlaying && (_showLocalPreview?.Value ?? true); SetPreviewVisible(visible: false); SetOverlayVisible(overlayVisible); } private void DestroyPreviewObject() { if ((Object)(object)_previewObject != (Object)null) { Object.Destroy((Object)(object)_previewObject); _previewObject = null; } if ((Object)(object)_previewMaterial != (Object)null) { Object.Destroy((Object)(object)_previewMaterial); _previewMaterial = null; } _previewRenderer = null; _localPlayerRoot = null; _headAnchor = null; _anchorLogged = false; _previewRenderLayer = -1; } private void EnsureOverlayUi() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_overlayCanvasObject != (Object)null)) { _overlayCanvasObject = new GameObject("RepoWebcamOverlayCanvas", new Type[3] { typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Object.DontDestroyOnLoad((Object)(object)_overlayCanvasObject); Canvas component = _overlayCanvasObject.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.sortingOrder = 32000; CanvasScaler component2 = _overlayCanvasObject.GetComponent<CanvasScaler>(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(1920f, 1080f); component2.matchWidthOrHeight = 1f; GameObject val = new GameObject("RepoWebcamOverlayPanel", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); val.transform.SetParent(_overlayCanvasObject.transform, false); _overlayRectTransform = val.GetComponent<RectTransform>(); _overlayRectTransform.anchorMin = new Vector2(1f, 0f); _overlayRectTransform.anchorMax = new Vector2(1f, 0f); _overlayRectTransform.pivot = new Vector2(1f, 0f); _overlayRectTransform.anchoredPosition = new Vector2(-20f, 20f); Image component3 = val.GetComponent<Image>(); ((Graphic)component3).color = OverlayPanelColor; ((Graphic)component3).raycastTarget = false; component3.sprite = GetOrCreateCircleSprite(); val.AddComponent<Mask>().showMaskGraphic = true; GameObject val2 = new GameObject("RepoWebcamOverlayImage", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage) }); val2.transform.SetParent(val.transform, false); RectTransform component4 = val2.GetComponent<RectTransform>(); component4.anchorMin = Vector2.zero; component4.anchorMax = Vector2.one; component4.offsetMin = new Vector2(6f, 6f); component4.offsetMax = new Vector2(-6f, -6f); _overlayRawImage = val2.GetComponent<RawImage>(); ((Graphic)_overlayRawImage).color = Color.white; ((Graphic)_overlayRawImage).raycastTarget = false; WebCamTexture? localCameraTexture = _localCameraTexture; int textureWidth = Mathf.Max(1, (localCameraTexture != null) ? ((Texture)localCameraTexture).width : 16); WebCamTexture? localCameraTexture2 = _localCameraTexture; UpdateOverlayRectTransform(textureWidth, Mathf.Max(1, (localCameraTexture2 != null) ? ((Texture)localCameraTexture2).height : 9)); SetOverlayVisible(visible: false); _fileLogger?.WriteInfo("Screen overlay UI created for local preview."); } } private void SetOverlayVisible(bool visible) { if (!((Object)(object)_overlayCanvasObject == (Object)null)) { _overlayCanvasObject.SetActive(visible); } } private Sprite GetOrCreateCircleSprite() { //IL_001f: 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_0030: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_circleSprite != (Object)null) { return _circleSprite; } int num = 128; Texture2D val = new Texture2D(num, num, (TextureFormat)4, false) { name = "CircleMaskTempTex" }; Color32[] array = (Color32[])(object)new Color32[num * num]; float num2 = (float)num / 2f; float num3 = (float)num / 2f - 1f; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { float num4 = (float)j + 0.5f - num2; float num5 = (float)i + 0.5f - num2; float num6 = Mathf.Sqrt(num4 * num4 + num5 * num5); float num7 = Mathf.Clamp01(num3 - num6 + 1f); array[i * num + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num7 * 255f)); } } val.SetPixels32(array); val.Apply(false, true); _circleSprite = Sprite.Create(val, new Rect(0f, 0f, (float)num, (float)num), new Vector2(0.5f, 0.5f)); ((Object)_circleSprite).name = "RepoWebcamCircleMask"; return _circleSprite; } private void DestroyOverlayUi() { if ((Object)(object)_overlayCanvasObject != (Object)null) { Object.Destroy((Object)(object)_overlayCanvasObject); _overlayCanvasObject = null; } if ((Object)(object)_circleSprite != (Object)null) { if ((Object)(object)_circleSprite.texture != (Object)null) { Object.Destroy((Object)(object)_circleSprite.texture); } Object.Destroy((Object)(object)_circleSprite); _circleSprite = null; } _overlayRectTransform = null; _overlayRawImage = null; _overlayScreenWidth = 0; _overlayScreenHeight = 0; } private void UpdateOverlayRectTransform(int textureWidth, int textureHeight) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0467: Unknown result type (might be due to invalid IL or missing references) //IL_0481: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_04b5: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0390: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03fd: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_0431: Unknown result type (might be due to invalid IL or missing references) //IL_044b: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_overlayRectTransform == (Object)null)) { float num = Mathf.Min(_overlaySize?.Value ?? 200f, (float)Screen.width * 0.42f); float num2 = num; _overlayRectTransform.sizeDelta = new Vector2(num, num2); switch (_localPreviewPosition?.Value ?? "TopCenter") { case "TopLeft": _overlayRectTransform.anchorMin = new Vector2(0f, 1f); _overlayRectTransform.anchorMax = new Vector2(0f, 1f); _overlayRectTransform.pivot = new Vector2(0f, 1f); _overlayRectTransform.anchoredPosition = new Vector2(20f, -20f); break; case "TopCenter": _overlayRectTransform.anchorMin = new Vector2(0.5f, 1f); _overlayRectTransform.anchorMax = new Vector2(0.5f, 1f); _overlayRectTransform.pivot = new Vector2(0.5f, 1f); _overlayRectTransform.anchoredPosition = new Vector2(0f, -20f); break; case "TopRight": _overlayRectTransform.anchorMin = new Vector2(1f, 1f); _overlayRectTransform.anchorMax = new Vector2(1f, 1f); _overlayRectTransform.pivot = new Vector2(1f, 1f); _overlayRectTransform.anchoredPosition = new Vector2(-20f, -20f); break; case "MiddleLeft": _overlayRectTransform.anchorMin = new Vector2(0f, 0.5f); _overlayRectTransform.anchorMax = new Vector2(0f, 0.5f); _overlayRectTransform.pivot = new Vector2(0f, 0.5f); _overlayRectTransform.anchoredPosition = new Vector2(20f, 0f); break; case "MiddleRight": _overlayRectTransform.anchorMin = new Vector2(1f, 0.5f); _overlayRectTransform.anchorMax = new Vector2(1f, 0.5f); _overlayRectTransform.pivot = new Vector2(1f, 0.5f); _overlayRectTransform.anchoredPosition = new Vector2(-20f, 0f); break; case "BottomLeft": _overlayRectTransform.anchorMin = new Vector2(0f, 0f); _overlayRectTransform.anchorMax = new Vector2(0f, 0f); _overlayRectTransform.pivot = new Vector2(0f, 0f); _overlayRectTransform.anchoredPosition = new Vector2(20f, 20f); break; case "BottomRight": _overlayRectTransform.anchorMin = new Vector2(1f, 0f); _overlayRectTransform.anchorMax = new Vector2(1f, 0f); _overlayRectTransform.pivot = new Vector2(1f, 0f); _overlayRectTransform.anchoredPosition = new Vector2(-20f, 20f); break; default: _overlayRectTransform.anchorMin = new Vector2(0f, 1f); _overlayRectTransform.anchorMax = new Vector2(0f, 1f); _overlayRectTransform.pivot = new Vector2(0f, 1f); _overlayRectTransform.anchoredPosition = new Vector2(20f, -20f); break; } _overlayScreenWidth = Screen.width; _overlayScreenHeight = Screen.height; } } private void UpdateOverlayLayoutIfNeeded(bool force = false) { ConfigEntry<bool>? showLocalPreview = _showLocalPreview; if ((showLocalPreview == null || showLocalPreview.Value) && !((Object)(object)_overlayRectTransform == (Object)null) && !((Object)(object)_localCameraTexture == (Object)null) && (force || _overlayScreenWidth != Screen.width || _overlayScreenHeight != Screen.height)) { UpdateOverlayRectTransform(Mathf.Max(1, ((Texture)_localCameraTexture).width), Mathf.Max(1, ((Texture)_localCameraTexture).height)); } } private void ApplyMirrorToPreviewMaterial() { //IL_0080: 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_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00c4: Unknown result type (might be due to invalid IL or missing references) bool mirror = _cameraMirror?.Value ?? false; float zoom = _cameraZoom?.Value ?? 1f; float offsetX = _cameraOffsetX?.Value ?? 0f; float offsetY = _cameraOffsetY?.Value ?? 0f; CalculateUV(mirror, zoom, offsetX, offsetY, (Texture?)(object)_localCameraTexture, out var scale, out var offset); if ((Object)(object)_previewMaterial != (Object)null) { _previewMaterial.mainTextureScale = scale; _previewMaterial.mainTextureOffset = offset; } if ((Object)(object)_overlayRawImage != (Object)null) { _overlayRawImage.uvRect = new Rect(offset.x, offset.y, scale.x, scale.y); } } private void UpdatePreviewScaleFromTexture(WebCamTexture texture) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) int textureWidth = Mathf.Max(1, ((Texture)texture).width); int textureHeight = Mathf.Max(1, ((Texture)texture).height); if ((Object)(object)_previewObject != (Object)null) { _previewObject.transform.localScale = new Vector3(0.22f, 0.22f, 1f); } UpdateOverlayRectTransform(textureWidth, textureHeight); } private void UpdatePreviewTransform() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_previewObject == (Object)null || !_previewObject.activeSelf) { return; } ResolveLocalAnchorIfNeeded(); Transform val = _headAnchor ?? _localPlayerRoot; Camera val2 = ResolveTargetCamera(); if ((Object)(object)val2 == (Object)null) { return; } ApplyRenderLayer(_previewObject, ResolvePreferredRenderLayer(val), isRemote: false, null); Transform transform = _previewObject.transform; if ((Object)(object)val != (Object)null) { transform.position = val.position + Vector3.up * 0.35f; } else { transform.position = ((Component)val2).transform.position + ((Component)val2).transform.forward * 0.9f + Vector3.up * -0.08f; if (!_fallbackLogged) { _fallbackLogged = true; _fileLogger?.WriteWarning("Player anchor not found. Preview is temporarily attached in front of the camera (fallback)."); LogAnchorCandidates(); } } Vector3 val3 = ((Component)val2).transform.position - transform.position; if (!(((Vector3)(ref val3)).sqrMagnitude < 0.001f)) { transform.rotation = Quaternion.LookRotation(((Vector3)(ref val3)).normalized, Vector3.up); } } private Camera? ResolveTargetCamera() { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) Camera val = null; float num = float.MinValue; Transform val2 = _headAnchor ?? _localPlayerRoot; Camera[] array = Object.FindObjectsOfType<Camera>(); foreach (Camera val3 in array) { if (!((Object)(object)val3 == (Object)null) && ((Behaviour)val3).isActiveAndEnabled && !((Object)(object)val3.targetTexture != (Object)null)) { float num2 = 0f; if ((Object)(object)val3 == (Object)(object)Camera.main) { num2 += 1000f; } if (string.Equals(((Component)val3).tag, "MainCamera", StringComparison.Ordinal)) { num2 += 300f; } num2 += Mathf.Clamp(val3.depth, -100f, 100f); if (((uint)val3.cullingMask & (true ? 1u : 0u)) != 0) { num2 += 80f; } if (((uint)val3.cullingMask & 0x20u) != 0 && (val3.cullingMask & 1) == 0) { num2 -= 300f; } if (IsUiOrMenuHierarchy(((Component)val3).transfor