Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of repo webcam v1.2.3
RepoWebcamMod.dll
Decompiled a month 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.Concurrent; 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.3.0")] [assembly: AssemblyInformationalVersion("1.2.3+681187f3c5adf990256897e470616953dc061428")] [assembly: AssemblyProduct("RepoWebcamMod")] [assembly: AssemblyTitle("RepoWebcamMod")] [assembly: AssemblyVersion("1.2.3.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.3"; 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.3")] 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 float PreviewDiagnosticsIntervalSeconds = 15f; private const float TransportDiagnosticsIntervalSeconds = 15f; 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) { Transform val = null; Transform val2 = null; RemoteAnchorSource source = RemoteAnchorSource.None; RemoteAnchorSource source2 = RemoteAnchorSource.None; Transform val3 = TryReuseTrustedRemoteAnchor(peer.AliveAnchor, allowDeathAnchor: false); Transform val4 = TryReuseTrustedRemoteAnchor(peer.DeathAnchor, allowDeathAnchor: true); switch (peer.LifecycleState) { case RemotePlayerLifecycleState.Alive: val = val3; if ((Object)(object)val != (Object)null) { source = RemoteAnchorSource.NameFallbackTrusted; break; } val = ResolveAliveRemoteAnchor(peer.PlayerId, out source); if ((Object)(object)val == (Object)null) { val2 = val4; if ((Object)(object)val2 != (Object)null) { source2 = RemoteAnchorSource.NameFallbackTrusted; } else { val2 = ResolveDeadRemoteAnchor(peer.PlayerId, out source2); } } break; case RemotePlayerLifecycleState.Dead: case RemotePlayerLifecycleState.AwaitingAliveAnchor: val2 = val4; if ((Object)(object)val2 != (Object)null) { source2 = RemoteAnchorSource.NameFallbackTrusted; break; } val2 = ResolveDeadRemoteAnchor(peer.PlayerId, out source2); if ((Object)(object)val2 == (Object)null) { val = val3; if ((Object)(object)val != (Object)null) { source = RemoteAnchorSource.NameFallbackTrusted; } else { val = ResolveAliveRemoteAnchor(peer.PlayerId, out source); } } break; default: val = val3; if ((Object)(object)val != (Object)null) { source = RemoteAnchorSource.NameFallbackTrusted; } else { val = ResolveAliveRemoteAnchor(peer.PlayerId, out source); } if ((Object)(object)val == (Object)null) { val2 = val4; if ((Object)(object)val2 != (Object)null) { source2 = RemoteAnchorSource.NameFallbackTrusted; } else { val2 = ResolveDeadRemoteAnchor(peer.PlayerId, out source2); } } break; } if ((Object)(object)val2 == (Object)null && (Object)(object)val != (Object)null && peer.LifecycleState != RemotePlayerLifecycleState.Alive) { val2 = TryResolveDeathHeadFromAliveAnchor(val); if ((Object)(object)val2 != (Object)null) { source2 = RemoteAnchorSource.Direct; } } 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; 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 isHeadAnchor = (Object)(object)val4 != (Object)(object)((Component)val3).transform; bool flag = IsDeathAnchorCandidate(val4, val3); if (preferDeathAnchor == flag) { int num2 = ScoreRemoteAnchorCandidate(val4, val3, isHeadAnchor); if (num2 > num) { num = num2; val = val4; val2 = val3; } } } if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null) { source = RemoteAnchorSource.Direct; 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_011