Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of SprayPaintableVeeper v1.2.39
SprayPaintableVeeper/SprayPaintableVeeper.dll
Decompiled 5 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.Json; using HarmonyLib; using Il2CppFishNet; using Il2CppFishNet.Connection; using Il2CppFishNet.Object; using Il2CppFishNet.Transporting; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Graffiti; using Il2CppScheduleOne.Interaction; using Il2CppScheduleOne.Networking; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.Persistence.Datas; using Il2CppScheduleOne.Vehicles; using Il2CppSteamworks; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Text; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using SprayPaintableVeeper; using SprayPaintableVeeper.Config; using SprayPaintableVeeper.Patches; using SprayPaintableVeeper.Runtime; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering.Universal; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(SprayPaintableVeeperMod), "Spray Paintable Veeper", "1.2.36", "brobb", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: AssemblyMetadata("NexusModID", "0")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("SprayPaintableVeeper")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.29.0")] [assembly: AssemblyInformationalVersion("1.2.29")] [assembly: AssemblyProduct("SprayPaintableVeeper")] [assembly: AssemblyTitle("SprayPaintableVeeper")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.29.0")] [module: UnverifiableCode] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SprayPaintableVeeper { public sealed class SprayPaintableVeeperMod : MelonMod { private const KeyCode RebuildKey = 291; private VeeperSurfaceManager _manager; internal static VeeperSurfaceManager ActiveManager { get; private set; } public override void OnInitializeMelon() { SprayPaintConfig.Initialize(); GraffitiCleanerPatch.Initialize(((MelonBase)this).LoggerInstance); SpraySurfaceSyncPatch.Initialize(((MelonBase)this).LoggerInstance); _manager = new VeeperSurfaceManager(((MelonBase)this).LoggerInstance); ActiveManager = _manager; ((MelonBase)this).LoggerInstance.Msg("[SprayPaintableVeeper] Initialized."); } public override void OnUpdate() { _manager?.Update(); if (Input.GetKeyDown((KeyCode)291)) { _manager?.RebuildNow(); } } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { _manager?.SetWorldReady(sceneName == "Main"); } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { if (sceneName == "Main") { _manager?.SetWorldReady(ready: false); } } public override void OnPreferencesSaved() { _manager?.RebuildNow(); } public override void OnPreferencesSaved(string filePath) { _manager?.RebuildNow(); } } } namespace SprayPaintableVeeper.Runtime { public sealed class VeeperSurfaceManager { private sealed class VeeperSurfaceSet { public readonly LandVehicle Vehicle; public readonly List<SurfaceSegment> Segments = new List<SurfaceSegment>(); public VeeperSurfaceSet(LandVehicle vehicle) { Vehicle = vehicle; } public bool HasSurface(SpraySurface surface) { for (int i = 0; i < Segments.Count; i++) { if ((Object)(object)Segments[i]?.Surface == (Object)(object)surface) { return true; } } return false; } } private readonly struct PanelSettings { public readonly string PanelId; public readonly Vector3 Outward; public readonly float Width; public readonly float Height; public readonly Vector3 Offset; public readonly Vector3 Euler; public PanelSettings(string panelId, Vector3 outward, float width, float height, Vector3 offset, Vector3 euler) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) PanelId = panelId; Outward = outward; Width = width; Height = height; Offset = offset; Euler = euler; } } private sealed class SurfaceSegment { public GameObject Root; public SpraySurface Surface; public SpraySurfaceInteraction Interaction; public InteractableObject Interactable; public bool IsRight; public bool IsUpper; public string PanelId; } private sealed class GraffitiFileData { public List<SpraySurfaceSaveData> SpraySurfaces { get; set; } } private sealed class SpraySurfaceSaveData { public string GUID { get; set; } public string VehicleGUID { get; set; } public string PanelId { get; set; } public bool HasDrawingBeenFinalized { get; set; } public List<SprayStrokeSaveData> Strokes { get; set; } } private sealed class SprayStrokeSaveData { public UShort2Data Start { get; set; } public UShort2Data End { get; set; } public int Color { get; set; } } private sealed class UShort2Data { public ushort X { get; set; } public ushort Y { get; set; } } private const string SurfaceNamePrefix = "VeeperSpraySurface"; private const string GraffitiFileName = "Graffiti.json"; private const string VeeperGraffitiFileName = "VeeperGraffiti.json"; private const float PanelWidth = 5f; private const float PanelHeight = 1.7f; private const float PanelVerticalOffset = 0.75f; private const float PanelPitchDegrees = 0f; private const float PanelBendHeight = 0.45f; private const float PanelBendAngle = 0f; private const float PanelForwardOffsetRight = -5f; private const float PanelForwardOffsetLeft = 5f; private const float PanelSideOffsetRight = 0.9f; private const float PanelSideOffsetLeft = -0.9f; private const float FrontPanelWidth = 2f; private const float FrontPanelHeight = 1f; private const float FrontOffsetX = 2f; private const float FrontOffsetY = 0.8f; private const float FrontOffsetZ = 2.4f; private const float FrontAngleX = -80f; private const float FrontAngleY = 0f; private const float FrontAngleZ = 0f; private const float BackPanelWidth = 2f; private const float BackPanelHeight = 2f; private const float BackOffsetX = -2f; private const float BackOffsetY = 0.7f; private const float BackOffsetZ = -2.4f; private const float BackAngleX = 0f; private const float BackAngleY = 0f; private const float BackAngleZ = 0f; private const float RoofPanelWidth = 2f; private const float RoofPanelHeight = 4.5f; private const float RoofOffsetX = -2f; private const float RoofOffsetY = 1.55f; private const float RoofOffsetZ = -0.2f; private const float RoofAngleX = 0f; private const float RoofAngleY = 0f; private const float RoofAngleZ = 0f; private const float ScanIntervalSeconds = 5f; private const float ClientSyncIntervalSeconds = 5f; private const float SurfaceSyncThrottleSeconds = 0.25f; private const string SteamMsgPrefix = "VeeperSpray"; private const string SurfaceDataType = "SurfaceData"; private const string SyncRequestType = "SyncRequest"; private static readonly JsonSerializerOptions SyncJsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; private readonly Instance _logger; private readonly Dictionary<string, VeeperSurfaceSet> _vehicleSurfaces = new Dictionary<string, VeeperSurfaceSet>(); private readonly HashSet<string> _restoredGuids = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<int> _knownClientIds = new HashSet<int>(); private readonly Dictionary<string, SpraySurface> _surfaceLookup = new Dictionary<string, SpraySurface>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, SpraySurfaceSaveData> _pendingSync = new Dictionary<string, SpraySurfaceSaveData>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, float> _lastSurfaceSyncTimes = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _suppressSurfaceSync = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private float _nextScanTime; private float _nextClientSyncTime; private bool _worldReady; private bool _rebuildRequested; private bool _warnedTemplate; private bool _warnedGraffitiMissing; private bool _warnedInteractionCheck; private bool _interactionActive; private bool _serverHandlersRegistered; private bool _clientHandlersRegistered; private Action<NetworkConnection, List<byte>> _serverSyncHandler; private Action<List<byte>> _clientSyncHandler; private bool _broadcastReady; private bool _broadcastFailed; private bool _broadcastWarned; private Callback<LobbyChatMsg_t> _lobbyChatCallback; private CSteamID _lobbySteamId; private bool _steamSyncReady; private bool _savedGraffitiApplied; private bool _initialClientSyncDone; private bool _globalSurfaceScanDone; private GameObject _templateRoot; private SpraySurface _templateSurface; private SpraySurfaceInteraction _templateInteraction; private float _templateCameraLocalZ = -1.5f; private Quaternion _templateCameraLocalRotation = Quaternion.identity; private string _activeSavePath; private Dictionary<string, SpraySurfaceSaveData> _graffitiCache; private bool _graffitiCacheLoaded; private Dictionary<string, SpraySurfaceSaveData> _veeperGraffitiCache; private bool _veeperGraffitiCacheLoaded; private bool _extraFileRegistered; private bool _saveEventRegistered; private UnityAction _saveCompleteAction; private static bool IsServerAuthoritative { get { if (!InstanceFinder.IsServer) { return InstanceFinder.IsOffline; } return true; } } public VeeperSurfaceManager(Instance logger) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) _logger = logger; } public void SetWorldReady(bool ready) { _worldReady = ready; if (!ready) { UnregisterBroadcastHandlers(); UnregisterSaveEventHandler(); _vehicleSurfaces.Clear(); _restoredGuids.Clear(); _knownClientIds.Clear(); _surfaceLookup.Clear(); _pendingSync.Clear(); _lastSurfaceSyncTimes.Clear(); _suppressSurfaceSync.Clear(); _graffitiCache = null; _graffitiCacheLoaded = false; _veeperGraffitiCache = null; _veeperGraffitiCacheLoaded = false; _extraFileRegistered = false; _activeSavePath = null; _templateRoot = null; _templateSurface = null; _templateInteraction = null; _warnedTemplate = false; _warnedGraffitiMissing = false; _serverHandlersRegistered = false; _clientHandlersRegistered = false; _serverSyncHandler = null; _clientSyncHandler = null; _broadcastReady = false; _broadcastFailed = false; _broadcastWarned = false; _savedGraffitiApplied = false; _initialClientSyncDone = false; _globalSurfaceScanDone = false; } else { _rebuildRequested = true; _nextScanTime = 0f; } } public void RequestRebuild() { _rebuildRequested = true; _nextScanTime = 0f; _restoredGuids.Clear(); } public void RebuildNow() { _rebuildRequested = true; _nextScanTime = 0f; if (_worldReady && EnsureTemplateReady()) { RebuildAll(); } } public void Update() { if (!_worldReady) { return; } if (!_broadcastReady && !_broadcastFailed) { EnsureBroadcastHandlers(); } if (!EnsureTemplateReady()) { return; } UpdateInteractionState(); if (!_saveEventRegistered && IsServerAuthoritative) { RegisterSaveEventHandler(); } if (!(Time.unscaledTime < _nextScanTime)) { _nextScanTime = Time.unscaledTime + 5f; if (!IsServerAuthoritative && !_globalSurfaceScanDone) { _globalSurfaceScanDone = true; RegisterGlobalSurfaces(); } ScanVehicles(); SyncNewClients(); } } private bool EnsureTemplateReady() { //IL_008c: 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_00a7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_templateRoot != (Object)null) { RegisterExtraFileWithSaveSystem(); return true; } if (!NetworkSingleton<GraffitiManager>.InstanceExists) { return false; } RegisterExtraFileWithSaveSystem(); SpraySurfaceInteraction[] array = Il2CppArrayBase<SpraySurfaceInteraction>.op_Implicit(Object.FindObjectsOfType<SpraySurfaceInteraction>()); foreach (SpraySurfaceInteraction val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.SpraySurface == (Object)null)) { _templateInteraction = val; _templateSurface = val.SpraySurface; _templateRoot = ((Component)val).gameObject; if ((Object)(object)val.CameraPosition != (Object)null) { _templateCameraLocalZ = val.CameraPosition.localPosition.z; _templateCameraLocalRotation = val.CameraPosition.localRotation; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Template surface found: " + ((Object)_templateRoot).name); } return true; } } if (!_warnedTemplate) { _warnedTemplate = true; _logger.Warning("[SprayPaintableVeeper] Could not find a spray surface template yet."); } return false; } private void RegisterExtraFileWithSaveSystem() { if (_extraFileRegistered || !NetworkSingleton<GraffitiManager>.InstanceExists) { return; } try { GraffitiManager instance = NetworkSingleton<GraffitiManager>.Instance; if ((Object)(object)instance == (Object)null) { return; } List<string> val = instance.LocalExtraFiles; if (val == null) { val = (instance.LocalExtraFiles = new List<string>()); } bool flag = false; for (int i = 0; i < val.Count; i++) { if (string.Equals(val[i], "VeeperGraffiti.json", StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } if (!flag) { val.Add("VeeperGraffiti.json"); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Registered VeeperGraffiti.json with GraffitiManager.LocalExtraFiles"); } } _extraFileRegistered = true; } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to register extra file: " + ex.Message); } } private void ScanVehicles() { if (!NetworkSingleton<VehicleManager>.InstanceExists) { return; } VehicleManager instance = NetworkSingleton<VehicleManager>.Instance; HashSet<LandVehicle> hashSet = new HashSet<LandVehicle>(); List<LandVehicle> playerOwnedVehicles = instance.PlayerOwnedVehicles; if (playerOwnedVehicles != null) { for (int i = 0; i < playerOwnedVehicles.Count; i++) { if ((Object)(object)playerOwnedVehicles[i] != (Object)null) { hashSet.Add(playerOwnedVehicles[i]); } } } List<LandVehicle> allVehicles = instance.AllVehicles; if (allVehicles != null) { for (int j = 0; j < allVehicles.Count; j++) { if ((Object)(object)allVehicles[j] != (Object)null) { hashSet.Add(allVehicles[j]); } } } if (hashSet.Count == 0) { return; } HashSet<string> hashSet2 = new HashSet<string>(); foreach (LandVehicle item in hashSet) { if ((Object)(object)item == (Object)null || !IsVeeperVehicle(item)) { continue; } string vehicleKey = GetVehicleKey(item); if (string.IsNullOrEmpty(vehicleKey)) { continue; } hashSet2.Add(vehicleKey); if (_rebuildRequested && _vehicleSurfaces.TryGetValue(vehicleKey, out var value)) { DestroySurfaceSet(value); _vehicleSurfaces.Remove(vehicleKey); } if (_vehicleSurfaces.ContainsKey(vehicleKey)) { continue; } VeeperSurfaceSet veeperSurfaceSet = CreateSurfaceSet(item); if (veeperSurfaceSet == null) { veeperSurfaceSet = RegisterVehicleSurfaces(item); } if (veeperSurfaceSet != null) { _vehicleSurfaces[vehicleKey] = veeperSurfaceSet; if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] Registered Veeper surfaces for {vehicleKey} (isHost={IsServerAuthoritative})."); } if (IsServerAuthoritative) { TryApplySavedGraffitiForVehicle(veeperSurfaceSet); } } } if (hashSet.Count > 0) { CleanupMissingVehicles(hashSet2); } _rebuildRequested = false; if (IsServerAuthoritative && !_savedGraffitiApplied) { _savedGraffitiApplied = true; TryApplySavedGraffitiToAll(); } } private void SyncNewClients() { if (!IsServerAuthoritative || !_broadcastReady || Time.unscaledTime < _nextClientSyncTime) { return; } _nextClientSyncTime = Time.unscaledTime + 5f; if ((Object)(object)InstanceFinder.ServerManager == (Object)null) { return; } Dictionary<int, NetworkConnection> clients = InstanceFinder.ServerManager.Clients; if (clients == null || clients.Count == 0) { return; } PruneDisconnectedClients(clients); Enumerator<int, NetworkConnection> enumerator = clients.GetEnumerator(); while (enumerator.MoveNext()) { KeyValuePair<int, NetworkConnection> current = enumerator.Current; int key = current.Key; NetworkConnection value = current.Value; if (!(value == (NetworkConnection)null) && !_knownClientIds.Contains(key)) { _knownClientIds.Add(key); RefreshClient(value); } } } private void EnsureSteamSync() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (_steamSyncReady) { return; } try { Lobby instance = Singleton<Lobby>.Instance; if ((Object)(object)instance == (Object)null) { return; } _lobbySteamId = instance.LobbySteamID; if (!(_lobbySteamId == CSteamID.Nil)) { if (_lobbyChatCallback == null) { _lobbyChatCallback = Callback<LobbyChatMsg_t>.Create(DispatchDelegate<LobbyChatMsg_t>.op_Implicit((Action<LobbyChatMsg_t>)OnLobbyChatMessage)); } _steamSyncReady = true; if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Steam lobby sync initialized"); } if (!IsServerAuthoritative && !_initialClientSyncDone) { _initialClientSyncDone = true; RequestSyncFromHost(); } } } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] Failed to initialize Steam sync: " + ex.Message); } } } private void EnsureBroadcastHandlers() { EnsureSteamSync(); _broadcastReady = _steamSyncReady; } private void UnregisterBroadcastHandlers() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) _steamSyncReady = false; _lobbySteamId = CSteamID.Nil; _broadcastReady = false; } private void OnLobbyChatMessage(LobbyChatMsg_t msg) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) if (!_steamSyncReady || !_worldReady) { return; } try { Il2CppStructArray<byte> val = Il2CppStructArray<byte>.op_Implicit(new byte[32768]); CSteamID val2 = default(CSteamID); EChatEntryType val3 = default(EChatEntryType); int lobbyChatEntry = SteamMatchmaking.GetLobbyChatEntry(_lobbySteamId, (int)msg.m_iChatID, ref val2, val, ((Il2CppArrayBase<byte>)(object)val).Length, ref val3); CSteamID steamID = SteamUser.GetSteamID(); if (val2.m_SteamID == steamID.m_SteamID) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] OnLobbyChatMessage: Ignoring own message from {val2.m_SteamID}"); } return; } string text = Encoding.UTF8.GetString(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)val), 0, lobbyChatEntry).TrimEnd('\0'); int num = text.IndexOf('|'); if (num < 0 || !text.StartsWith("VeeperSpray")) { return; } int num2 = text.IndexOf('|', num + 1); if (num2 < 0) { return; } string text2 = text.Substring(num + 1, num2 - num - 1); int num3 = text.IndexOf('|', num2 + 1); if (num3 < 0) { return; } string value = text.Substring(num2 + 1, num3 - num2 - 1); string text3 = text.Substring(num3 + 1); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] OnLobbyChatMessage: type={text2}, guid={value}, from={val2.m_SteamID}, dataLen={text3.Length}"); } if (text2 == "SurfaceData" && !string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(text3)) { if (SprayPaintConfig.DebugLogging.Value) { string text4 = ((text3.Length > 80) ? (text3.Substring(0, 80) + "...") : text3); _logger.Msg("[SprayPaintableVeeper] OnLobbyChatMessage: Received JSON: " + text4); } if (!TryDeserializePayload(text3, out var payload)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] OnLobbyChatMessage: Failed to deserialize"); } return; } if (!ApplySurfacePayload(payload, out var surface)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] OnLobbyChatMessage: Failed to apply payload for GUID=" + payload?.GUID); } return; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] OnLobbyChatMessage: Applied surface data for GUID=" + payload?.GUID); } if (IsServerAuthoritative) { BroadcastSurfaceStateViaSteam(surface, val2); } } else if (text2 == "SyncRequest" && IsServerAuthoritative) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] OnLobbyChatMessage: Received sync request from {val2.m_SteamID}"); } SendAllPaintedSurfacesViaSteam(); } } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] OnLobbyChatMessage error: " + ex.Message); } } } private void OnSurfaceSyncFromClient(NetworkConnection connection, List<byte> payload) { if (!_broadcastReady || !IsServerAuthoritative) { return; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] OnSurfaceSyncFromClient: Received data from client {((connection != null) ? new int?(connection.ClientId) : null)}, payload count={payload?.Count ?? (-1)}"); } string text = DecodePayload(payload); if (SprayPaintConfig.DebugLogging.Value) { string text2 = (string.IsNullOrEmpty(text) ? "(null/empty)" : ((text.Length > 100) ? (text.Substring(0, 100) + "...") : text)); _logger.Msg("[SprayPaintableVeeper] OnSurfaceSyncFromClient: Decoded JSON: " + text2); } if (!TryDeserializePayload(text, out var payload2)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] OnSurfaceSyncFromClient: Failed to deserialize payload"); } return; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] OnSurfaceSyncFromClient: Applying data for GUID=" + payload2?.GUID); } if (!ApplySurfacePayload(payload2, out var surface)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] OnSurfaceSyncFromClient: Failed to apply payload (surface not found?)"); } } else { BroadcastSurfaceState(surface, connection); } } private void OnSurfaceSyncFromServer(List<byte> payload) { if (_broadcastReady && !IsServerAuthoritative) { string json = DecodePayload(payload); if (TryDeserializePayload(json, out var payload2)) { ApplySurfacePayload(payload2, out var _); } } } private void PruneDisconnectedClients(Dictionary<int, NetworkConnection> clients) { if (_knownClientIds.Count == 0) { return; } List<int> list = null; foreach (int knownClientId in _knownClientIds) { if (!clients.ContainsKey(knownClientId)) { if (list == null) { list = new List<int>(); } list.Add(knownClientId); } } if (list != null) { for (int i = 0; i < list.Count; i++) { _knownClientIds.Remove(list[i]); } } } private void RefreshClient(NetworkConnection connection) { if (!_steamSyncReady) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] RefreshClient: Steam sync not ready yet"); } return; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] Syncing all panels via Steam for new client {((connection != null) ? connection.ClientId : (-1))}."); } int num = 0; foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { VeeperSurfaceSet value = vehicleSurface.Value; if (value == null) { continue; } for (int i = 0; i < value.Segments.Count; i++) { SurfaceSegment surfaceSegment = value.Segments[i]; if (surfaceSegment != null && !((Object)(object)surfaceSegment.Surface == (Object)null) && HasAnyPaint(surfaceSegment.Surface)) { BroadcastSurfaceStateViaSteam(surfaceSegment.Surface); num++; } } } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] RefreshClient: Synced {num} painted surfaces via Steam"); } } private void SendSurfaceStateToClient(SpraySurface surface, NetworkConnection connection) { if (_broadcastReady && !((Object)(object)surface == (Object)null) && !(connection == (NetworkConnection)null) && !((Object)(object)InstanceFinder.ServerManager == (Object)null) && TrySerializeSurface(surface, out var json)) { List<byte> val = EncodePayload(json); if (val != null) { InstanceFinder.ServerManager.Broadcast<List<byte>>(connection, val, true, (Channel)0); } } } private void SendSurfaceStateToServer(SpraySurface surface) { //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (!_steamSyncReady) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] SendSurfaceStateToServer: Steam sync not ready"); } } else { if ((Object)(object)surface == (Object)null) { return; } if (!TrySerializeSurface(surface, out var json)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] SendSurfaceStateToServer: Failed to serialize surface"); } return; } string surfaceGuid = GetSurfaceGuid(surface); if (string.IsNullOrEmpty(surfaceGuid)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] SendSurfaceStateToServer: No GUID for surface"); } return; } string text = $"{"VeeperSpray"}|{"SurfaceData"}|{surfaceGuid}|{json}"; Il2CppStructArray<byte> bytes = Encoding.UTF8.GetBytes(text); SteamMatchmaking.SendLobbyChatMsg(_lobbySteamId, bytes, ((Il2CppArrayBase<byte>)(object)bytes).Length); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] SendSurfaceStateToServer: Sent {json.Length} bytes for GUID={surfaceGuid}"); } } } private void BroadcastSurfaceStateViaSteam(SpraySurface surface, CSteamID? excludeSender = null) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (!_steamSyncReady || (Object)(object)surface == (Object)null || !TrySerializeSurface(surface, out var json)) { return; } string surfaceGuid = GetSurfaceGuid(surface); if (!string.IsNullOrEmpty(surfaceGuid)) { string text = $"{"VeeperSpray"}|{"SurfaceData"}|{surfaceGuid}|{json}"; Il2CppStructArray<byte> bytes = Encoding.UTF8.GetBytes(text); SteamMatchmaking.SendLobbyChatMsg(_lobbySteamId, bytes, ((Il2CppArrayBase<byte>)(object)bytes).Length); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] BroadcastSurfaceStateViaSteam: Sent {json.Length} bytes for GUID={surfaceGuid}"); } } } private void BroadcastSurfaceState(SpraySurface surface, NetworkConnection excludedConnection) { BroadcastSurfaceStateViaSteam(surface); } private void RequestSyncFromHost() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (_steamSyncReady && !IsServerAuthoritative) { string text = "VeeperSpray|SyncRequest|_|_"; Il2CppStructArray<byte> bytes = Encoding.UTF8.GetBytes(text); SteamMatchmaking.SendLobbyChatMsg(_lobbySteamId, bytes, ((Il2CppArrayBase<byte>)(object)bytes).Length); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] RequestSyncFromHost: Sent sync request to host"); } } } private void SendAllPaintedSurfacesViaSteam() { if (!_steamSyncReady || !IsServerAuthoritative) { return; } int num = 0; foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { VeeperSurfaceSet value = vehicleSurface.Value; if (value == null) { continue; } for (int i = 0; i < value.Segments.Count; i++) { SurfaceSegment surfaceSegment = value.Segments[i]; if (surfaceSegment != null && !((Object)(object)surfaceSegment.Surface == (Object)null) && HasAnyPaint(surfaceSegment.Surface)) { BroadcastSurfaceStateViaSteam(surfaceSegment.Surface); num++; } } } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] SendAllPaintedSurfacesViaSteam: Sent {num} painted surfaces"); } } private bool TrySerializeSurface(SpraySurface surface, out string json) { json = null; if (!TryBuildSurfacePayload(surface, out var payload)) { return false; } try { json = JsonSerializer.Serialize(payload); return true; } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] Failed to serialize surface: " + ex.Message); } return false; } } private static List<byte> EncodePayload(string payload) { if (string.IsNullOrEmpty(payload)) { return null; } byte[] bytes = Encoding.UTF8.GetBytes(payload); List<byte> val = new List<byte>(bytes.Length); for (int i = 0; i < bytes.Length; i++) { val.Add(bytes[i]); } return val; } private static string DecodePayload(List<byte> payload) { if (payload == null) { return null; } int count = payload.Count; if (count <= 0) { return null; } byte[] array = new byte[count]; for (int i = 0; i < count; i++) { array[i] = payload[i]; } return Encoding.UTF8.GetString(array); } private bool HasAnyPaint(SpraySurface surface) { if ((Object)(object)surface == (Object)null) { return false; } try { SpraySurfaceData saveData = surface.GetSaveData(); if (saveData == null || saveData.Strokes == null) { return false; } return saveData.Strokes.Count > 0; } catch { return false; } } private bool TryBuildSurfacePayload(SpraySurface surface, out SpraySurfaceSaveData payload) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected I4, but got Unknown payload = null; if ((Object)(object)surface == (Object)null) { return false; } SpraySurfaceData saveData; try { saveData = surface.GetSaveData(); } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] Failed to read surface data: " + ex.Message); } return false; } if (saveData == null) { return false; } string text = saveData.GUID; if (string.IsNullOrEmpty(text)) { text = GetSurfaceGuid(surface); } if (string.IsNullOrEmpty(text)) { return false; } payload = new SpraySurfaceSaveData { GUID = text, HasDrawingBeenFinalized = saveData.HasDrawingBeenFinalized, Strokes = new List<SprayStrokeSaveData>() }; List<SprayStroke> strokes = saveData.Strokes; if (strokes != null) { for (int i = 0; i < strokes.Count; i++) { SprayStroke val = strokes[i]; if (val != null) { UShort2 start = val.Start; UShort2 end = val.End; payload.Strokes.Add(new SprayStrokeSaveData { Start = new UShort2Data { X = start.X, Y = start.Y }, End = new UShort2Data { X = end.X, Y = end.Y }, Color = (int)val.Color }); } } } return true; } private bool TryDeserializePayload(string json, out SpraySurfaceSaveData payload) { payload = null; if (string.IsNullOrWhiteSpace(json)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] TryDeserializePayload: JSON is null/empty"); } return false; } try { payload = JsonSerializer.Deserialize<SpraySurfaceSaveData>(json, SyncJsonOptions); } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] TryDeserializePayload exception: " + ex.GetType().Name + ": " + ex.Message); } return false; } if (payload == null) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] TryDeserializePayload: payload is null after deserialize"); } } else if (string.IsNullOrEmpty(payload.GUID) && SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] TryDeserializePayload: payload.GUID is null/empty"); } if (payload != null) { return !string.IsNullOrEmpty(payload.GUID); } return false; } private bool ApplySurfacePayload(SpraySurfaceSaveData payload, out SpraySurface surface) { surface = null; if (payload == null || string.IsNullOrEmpty(payload.GUID)) { return false; } if (!_surfaceLookup.TryGetValue(payload.GUID, out surface) || (Object)(object)surface == (Object)null) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] ApplySurfacePayload: GUID={payload.GUID} not found in lookup. Known GUIDs ({_surfaceLookup.Count}): {string.Join(", ", _surfaceLookup.Keys)}"); } _pendingSync[payload.GUID] = payload; return false; } _suppressSurfaceSync.Add(payload.GUID); try { ApplySurfacePayload(surface, payload); } finally { _suppressSurfaceSync.Remove(payload.GUID); } return true; } private void ApplySurfacePayload(SpraySurface surface, SpraySurfaceSaveData payload) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: 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_00ad: 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_00b9: Expected O, but got Unknown if ((Object)(object)surface == (Object)null || payload == null) { return; } try { surface.EnsureDrawingExists(); } catch { } Il2CppReferenceArray<SprayStroke> val = null; if (payload.Strokes != null && payload.Strokes.Count > 0) { val = new Il2CppReferenceArray<SprayStroke>((long)payload.Strokes.Count); UShort2 val2 = default(UShort2); UShort2 val3 = default(UShort2); for (int i = 0; i < payload.Strokes.Count; i++) { SprayStrokeSaveData sprayStrokeSaveData = payload.Strokes[i]; ((UShort2)(ref val2))..ctor(sprayStrokeSaveData.Start.X, sprayStrokeSaveData.Start.Y); ((UShort2)(ref val3))..ctor(sprayStrokeSaveData.End.X, sprayStrokeSaveData.End.Y); ESprayColor val4 = (ESprayColor)(byte)Mathf.Clamp(sprayStrokeSaveData.Color, 0, 255); ((Il2CppArrayBase<SprayStroke>)(object)val)[i] = new SprayStroke(val2, val3, val4); } } try { if (val == null) { val = new Il2CppReferenceArray<SprayStroke>(0L); surface.ClearDrawing(); } surface.Set((NetworkConnection)null, val, payload.HasDrawingBeenFinalized); surface.ResizeProjector(); } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to apply surface sync: " + ex.Message); } if (IsServerAuthoritative) { MarkGraffitiDirty(); } } private void MarkGraffitiDirty() { if (!NetworkSingleton<GraffitiManager>.InstanceExists) { return; } try { GraffitiManager instance = NetworkSingleton<GraffitiManager>.Instance; if ((Object)(object)instance != (Object)null) { instance.HasChanged = true; } } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Warning("[SprayPaintableVeeper] Failed to mark graffiti dirty: " + ex.Message); } } } private void RegisterSaveEventHandler() { if (_saveEventRegistered) { return; } try { if (!Singleton<SaveManager>.InstanceExists) { return; } SaveManager instance = Singleton<SaveManager>.Instance; if (!((Object)(object)instance == (Object)null) && instance.onSaveComplete != null) { Action action = OnGameSaveComplete; _saveCompleteAction = UnityAction.op_Implicit(action); instance.onSaveComplete.AddListener(_saveCompleteAction); _saveEventRegistered = true; if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Registered save event handler"); } } } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to register save event: " + ex.Message); } } private void UnregisterSaveEventHandler() { if (!_saveEventRegistered || (Delegate)(object)_saveCompleteAction == (Delegate)null) { return; } try { if (Singleton<SaveManager>.InstanceExists) { SaveManager instance = Singleton<SaveManager>.Instance; if ((Object)(object)instance != (Object)null && instance.onSaveComplete != null) { instance.onSaveComplete.RemoveListener(_saveCompleteAction); } } } catch { } _saveEventRegistered = false; _saveCompleteAction = null; } private void OnGameSaveComplete() { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Game save complete - saving Veeper graffiti"); } SaveVeeperGraffiti(); } private void SaveVeeperGraffiti() { if (!IsServerAuthoritative) { return; } string activeSavePath = GetActiveSavePath(); if (string.IsNullOrEmpty(activeSavePath)) { return; } try { List<SpraySurfaceSaveData> list = new List<SpraySurfaceSaveData>(); foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { VeeperSurfaceSet value = vehicleSurface.Value; if ((Object)(object)value?.Vehicle == (Object)null) { continue; } string persistentVehicleGuid = GetPersistentVehicleGuid(value.Vehicle); if (string.IsNullOrEmpty(persistentVehicleGuid)) { continue; } foreach (SurfaceSegment segment in value.Segments) { if (!((Object)(object)segment?.Surface == (Object)null) && !string.IsNullOrEmpty(segment.PanelId) && TryBuildSurfacePayload(segment.Surface, out var payload) && payload.Strokes != null && payload.Strokes.Count > 0) { payload.VehicleGUID = persistentVehicleGuid; payload.PanelId = segment.PanelId; list.Add(payload); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] SaveVeeperGraffiti: Saving key='{persistentVehicleGuid}:{segment.PanelId}' with {payload.Strokes.Count} strokes"); } } } } string contents = JsonSerializer.Serialize(new GraffitiFileData { SpraySurfaces = list }, new JsonSerializerOptions { WriteIndented = true }); string text = Path.Combine(activeSavePath, "VeeperGraffiti.json"); File.WriteAllText(text, contents); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] Saved {list.Count} Veeper surfaces to {text}"); } } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to save Veeper graffiti: " + ex.Message); } } private Dictionary<string, SpraySurfaceSaveData> LoadVeeperGraffitiCache(string savePath) { string path = Path.Combine(savePath, "VeeperGraffiti.json"); if (!File.Exists(path)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] No Veeper graffiti save file found."); } return null; } try { string text = File.ReadAllText(path); if (string.IsNullOrWhiteSpace(text)) { return null; } JsonSerializerOptions options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; GraffitiFileData graffitiFileData = JsonSerializer.Deserialize<GraffitiFileData>(text, options); if (graffitiFileData?.SpraySurfaces == null || graffitiFileData.SpraySurfaces.Count == 0) { return null; } Dictionary<string, SpraySurfaceSaveData> dictionary = new Dictionary<string, SpraySurfaceSaveData>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < graffitiFileData.SpraySurfaces.Count; i++) { SpraySurfaceSaveData spraySurfaceSaveData = graffitiFileData.SpraySurfaces[i]; if (spraySurfaceSaveData == null) { continue; } if (!string.IsNullOrEmpty(spraySurfaceSaveData.VehicleGUID) && !string.IsNullOrEmpty(spraySurfaceSaveData.PanelId)) { string key = spraySurfaceSaveData.VehicleGUID + ":" + spraySurfaceSaveData.PanelId; if (!dictionary.ContainsKey(key)) { dictionary.Add(key, spraySurfaceSaveData); } } else if (!string.IsNullOrEmpty(spraySurfaceSaveData.GUID) && !dictionary.ContainsKey(spraySurfaceSaveData.GUID)) { dictionary.Add(spraySurfaceSaveData.GUID, spraySurfaceSaveData); } } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] Loaded {dictionary.Count} Veeper surfaces from save file."); foreach (KeyValuePair<string, SpraySurfaceSaveData> item in dictionary) { _logger.Msg($"[SprayPaintableVeeper] Cache key: '{item.Key}' (VehicleGUID={item.Value.VehicleGUID}, PanelId={item.Value.PanelId}, Strokes={item.Value.Strokes?.Count ?? 0})"); } } return dictionary; } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to load Veeper graffiti save: " + ex.Message); return null; } } private void RebuildAll() { foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { DestroySurfaceSet(vehicleSurface.Value); } _vehicleSurfaces.Clear(); _restoredGuids.Clear(); ScanVehicles(); SyncNewClients(); } private void CleanupMissingVehicles(HashSet<string> seen) { List<string> list = null; foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { if (seen.Contains(vehicleSurface.Key)) { continue; } if ((Object)(object)vehicleSurface.Value?.Vehicle == (Object)null || ((Il2CppObjectBase)vehicleSurface.Value.Vehicle).WasCollected || (Object)(object)((Component)vehicleSurface.Value.Vehicle).gameObject == (Object)null) { if (list == null) { list = new List<string>(); } list.Add(vehicleSurface.Key); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] CleanupMissingVehicles: Removing destroyed vehicle " + vehicleSurface.Key); } } else if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] CleanupMissingVehicles: Keeping " + vehicleSurface.Key + " (vehicle still exists but not in scan list)"); } } if (list == null) { return; } for (int i = 0; i < list.Count; i++) { string key = list[i]; if (_vehicleSurfaces.TryGetValue(key, out var value)) { DestroySurfaceSet(value); _vehicleSurfaces.Remove(key); } } } private bool IsVeeperVehicle(LandVehicle vehicle) { string obj = ((vehicle.VehicleName != null) ? vehicle.VehicleName.ToLowerInvariant() : string.Empty); string text = ((vehicle.VehicleCode != null) ? vehicle.VehicleCode.ToLowerInvariant() : string.Empty); string text2 = (((Object)(object)((Component)vehicle).gameObject != (Object)null) ? ((Object)((Component)vehicle).gameObject).name.ToLowerInvariant() : string.Empty); if (!obj.Contains("veep") && !text.Contains("veep")) { return text2.Contains("veep"); } return true; } private static string GetVehicleKey(LandVehicle vehicle) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicle == (Object)null) { return null; } try { Guid gUID = vehicle.GUID; string text = ((object)(Guid)(ref gUID)).ToString(); if (string.IsNullOrEmpty(text) || text == "00000000-0000-0000-0000-000000000000") { return null; } return text; } catch { return null; } } private VeeperSurfaceSet CreateSurfaceSet(LandVehicle vehicle) { if ((Object)(object)_templateRoot == (Object)null || (Object)(object)vehicle == (Object)null) { return null; } GetPanelSettings(out var panelWidth, out var panelHeight, out var forwardOffsetRight, out var forwardOffsetLeft, out var verticalOffset, out var sideOffsetRight, out var sideOffsetLeft, out var pitch, out var _, out var _); VeeperSurfaceSet veeperSurfaceSet = new VeeperSurfaceSet(vehicle); CreateSideSegments(veeperSurfaceSet, vehicle, isRight: true, panelWidth, panelHeight, forwardOffsetRight, verticalOffset, sideOffsetRight, pitch); CreateSideSegments(veeperSurfaceSet, vehicle, isRight: false, panelWidth, panelHeight, forwardOffsetLeft, verticalOffset, sideOffsetLeft, pitch); if (TryGetFrontPanelSettings(out var settings)) { CreateFrontBackSegment(veeperSurfaceSet, vehicle, settings); } if (TryGetBackPanelSettings(out var settings2)) { CreateFrontBackSegment(veeperSurfaceSet, vehicle, settings2); } if (TryGetRoofPanelSettings(out var settings3)) { CreateFrontBackSegment(veeperSurfaceSet, vehicle, settings3); } return veeperSurfaceSet; } private void CreateSideSegments(VeeperSurfaceSet set, LandVehicle vehicle, bool isRight, float panelWidth, float panelHeight, float forwardOffset, float verticalOffset, float sideOffset, float pitch) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) Vector3 localCenter = default(Vector3); ((Vector3)(ref localCenter))..ctor(sideOffset, verticalOffset, forwardOffset); Quaternion localRotation = ComputeBaseRotation(isRight, pitch); SurfaceSegment surfaceSegment = CreateSegment(vehicle, localCenter, localRotation, panelWidth, panelHeight, isRight, isUpper: false); if (surfaceSegment != null) { set.Segments.Add(surfaceSegment); } } private void CreateFrontBackSegment(VeeperSurfaceSet set, LandVehicle vehicle, PanelSettings settings) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) Vector3 offset = settings.Offset; Quaternion localRotation = ComputePanelRotation(settings.Outward, settings.Euler); SurfaceSegment surfaceSegment = CreateSegment(vehicle, offset, localRotation, settings.Width, settings.Height, isRight: false, isUpper: false, settings.PanelId); if (surfaceSegment != null) { set.Segments.Add(surfaceSegment); } } private SurfaceSegment CreateSegment(LandVehicle vehicle, Vector3 localCenter, Quaternion localRotation, float width, float height, bool isRight, bool isUpper, string panelIdOverride = null) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) if (height <= 0.01f) { return null; } GameObject val = Object.Instantiate<GameObject>(_templateRoot); string text = (string.IsNullOrEmpty(panelIdOverride) ? ((isRight ? "Right" : "Left") + "_" + (isUpper ? "Upper" : "Lower")) : panelIdOverride); ((Object)val).name = "VeeperSpraySurface_" + text; val.SetActive(false); val.transform.SetParent(((Component)vehicle).transform, false); val.transform.localPosition = localCenter; val.transform.localRotation = localRotation; val.transform.localScale = Vector3.one; SpraySurface componentInChildren = val.GetComponentInChildren<SpraySurface>(true); SpraySurfaceInteraction componentInChildren2 = val.GetComponentInChildren<SpraySurfaceInteraction>(true); InteractableObject componentInChildren3 = val.GetComponentInChildren<InteractableObject>(true); NetworkObject val2 = val.GetComponentInChildren<NetworkObject>(true); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren2 == (Object)null) { _logger.Warning("[SprayPaintableVeeper] Template clone missing SpraySurface or Interaction."); Object.Destroy((Object)(object)val); return null; } if (!IsServerAuthoritative && (Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)val2); val2 = null; } componentInChildren2.SpraySurface = componentInChildren; if ((Object)(object)componentInChildren3 != (Object)null) { componentInChildren2.IntObj = componentInChildren3; } if ((Object)(object)_templateSurface != (Object)null) { componentInChildren.Region = _templateSurface.Region; } EnsureBottomLeftPoint(componentInChildren); EnsureProjector(componentInChildren); componentInChildren.Width = ToPixels(width); componentInChildren.Height = ToPixels(height); Transform bottomLeftPoint = componentInChildren.BottomLeftPoint; if ((Object)(object)bottomLeftPoint != (Object)null) { bottomLeftPoint.localPosition = new Vector3((0f - width) * 0.5f, (0f - height) * 0.5f, 0f); bottomLeftPoint.localRotation = Quaternion.identity; } UpdateInteractable(componentInChildren3, width, height); UpdateCameraPosition(componentInChildren2); if (string.IsNullOrEmpty(panelIdOverride)) { SetStableGuid(vehicle, isRight, isUpper, componentInChildren); } else { SetStableGuid(vehicle, panelIdOverride, componentInChildren); } componentInChildren.EnsureDrawingExists(); componentInChildren.ResizeProjector(); componentInChildren2.ResizeCanvas(); val.SetActive(true); if (IsServerAuthoritative && (Object)(object)val2 != (Object)null && (Object)(object)InstanceFinder.ServerManager != (Object)null) { try { InstanceFinder.ServerManager.Spawn(val2, (NetworkConnection)null, default(Scene)); } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to spawn network object for panel: " + ex.Message); } } RegisterSurface(componentInChildren); TrackSurface(componentInChildren); TryApplySavedGraffiti(componentInChildren); ApplyInteractionState(componentInChildren3, _interactionActive); string panelId; if (!string.IsNullOrEmpty(panelIdOverride)) { panelId = panelIdOverride.ToLowerInvariant(); } else { string obj = (isRight ? "right" : "left"); string text2 = (isUpper ? "upper" : "lower"); panelId = obj + ":" + text2; } return new SurfaceSegment { Root = val, Surface = componentInChildren, Interaction = componentInChildren2, Interactable = componentInChildren3, IsRight = isRight, IsUpper = isUpper, PanelId = panelId }; } private static Quaternion ComputeBaseRotation(bool isRight, float pitch) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Quaternion val = Quaternion.LookRotation(isRight ? Vector3.right : Vector3.left, Vector3.up); if (Mathf.Abs(pitch) <= 0.01f) { return val; } return Quaternion.AngleAxis(pitch, val * Vector3.right) * val; } private static Quaternion ComputePanelRotation(Vector3 outward, Vector3 eulerOffset) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (outward == Vector3.zero) { outward = Vector3.forward; } Vector3 val = Vector3.up; if (Mathf.Abs(Vector3.Dot(((Vector3)(ref outward)).normalized, val)) > 0.99f) { val = Vector3.forward; } Quaternion val2 = Quaternion.LookRotation(outward, val); if (eulerOffset == Vector3.zero) { return val2; } return val2 * Quaternion.Euler(eulerOffset); } private void DestroySurfaceSet(VeeperSurfaceSet set) { if (set == null) { return; } for (int i = 0; i < set.Segments.Count; i++) { SurfaceSegment surfaceSegment = set.Segments[i]; if (surfaceSegment == null) { continue; } NetworkObject val = (((Object)(object)surfaceSegment.Root != (Object)null) ? surfaceSegment.Root.GetComponentInChildren<NetworkObject>(true) : (((Object)(object)surfaceSegment.Surface != (Object)null) ? ((Component)surfaceSegment.Surface).GetComponent<NetworkObject>() : null)); if ((Object)(object)surfaceSegment.Surface != (Object)null) { UnregisterSurface(surfaceSegment.Surface); UntrackSurface(surfaceSegment.Surface); } if ((Object)(object)surfaceSegment.Root != (Object)null) { if (IsServerAuthoritative && (Object)(object)val != (Object)null && (Object)(object)InstanceFinder.ServerManager != (Object)null) { try { InstanceFinder.ServerManager.Despawn(val, (Nullable<DespawnType>)null); } catch { } } Object.Destroy((Object)(object)surfaceSegment.Root); } else if ((Object)(object)surfaceSegment.Surface != (Object)null) { Object.Destroy((Object)(object)((Component)surfaceSegment.Surface).gameObject); } } } private void UpdateInteractionState() { if (!TryGetInteractionActive(out var active) || active == _interactionActive) { return; } _interactionActive = active; foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { VeeperSurfaceSet value = vehicleSurface.Value; if (value == null) { continue; } for (int i = 0; i < value.Segments.Count; i++) { SurfaceSegment surfaceSegment = value.Segments[i]; if (surfaceSegment != null) { ApplyInteractionState(surfaceSegment.Interactable, _interactionActive); } } } } private static void ApplyInteractionState(InteractableObject intObj, bool active) { if (!((Object)(object)intObj == (Object)null)) { intObj.SetInteractableState((EInteractableState)((!active) ? 2 : 0)); Collider val = intObj.displayLocationCollider; if ((Object)(object)val == (Object)null) { val = ((Component)intObj).GetComponent<Collider>(); } if ((Object)(object)val == (Object)null) { val = ((Component)intObj).GetComponentInChildren<Collider>(true); } if ((Object)(object)val != (Object)null) { val.enabled = active; } } } private bool TryGetInteractionActive(out bool active) { active = false; try { active = SpraySurfaceInteraction.IsSprayCanEquipped() || SpraySurfaceInteraction.IsGraffitiCleanerEquipped(); return true; } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value && !_warnedInteractionCheck) { _warnedInteractionCheck = true; _logger.Warning("[SprayPaintableVeeper] Spray tool check failed, enabling interaction anyway: " + ex.Message); } active = true; return true; } } public void HandleSurfaceChanged(SpraySurface surface, bool force = false) { if (!_worldReady || (Object)(object)surface == (Object)null || (Object)(object)((Component)surface).gameObject == (Object)null) { return; } string text = ((Object)((Component)surface).gameObject).name ?? string.Empty; if (!text.StartsWith("VeeperSpraySurface", StringComparison.OrdinalIgnoreCase)) { return; } TrackSurface(surface); string surfaceGuid = GetSurfaceGuid(surface); if (!string.IsNullOrEmpty(surfaceGuid) && _suppressSurfaceSync.Contains(surfaceGuid)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] HandleSurfaceChanged: SUPPRESSED (applying network data) for guid=" + surfaceGuid); } return; } bool flag = false; try { flag = surface.HasDrawingBeenFinalized; } catch { } if (!flag && !force) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] HandleSurfaceChanged: Waiting for finalization, guid=" + surfaceGuid); } return; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] HandleSurfaceChanged: {text}, guid={surfaceGuid}, isHost={IsServerAuthoritative}, finalized={flag}"); } if (IsServerAuthoritative) { MarkGraffitiDirty(); BroadcastSurfaceState(surface, null); } else { SendSurfaceStateToServer(surface); } } public void ForceSyncSurface(SpraySurface surface) { HandleSurfaceChanged(surface, force: true); } public void HandleSurfaceFinalized(SpraySurface surface) { if (!_worldReady || (Object)(object)surface == (Object)null || (Object)(object)((Component)surface).gameObject == (Object)null) { return; } string text = ((Object)((Component)surface).gameObject).name ?? string.Empty; if (!text.StartsWith("VeeperSpraySurface", StringComparison.OrdinalIgnoreCase)) { return; } TrackSurface(surface); string surfaceGuid = GetSurfaceGuid(surface); if (!string.IsNullOrEmpty(surfaceGuid) && _suppressSurfaceSync.Contains(surfaceGuid)) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] HandleSurfaceFinalized: SUPPRESSED for guid=" + surfaceGuid); } return; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] HandleSurfaceFinalized: {text}, guid={surfaceGuid}, isHost={IsServerAuthoritative}"); } if (IsServerAuthoritative) { MarkGraffitiDirty(); BroadcastSurfaceState(surface, null); } else { SendSurfaceStateToServer(surface); } } private VeeperSurfaceSet RegisterVehicleSurfaces(LandVehicle vehicle) { if ((Object)(object)vehicle == (Object)null) { return null; } string vehicleKey = GetVehicleKey(vehicle); if (string.IsNullOrEmpty(vehicleKey)) { return null; } VeeperSurfaceSet value = null; SpraySurface[] array = Il2CppArrayBase<SpraySurface>.op_Implicit(((Component)vehicle).GetComponentsInChildren<SpraySurface>(true)); int num = 0; foreach (SpraySurface val in array) { if ((Object)(object)val == (Object)null || !((Object)((Component)val).gameObject).name.StartsWith("VeeperSpraySurface", StringComparison.OrdinalIgnoreCase)) { continue; } string surfaceGuid = GetSurfaceGuid(val); if (string.IsNullOrEmpty(surfaceGuid) || surfaceGuid == "00000000-0000-0000-0000-000000000000") { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Skipping surface with empty GUID on vehicle " + vehicleKey); } continue; } if (value == null && !_vehicleSurfaces.TryGetValue(vehicleKey, out value)) { value = new VeeperSurfaceSet(vehicle); } RegisterSurface(val); RegisterClientSegment(value, val); num++; } if (SprayPaintConfig.DebugLogging.Value && num > 0) { _logger.Msg($"[SprayPaintableVeeper] Client registered {num} existing surfaces for vehicle {vehicleKey}"); } return value; } private void RegisterGlobalSurfaces() { if (IsServerAuthoritative) { return; } SpraySurface[] array = Il2CppArrayBase<SpraySurface>.op_Implicit(Object.FindObjectsOfType<SpraySurface>(true)); int num = 0; foreach (SpraySurface val in array) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { continue; } string name = ((Object)((Component)val).gameObject).name; if (string.IsNullOrEmpty(name) || !name.StartsWith("VeeperSpraySurface", StringComparison.OrdinalIgnoreCase)) { continue; } string surfaceGuid = GetSurfaceGuid(val); if (string.IsNullOrEmpty(surfaceGuid) || surfaceGuid == "00000000-0000-0000-0000-000000000000" || _surfaceLookup.ContainsKey(surfaceGuid)) { continue; } RegisterSurface(val); TrackSurface(val); LandVehicle val2 = ((Component)val).GetComponentInParent<LandVehicle>(); if ((Object)(object)val2 == (Object)null) { val2 = FindVehicleForSurface(val, surfaceGuid); } if ((Object)(object)val2 != (Object)null) { string vehicleKey = GetVehicleKey(val2); if (!string.IsNullOrEmpty(vehicleKey)) { if (!_vehicleSurfaces.TryGetValue(vehicleKey, out var value)) { value = new VeeperSurfaceSet(val2); _vehicleSurfaces[vehicleKey] = value; } if (!value.HasSurface(val)) { RegisterClientSegment(value, val); } } } SpraySurfaceInteraction componentInParent = ((Component)val).GetComponentInParent<SpraySurfaceInteraction>(true); InteractableObject val3 = ((componentInParent != null) ? componentInParent.IntObj : null); if ((Object)(object)val3 == (Object)null) { val3 = ((Component)val).GetComponentInParent<InteractableObject>(true); } ApplyInteractionState(val3, _interactionActive); num++; } if (num > 0 && SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] Client globally registered {num} VeeperSpraySurface objects."); } } private LandVehicle FindVehicleForSurface(SpraySurface surface, string surfaceGuid) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if (!NetworkSingleton<VehicleManager>.InstanceExists) { return null; } List<LandVehicle> allVehicles = NetworkSingleton<VehicleManager>.Instance.AllVehicles; if (allVehicles == null) { return null; } string[] array = new string[5] { "right:lower", "left:lower", "front", "back", "roof" }; for (int i = 0; i < allVehicles.Count; i++) { LandVehicle val = allVehicles[i]; if ((Object)(object)val == (Object)null || !IsVeeperVehicle(val)) { continue; } string networkSyncedVehicleId = GetNetworkSyncedVehicleId(val); if (string.IsNullOrEmpty(networkSyncedVehicleId)) { continue; } string[] array2 = array; foreach (string text in array2) { Guid val2 = CreateDeterministicGuid("veeper:" + networkSyncedVehicleId + ":" + text); if (((object)(Guid)(ref val2)).ToString().Equals(surfaceGuid, StringComparison.OrdinalIgnoreCase)) { return val; } } } return null; } private void RegisterSurface(SpraySurface surface) { if ((Object)(object)surface == (Object)null || !NetworkSingleton<GraffitiManager>.InstanceExists) { return; } GraffitiManager instance = NetworkSingleton<GraffitiManager>.Instance; if (instance.SpraySurfaces != null && !instance.SpraySurfaces.Contains(surface)) { instance.SpraySurfaces.Add(surface); if (IsServerAuthoritative) { instance.HasChanged = true; } } } private void UnregisterSurface(SpraySurface surface) { if ((Object)(object)surface == (Object)null || !NetworkSingleton<GraffitiManager>.InstanceExists) { return; } List<SpraySurface> spraySurfaces = NetworkSingleton<GraffitiManager>.Instance.SpraySurfaces; if (spraySurfaces != null) { if (!spraySurfaces.Contains(surface)) { spraySurfaces.Add(surface); } if ((Object)(object)_templateSurface != (Object)null) { ((Component)surface).gameObject.layer = ((Component)_templateSurface).gameObject.layer; } } } private void RegisterClientSegment(VeeperSurfaceSet set, SpraySurface surface) { if (set != null && !((Object)(object)surface == (Object)null) && !set.HasSurface(surface)) { SpraySurfaceInteraction componentInParent = ((Component)surface).GetComponentInParent<SpraySurfaceInteraction>(true); InteractableObject val = null; if ((Object)(object)componentInParent != (Object)null) { componentInParent.SpraySurface = surface; val = componentInParent.IntObj; } if ((Object)(object)val == (Object)null) { val = ((Component)surface).GetComponentInParent<InteractableObject>(true); } string panelId = null; GameObject gameObject = ((Component)surface).gameObject; string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? ""; if (text.Contains("Front", StringComparison.OrdinalIgnoreCase)) { panelId = "front"; } else if (text.Contains("Back", StringComparison.OrdinalIgnoreCase)) { panelId = "back"; } else if (text.Contains("Roof", StringComparison.OrdinalIgnoreCase)) { panelId = "roof"; } else if (text.Contains("Right", StringComparison.OrdinalIgnoreCase)) { panelId = (text.Contains("Upper", StringComparison.OrdinalIgnoreCase) ? "right:upper" : "right:lower"); } else if (text.Contains("Left", StringComparison.OrdinalIgnoreCase)) { panelId = (text.Contains("Upper", StringComparison.OrdinalIgnoreCase) ? "left:upper" : "left:lower"); } set.Segments.Add(new SurfaceSegment { Root = ((Component)surface).gameObject, Surface = surface, Interaction = componentInParent, Interactable = val, IsRight = text.Contains("Right", StringComparison.OrdinalIgnoreCase), IsUpper = text.Contains("Upper", StringComparison.OrdinalIgnoreCase), PanelId = panelId }); TrackSurface(surface); ApplyInteractionState(val, _interactionActive); } } private void TrackSurface(SpraySurface surface) { if ((Object)(object)surface == (Object)null) { return; } string surfaceGuid = GetSurfaceGuid(surface); if (!string.IsNullOrEmpty(surfaceGuid)) { _surfaceLookup[surfaceGuid] = surface; if (_pendingSync.TryGetValue(surfaceGuid, out var value)) { _pendingSync.Remove(surfaceGuid); ApplySurfacePayload(surface, value); } } } private void UntrackSurface(SpraySurface surface) { if (!((Object)(object)surface == (Object)null)) { string surfaceGuid = GetSurfaceGuid(surface); if (!string.IsNullOrEmpty(surfaceGuid)) { _surfaceLookup.Remove(surfaceGuid); _lastSurfaceSyncTimes.Remove(surfaceGuid); _pendingSync.Remove(surfaceGuid); _suppressSurfaceSync.Remove(surfaceGuid); } } } private void EnsureBottomLeftPoint(SpraySurface surface) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown if ((Object)(object)surface.BottomLeftPoint != (Object)null) { surface.BottomLeftPoint.SetParent(((Component)surface).transform, false); return; } GameObject val = new GameObject("BottomLeftPoint"); val.transform.SetParent(((Component)surface).transform, false); surface.BottomLeftPoint = val.transform; } private void EnsureProjector(SpraySurface surface) { if (!((Object)(object)surface.Projector != (Object)null)) { DecalProjector componentInChildren = ((Component)surface).GetComponentInChildren<DecalProjector>(true); if ((Object)(object)componentInChildren != (Object)null) { surface.Projector = componentInChildren; } } } private void UpdateInteractable(InteractableObject intObj, float width, float height) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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) if (!((Object)(object)intObj == (Object)null)) { Collider val = intObj.displayLocationCollider; if ((Object)(object)val == (Object)null) { val = ((Component)intObj).GetComponent<Collider>(); } if ((Object)(object)val == (Object)null) { val = ((Component)intObj).GetComponentInChildren<Collider>(true); } BoxCollider val2 = (BoxCollider)(object)((val is BoxCollider) ? val : null); if (val2 != null) { float num = Mathf.Max(0.05f, val2.size.z); val2.size = new Vector3(width, height, num); val2.center = Vector3.zero; ((Collider)val2).isTrigger = false; } else if ((Object)(object)val != (Object)null) { val.isTrigger = false; } if ((Object)(object)intObj.displayLocationPoint != (Object)null) { intObj.displayLocationPoint.localPosition = Vector3.zero; intObj.displayLocationPoint.localRotation = Quaternion.identity; } intObj.LimitInteractionAngle = false; } } private void UpdateCameraPosition(SpraySurfaceInteraction interaction) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)((interaction != null) ? interaction.CameraPosition : null) == (Object)null)) { float num = _templateCameraLocalZ; if (Mathf.Abs(num) < 0.05f) { num = -1.5f; } interaction.CameraPosition.localPosition = new Vector3(0f, 0f, num); interaction.CameraPosition.localRotation = _templateCameraLocalRotation; } } private static int ToPixels(float worldSize) { float num = SpraySurface.PIXEL_SIZE; if (num <= 0f) { num = 0.05f; } int num2 = Mathf.RoundToInt(worldSize / num); return Mathf.Max(1, num2); } private string GetNetworkSyncedVehicleId(LandVehicle vehicle) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicle == (Object)null) { return null; } try { NetworkObject component = ((Component)vehicle).GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null && component.ObjectId > 0) { return $"netobj:{component.ObjectId}"; } Guid gUID = vehicle.GUID; string text = ((object)(Guid)(ref gUID)).ToString(); if (!string.IsNullOrEmpty(text) && text != "00000000-0000-0000-0000-000000000000") { return text; } return null; } catch { return null; } } private static string GetPersistentVehicleGuid(LandVehicle vehicle) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicle == (Object)null) { return null; } try { Guid gUID = vehicle.GUID; string text = ((object)(Guid)(ref gUID)).ToString(); if (!string.IsNullOrEmpty(text) && text != "00000000-0000-0000-0000-000000000000") { return text; } return null; } catch { return null; } } private void SetStableGuid(LandVehicle vehicle, bool isRight, bool isUpper, SpraySurface surface) { //IL_00b0: 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_00b9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicle == (Object)null || (Object)(object)surface == (Object)null) { return; } try { string networkSyncedVehicleId = GetNetworkSyncedVehicleId(vehicle); if (string.IsNullOrEmpty(networkSyncedVehicleId)) { _logger.Warning("[SprayPaintableVeeper] SetStableGuid: Could not get stable vehicle ID"); surface.RegenerateGUID(); return; } string value = (isRight ? "right" : "left"); string value2 = (isUpper ? "upper" : "lower"); string text = $"veeper:{networkSyncedVehicleId}:{value}:{value2}"; Guid gUID = CreateDeterministicGuid(text); surface.SetGUID(gUID); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] SetStableGuid: vehicleID={networkSyncedVehicleId}, seed={text}, surfaceGUID={((object)(Guid)(ref gUID)).ToString()}"); } } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to set stable GUID: " + ex.Message); surface.RegenerateGUID(); } } private void SetStableGuid(LandVehicle vehicle, string panelId, SpraySurface surface) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)vehicle == (Object)null || (Object)(object)surface == (Object)null || string.IsNullOrWhiteSpace(panelId)) { return; } try { string networkSyncedVehicleId = GetNetworkSyncedVehicleId(vehicle); if (string.IsNullOrEmpty(networkSyncedVehicleId)) { _logger.Warning("[SprayPaintableVeeper] SetStableGuid: Could not get stable vehicle ID"); surface.RegenerateGUID(); return; } string text = "veeper:" + networkSyncedVehicleId + ":" + panelId.ToLowerInvariant(); Guid gUID = CreateDeterministicGuid(text); surface.SetGUID(gUID); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] SetStableGuid: vehicleID={networkSyncedVehicleId}, seed={text}, surfaceGUID={((object)(Guid)(ref gUID)).ToString()}"); } } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to set stable GUID: " + ex.Message); surface.RegenerateGUID(); } } private static Guid CreateDeterministicGuid(string seed) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) using MD5 mD = MD5.Create(); byte[] b = mD.ComputeHash(Encoding.UTF8.GetBytes(seed)); return new Guid(new Guid(b).ToString()); } private static string GetSurfaceGuid(SpraySurface surface) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)surface == (Object)null) { return null; } try { Guid gUID = surface.GUID; return ((object)(Guid)(ref gUID)).ToString(); } catch { return null; } } private static void GetPanelSettings(out float panelWidth, out float panelHeight, out float forwardOffsetRight, out float forwardOffsetLeft, out float verticalOffset, out float sideOffsetRight, out float sideOffsetLeft, out float pitch, out float bendHeight, out float bendAngle) { panelWidth = Mathf.Max(0.1f, 5f); panelHeight = Mathf.Max(0.1f, 1.7f); forwardOffsetRight = -5f; forwardOffsetLeft = 5f; verticalOffset = 0.75f; sideOffsetRight = 0.9f; sideOffsetLeft = -0.9f; pitch = 0f; bendHeight = Mathf.Clamp(0.45f, 0f, panelHeight); bendAngle = 0f; } private static bool TryGetFrontPanelSettings(out PanelSettings settings) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) settings = default(PanelSettings); settings = new PanelSettings("Front", Vector3.forward, Mathf.Max(0.1f, 2f), Mathf.Max(0.1f, 1f), new Vector3(2f, 0.8f, 2.4f), new Vector3(-80f, 0f, 0f)); return true; } private static bool TryGetBackPanelSettings(out PanelSettings settings) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) settings = default(PanelSettings); settings = new PanelSettings("Back", Vector3.back, Mathf.Max(0.1f, 2f), Mathf.Max(0.1f, 2f), new Vector3(-2f, 0.7f, -2.4f), new Vector3(0f, 0f, 0f)); return true; } private static bool TryGetRoofPanelSettings(out PanelSettings settings) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) settings = default(PanelSettings); settings = new PanelSettings("Roof", Vector3.up, Mathf.Max(0.1f, 2f), Mathf.Max(0.1f, 4.5f), new Vector3(-2f, 1.55f, -0.2f), new Vector3(0f, 0f, 0f)); return true; } private void TryApplySavedGraffitiForVehicle(VeeperSurfaceSet set) { if ((Object)(object)set?.Vehicle == (Object)null || !IsServerAuthoritative) { return; } Dictionary<string, SpraySurfaceSaveData> graffitiCache = GetGraffitiCache(); Dictionary<string, SpraySurfaceSaveData> veeperGraffitiCache = GetVeeperGraffitiCache(); Dictionary<string, SpraySurfaceSaveData> dictionary = new Dictionary<string, SpraySurfaceSaveData>(StringComparer.OrdinalIgnoreCase); if (graffitiCache != null) { foreach (KeyValuePair<string, SpraySurfaceSaveData> item in graffitiCache) { dictionary[item.Key] = item.Value; } } if (veeperGraffitiCache != null) { foreach (KeyValuePair<string, SpraySurfaceSaveData> item2 in veeperGraffitiCache) { dictionary[item2.Key] = item2.Value; } } if (dictionary.Count == 0) { return; } string persistentVehicleGuid = GetPersistentVehicleGuid(set.Vehicle); if (string.IsNullOrEmpty(persistentVehicleGuid)) { return; } if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] TryApplySavedGraffitiForVehicle: vehicleGuid={persistentVehicleGuid}, segments={set.Segments.Count}"); } for (int i = 0; i < set.Segments.Count; i++) { SurfaceSegment surfaceSegment = set.Segments[i]; if (!((Object)(object)surfaceSegment?.Surface == (Object)null)) { TryApplySavedGraffiti(surfaceSegment.Surface, surfaceSegment.PanelId, persistentVehicleGuid, dictionary); } } } private void TryApplySavedGraffitiToAll() { if (!_worldReady || !IsServerAuthoritative) { return; } Dictionary<string, SpraySurfaceSaveData> graffitiCache = GetGraffitiCache(); Dictionary<string, SpraySurfaceSaveData> veeperGraffitiCache = GetVeeperGraffitiCache(); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] TryApplySavedGraffitiToAll: gameCache={graffitiCache?.Count ?? 0}, veeperCache={veeperGraffitiCache?.Count ?? 0}"); } Dictionary<string, SpraySurfaceSaveData> dictionary = new Dictionary<string, SpraySurfaceSaveData>(StringComparer.OrdinalIgnoreCase); if (graffitiCache != null) { foreach (KeyValuePair<string, SpraySurfaceSaveData> item in graffitiCache) { dictionary[item.Key] = item.Value; } } if (veeperGraffitiCache != null) { foreach (KeyValuePair<string, SpraySurfaceSaveData> item2 in veeperGraffitiCache) { dictionary[item2.Key] = item2.Value; } } if (dictionary.Count == 0) { if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] TryApplySavedGraffitiToAll: No saved graffiti data found"); } return; } foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { VeeperSurfaceSet value = vehicleSurface.Value; if ((Object)(object)value?.Vehicle == (Object)null) { continue; } string persistentVehicleGuid = GetPersistentVehicleGuid(value.Vehicle); for (int i = 0; i < value.Segments.Count; i++) { SurfaceSegment surfaceSegment = value.Segments[i]; if (!((Object)(object)surfaceSegment?.Surface == (Object)null)) { TryApplySavedGraffiti(surfaceSegment.Surface, surfaceSegment.PanelId, persistentVehicleGuid, dictionary); } } } } private Dictionary<string, SpraySurfaceSaveData> GetVeeperGraffitiCache() { string activeSavePath = GetActiveSavePath(); if (string.IsNullOrEmpty(activeSavePath)) { return null; } if (!string.Equals(activeSavePath, _activeSavePath, StringComparison.OrdinalIgnoreCase)) { _veeperGraffitiCacheLoaded = false; _veeperGraffitiCache = null; } if (_veeperGraffitiCacheLoaded) { return _veeperGraffitiCache; } _veeperGraffitiCache = LoadVeeperGraffitiCache(activeSavePath); _veeperGraffitiCacheLoaded = true; return _veeperGraffitiCache; } private void TryApplySavedGraffiti(SpraySurface surface) { string text = null; string panelId = null; foreach (KeyValuePair<string, VeeperSurfaceSet> vehicleSurface in _vehicleSurfaces) { VeeperSurfaceSet value = vehicleSurface.Value; if ((Object)(object)value?.Vehicle == (Object)null) { continue; } for (int i = 0; i < value.Segments.Count; i++) { if ((Object)(object)value.Segments[i]?.Surface == (Object)(object)surface) { text = GetPersistentVehicleGuid(value.Vehicle); panelId = value.Segments[i].PanelId; break; } } if (text != null) { break; } } Dictionary<string, SpraySurfaceSaveData> graffitiCache = GetGraffitiCache(); Dictionary<string, SpraySurfaceSaveData> veeperGraffitiCache = GetVeeperGraffitiCache(); Dictionary<string, SpraySurfaceSaveData> dictionary = new Dictionary<string, SpraySurfaceSaveData>(StringComparer.OrdinalIgnoreCase); if (graffitiCache != null) { foreach (KeyValuePair<string, SpraySurfaceSaveData> item in graffitiCache) { dictionary[item.Key] = item.Value; } } if (veeperGraffitiCache != null) { foreach (KeyValuePair<string, SpraySurfaceSaveData> item2 in veeperGraffitiCache) { dictionary[item2.Key] = item2.Value; } } if (dictionary.Count != 0) { TryApplySavedGraffiti(surface, panelId, text, dictionary); } } private void TryApplySavedGraffiti(SpraySurface surface, string panelId, string vehicleGuid, Dictionary<string, SpraySurfaceSaveData> cache) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Expected O, but got Unknown if ((Object)(object)surface == (Object)null || cache == null || cache.Count == 0 || !IsServerAuthoritative) { return; } string text = null; try { Guid gUID = surface.GUID; text = ((object)(Guid)(ref gUID)).ToString(); } catch { return; } if (string.IsNullOrEmpty(text)) { return; } string text2 = ((!string.IsNullOrEmpty(vehicleGuid) && !string.IsNullOrEmpty(panelId)) ? (vehicleGuid + ":" + panelId) : text); if (_restoredGuids.Contains(text2)) { return; } SpraySurfaceSaveData value = null; if (!string.IsNullOrEmpty(vehicleGuid) && !string.IsNullOrEmpty(panelId)) { string text3 = vehicleGuid + ":" + panelId; bool value2 = cache.TryGetValue(text3, out value); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg($"[SprayPaintableVeeper] TryApplySavedGraffiti: Looking up key='{text3}', found={value2}, dataStrokes={(value?.Strokes?.Count).GetValueOrDefault()}"); } } if (value == null) { cache.TryGetValue(text, out value); } if (value == null || value.Strokes == null || value.Strokes.Count == 0) { _restoredGuids.Add(text2); return; } try { Il2CppReferenceArray<SprayStroke> val = new Il2CppReferenceArray<SprayStroke>((long)value.Strokes.Count); UShort2 val2 = default(UShort2); UShort2 val3 = default(UShort2); for (int i = 0; i < value.Strokes.Count; i++) { SprayStrokeSaveData sprayStrokeSaveData = value.Strokes[i]; ((UShort2)(ref val2))..ctor(sprayStrokeSaveData.Start.X, sprayStrokeSaveData.Start.Y); ((UShort2)(ref val3))..ctor(sprayStrokeSaveData.End.X, sprayStrokeSaveData.End.Y); ESprayColor val4 = (ESprayColor)(byte)Mathf.Clamp(sprayStrokeSaveData.Color, 0, 255); ((Il2CppArrayBase<SprayStroke>)(object)val)[i] = new SprayStroke(val2, val3, val4); } surface.Set((NetworkConnection)null, val, value.HasDrawingBeenFinalized); surface.ResizeProjector(); _restoredGuids.Add(text2); if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Restored graffiti for " + text2); } } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to restore graffiti for " + text2 + ": " + ex.Message); } } private Dictionary<string, SpraySurfaceSaveData> GetGraffitiCache() { string activeSavePath = GetActiveSavePath(); if (string.IsNullOrEmpty(activeSavePath)) { return null; } if (!string.Equals(activeSavePath, _activeSavePath, StringComparison.OrdinalIgnoreCase)) { _activeSavePath = activeSavePath; _graffitiCacheLoaded = false; _graffitiCache = null; _restoredGuids.Clear(); } if (_graffitiCacheLoaded) { return _graffitiCache; } _graffitiCache = LoadGraffitiCache(activeSavePath); _graffitiCacheLoaded = _graffitiCache != null; return _graffitiCache; } private Dictionary<string, SpraySurfaceSaveData> LoadGraffitiCache(string savePath) { string path = Path.Combine(savePath, "Graffiti.json"); if (!File.Exists(path)) { if (!_warnedGraffitiMissing) { _warnedGraffitiMissing = true; if (SprayPaintConfig.DebugLogging.Value) { _logger.Msg("[SprayPaintableVeeper] Graffiti save file not found yet."); } } return null; } try { string text = File.ReadAllText(path); if (string.IsNullOrWhiteSpace(text)) { return null; } JsonSerializerOptions options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; GraffitiFileData graffitiFileData = JsonSerializer.Deserialize<GraffitiFileData>(text, options); if (graffitiFileData?.SpraySurfaces == null || graffitiFileData.SpraySurfaces.Count == 0) { return null; } Dictionary<string, SpraySurfaceSaveData> dictionary = new Dictionary<string, SpraySurfaceSaveData>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < graffitiFileData.SpraySurfaces.Count; i++) { SpraySurfaceSaveData spraySurfaceSaveData = graffitiFileData.SpraySurfaces[i]; if (spraySurfaceSaveData != null && !string.IsNullOrEmpty(spraySurfaceSaveData.GUID) && !dictionary.ContainsKey(spraySurfaceSaveData.GUID)) { dictionary.Add(spraySurfaceSaveData.GUID, spraySurfaceSaveData); } } return dictionary; } catch (Exception ex) { _logger.Warning("[SprayPaintableVeeper] Failed to read graffiti save: " + ex.Message); return null; } } private static string GetActiveSavePath() { try { LoadManager instance = Singleton<LoadManager>.Instance; if ((Object)(object)instance == (Object)null) { return null; } SaveInfo activeSaveInfo = instance.ActiveSaveInfo; if (activeSaveInfo == null) { return null; } return activeSaveInfo.SavePath; } catch { return null; } } } } namespace SprayPaintableVeeper.Patches { public static class GraffitiCleanerPatch { private const string SurfacePrefix = "VeeperSpraySurface"; private static bool _initialized; private static Instance _logger; private static bool _warnedCleanerCheck; public static void Initialize(Instance logger) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { _logger = logger; new Harmony("SprayPaintableVeeper.GraffitiCleaner").PatchAll(typeof(GraffitiCleanerPatch)); _initialized = true; } } private static void Log(string message) { if (SprayPaintConfig.DebugLogging.Value) { if (_logger != null) { _logger.Msg(message); } else { MelonLogger.Msg(message); } } } private static void Warn(string message) { if (SprayPaintConfig.DebugLogging.Value) { if (_logger != null) { _logger.Warning(message); } else { MelonLogger.Warning(message); } } } [HarmonyPatch(typeof(SpraySurfaceInteraction), "UseGraffitiCleaner")] [HarmonyPostfix] private static void UseGraffitiCleanerPostfix(SpraySurfaceInteraction __instance) { HandleCleaner(__instance, "UseGraffitiCleaner"); } [HarmonyPatch(typeof(SpraySurfaceInteraction), "Interacted")] [HarmonyPostfix] private static void InteractedPostfix(SpraySurfaceInteraction __instance) { if (TryIsCleanerEquipped(out var equipped) && equipped) { HandleCleaner(__instance, "Interacted"); } } [HarmonyPatch(typeof(SpraySurfaceInteraction), "Clear")] [HarmonyPostfix] private static void ClearPostfix(SpraySurfaceInteraction __instance) { HandleCleaner(__instance, "Clear"); } private static void HandleCleaner(SpraySurfaceInteraction interaction, string source) { if (!TryGetVeeperSurface(interaction, out var surface, out var name)) { return; } try { bool flag = InstanceFinder.IsServer || InstanceFinder.IsOffline; Log($"[SprayPaintableVeeper] Cleaner ({source}) target: {name} GUID={GetSurfaceGuid(surface)} Server={flag}"); if (flag) { ForceClear(surface, source + "-server"); } else { surface.RpcWriter___Server_ClearDrawing_2166136261(); Log("[SprayPaintableVeeper] Sent ClearDrawing RPC to server."); ForceClear(surface, source + "-client"); } SprayPaintableVeeperMod.ActiveManager?.ForceSyncSurface(surface); } catch (Exception ex) { Warn("[SprayPaintableVeeper] Cleaner (" + source + ") failed: " + ex.Message); } } private static bool TryIsCleanerEquipped(out bool equipped) { equipped = false; try { equipped = SpraySurfaceInteraction.IsGraffitiCleanerEquipped(); return true; } catch (Exception ex) { if (SprayPaintConfig.DebugLogging.Value && !_warnedCleanerCheck) { _warnedCleanerCheck = true; Warn("[SprayPaintableVeeper] Cleaner equip check failed: " + ex.Message); } return false; } } private static bool TryGetVeeperSurface(SpraySurfaceInteraction interaction, out SpraySurface surface, out string name) { surface = null; name = string.Empty; if ((Object)(object)interaction == (Object)null) { return false; } surface = interaction.SpraySurface; if ((Object)(object)surface == (Object)null || (Object)(object)((Component)surface).gameObject == (Object)null) { Log("[SprayPaintableVeeper] Cleaner target surface missing."); return false; } name = ((Object)((Component)surface).gameObject).name ?? string.Empty; if (!name.StartsWith("VeeperSpraySurface", StringComparison.OrdinalIgnoreCase)) { return false; } return true; } private static string GetSurfaceGuid(SpraySurface surface) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) try { Guid gUID = surface.GUID; return ((object)(Guid)(ref gUID)).ToString(); } catch { return "unknown"; } } private static void ForceClear(SpraySurface surface, string origin) { if ((Object)(object)surface == (Object)null) { return; } try { surface.ClearDrawing(); Log("[SprayPaintableVeeper] ClearDrawing called (" + origin + ")."); } catch (Exception ex) { Warn("[SprayPaintableVeeper] ClearDrawing failed (" + origin + "): " + ex.Message); } try { Il2CppReferenceArray<SprayStroke> val = new Il2CppReferenceArray<SprayStroke>(0L); surface.Set((NetworkConnection)null, val, false); Log("[SprayPaintableVeeper] Set(empty) called (" + origin + ")."); } catch (Exception ex2) { Warn("[SprayPaintableVeeper] Set(empty) failed (" + origin + "): " + ex2.Message); } try { surface.ResizeProjector(); Log("[SprayPaintableVeeper] ResizeProjector called (" + origin + ")."); } catch (Exception ex3) { Warn("[SprayPaintableVeeper] ResizeProjector failed (" + origin + "): " + ex3.Message); } MarkGraffitiDirty(); try { surface.RpcLogic___ClearDrawing_2166136261(); Log("[SprayPaintableVeeper] RpcLogic ClearDrawing called (" + origin + ")."); } catch (Exception ex4) { Warn("[SprayPaintableVeeper] RpcLogic ClearDrawing failed (" + origin + "): " + ex4.Message); } } private static void MarkGraffitiDirty() { if (!NetworkSingleton<GraffitiManager>.InstanceExists) { return;