using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Security;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using BoneLib;
using BoneLib.BoneMenu;
using BoneLib.Notifications;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Warehouse;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using TemporalEchoes;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Rendering;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(Core), "TemporalEchoes", "1.0.0", "CAitStudio", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonOptionalDependencies(new string[] { "BoneLib" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("TemporalEchoes")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TemporalEchoes")]
[assembly: AssemblyTitle("TemporalEchoes")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace TemporalEchoes
{
public class Core : MelonMod
{
private sealed class OwnedEchoLevel
{
public string Path;
public LevelEchoFile File;
public List<EchoMessage> Messages;
}
[CompilerGenerated]
private sealed class <LoadEchoesAfterDelay>d__25 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public int token;
public string barcode;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadEchoesAfterDelay>d__25(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<>2__current = (object)new WaitForSeconds(2.5f);
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
if (!ModEnabled || token != _levelLoadToken || barcode != _currentBarcode)
{
return false;
}
LoadCurrentLevelFile();
RenderCurrentLevelEchoes();
MelonCoroutines.Start(DownloadCurrentLevelFromServer());
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private const string ModName = "TemporalEchoes";
private const float MenuActionCooldownSeconds = 5f;
private const float EchoPopupSeconds = 5f;
private const float StatusPopupSeconds = 2f;
private const int LevelLoadDelayMs = 2500;
private const float DuplicateEchoRadius = 0.5f;
private const string ServerUrl = "https://temporal-echoes.3260142519.workers.dev";
private static Core _instance;
private static string _currentBarcode = string.Empty;
private static string _currentLevelTitle = string.Empty;
private static int _levelLoadToken;
private static string _draftBody = string.Empty;
private static LevelEchoFile _currentFile;
private static float _lastCreateClickTime = -100f;
private static float _lastReloadClickTime = -100f;
private static EchoApiClient _api;
private static EchoRenderer _renderer;
private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNameCaseInsensitive = true
};
private static string DataDir => Path.Combine(MelonEnvironment.UserDataDirectory, "TemporalEchoes");
private static string LevelsDir => Path.Combine(DataDir, "Levels");
private static bool DebugInfoEnabled => EchoPreferences.DebugInfo;
private static bool ModEnabled => EchoPreferences.ModEnabled;
public override void OnInitializeMelon()
{
_instance = this;
EchoPreferences.Setup();
Directory.CreateDirectory(LevelsDir);
_api = new EchoApiClient((Func<bool>)(() => ModEnabled), (Func<string>)NormalizeServerUrl, JsonOptions, (Action<string>)((MelonBase)this).LoggerInstance.Warning, (Action<string, string, NotificationType>)SendDebugNotification);
_renderer = new EchoRenderer((Func<bool>)(() => ModEnabled), (Action<EchoMessage>)SendEchoNotification, (Action<string>)((MelonBase)this).LoggerInstance.Warning);
_renderer.LoadIcons();
EchoBoneMenu.Setup(SetModEnabled, delegate(string body)
{
_draftBody = body ?? string.Empty;
}, CreateEchoFromMenu, ReloadCurrentLevel, RefreshMyEchoesMenu);
RefreshMyEchoesMenu();
Hooking.OnLevelLoaded += OnLevelLoaded;
Hooking.OnLevelUnloaded += OnLevelUnloaded;
((MelonBase)this).LoggerInstance.Msg("Initialized.");
}
public override void OnUpdate()
{
if (ModEnabled)
{
_renderer.Update();
}
}
private static void OnLevelLoaded(LevelInfo info)
{
//IL_0001: 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)
_currentBarcode = info.barcode ?? string.Empty;
_currentLevelTitle = info.title ?? string.Empty;
int token = ++_levelLoadToken;
((MelonBase)_instance).LoggerInstance.Msg("Level loaded: " + _currentBarcode);
if (!ModEnabled)
{
_renderer.Clear();
}
else
{
MelonCoroutines.Start(LoadEchoesAfterDelay(token, _currentBarcode));
}
}
[IteratorStateMachine(typeof(<LoadEchoesAfterDelay>d__25))]
private static IEnumerator LoadEchoesAfterDelay(int token, string barcode)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadEchoesAfterDelay>d__25(0)
{
token = token,
barcode = barcode
};
}
private static void OnLevelUnloaded()
{
_levelLoadToken++;
_renderer.Clear();
_currentBarcode = string.Empty;
_currentLevelTitle = string.Empty;
_currentFile = null;
}
private static void CreateEchoFromMenu()
{
//IL_013e: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_0145: Unknown result type (might be due to invalid IL or missing references)
//IL_01af: Unknown result type (might be due to invalid IL or missing references)
if (!ModEnabled || !TryStartMenuAction(ref _lastCreateClickTime))
{
return;
}
if (string.IsNullOrWhiteSpace(_currentBarcode))
{
SendNotification("Temporal Echoes", "No loaded level barcode yet.", 3f, (NotificationType)1);
return;
}
string text = (_draftBody ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(text))
{
SendNotification("Temporal Echoes", "Message body is empty.", 3f, (NotificationType)1);
return;
}
if (EchoContentFilter.TryFindSensitiveWord(text, out var word))
{
SendNotification("Temporal Echoes", "Blocked sensitive word: " + word, 2f, (NotificationType)1);
return;
}
string echoAuthorName = GetEchoAuthorName();
text = ComposeEchoBody(text, echoAuthorName);
EnsureCurrentLevelFile();
object obj;
if (!((Object)(object)Player.Chest != (Object)null))
{
if (!((Object)(object)Player.Head != (Object)null))
{
RigManager rigManager = Player.RigManager;
obj = ((rigManager != null) ? ((Component)rigManager).transform : null);
}
else
{
obj = Player.Head;
}
}
else
{
obj = Player.Chest;
}
Transform val = (Transform)obj;
if ((Object)(object)val == (Object)null)
{
SendNotification("Temporal Echoes", "Player transform not ready.", 3f, (NotificationType)1);
return;
}
Vector3 position = val.position;
if (HasNearbyEcho(position))
{
SendNotification("Temporal Echoes", "An echo already exists here.", 2f, (NotificationType)1);
return;
}
string createdAt = DateTimeOffset.Now.ToString("o");
string uuid = Guid.NewGuid().ToString("N");
EchoMessage echoMessage = new EchoMessage
{
uuid = uuid,
body = text,
position = SerializableVector3.From(position),
createdAt = createdAt,
steamId = TryGetSteamId(),
authorName = echoAuthorName,
authorHash = CurrentAuthorHash(),
color = EchoPreferences.BubbleColor.ToString(),
syncState = "pending"
};
_currentFile.messages.Add(echoMessage);
SaveCurrentLevelFile();
_renderer.Render(echoMessage);
RefreshMyEchoesMenu();
SendNotification("Echo Created", text, 2f, (NotificationType)3);
MelonCoroutines.Start(UploadEchoToServer(echoMessage));
}
private static void ReloadCurrentLevel()
{
if (ModEnabled && TryStartMenuAction(ref _lastReloadClickTime))
{
ReloadCurrentLevelNow();
SendNotification("Temporal Echoes", "Reloading echoes.", 2f, (NotificationType)0);
}
}
private static void ReloadCurrentLevelNow()
{
if (ModEnabled)
{
LoadCurrentLevelFile();
RenderCurrentLevelEchoes();
MelonCoroutines.Start(DownloadCurrentLevelFromServer());
}
}
private static string NormalizeServerUrl()
{
return "https://temporal-echoes.3260142519.workers.dev".Trim().TrimEnd('/');
}
private static bool TryStartMenuAction(ref float lastClickTime)
{
float time = Time.time;
if (time - lastClickTime < 5f)
{
return false;
}
lastClickTime = time;
return true;
}
private static string CurrentAuthorHash()
{
return HashString(EchoPreferences.AuthorSecret);
}
private static void SetModEnabled(bool enabled)
{
EchoPreferences.SetModEnabled(enabled);
if (!enabled)
{
_levelLoadToken++;
_renderer.Clear();
}
else
{
ReloadCurrentLevelNow();
}
}
private static string GetEchoAuthorName()
{
string text = EchoText.Limit(EchoPreferences.AuthorName.Trim(), 50);
if (!string.Equals(text, "NullMan", StringComparison.Ordinal))
{
return text;
}
string text2 = EchoText.Limit(TryGetSteamName().Trim(), 50);
if (string.IsNullOrWhiteSpace(text2))
{
return text;
}
EchoPreferences.SetAuthorName(text2);
return text2;
}
private static string ComposeEchoBody(string body, string authorName)
{
int maxLength = Math.Max(0, 180 - authorName.Length - 1);
string value = authorName + ":" + LimitText(body, maxLength);
return LimitText(value, 180);
}
private static string LimitText(string value, int maxLength)
{
return EchoText.Limit(value, maxLength);
}
private static void RenderCurrentLevelEchoes()
{
_renderer.RenderLevel(_currentFile);
}
private static void SendDebugNotification(string title, string message, NotificationType type)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
if (DebugInfoEnabled)
{
SendNotification(title, message, 2f, type);
}
}
private static string TryGetSteamId()
{
try
{
return (((from assembly in AppDomain.CurrentDomain.GetAssemblies()
select assembly.GetType("Steamworks.SteamClient")).FirstOrDefault((Type type) => type != null)?.GetProperty("SteamId", BindingFlags.Static | BindingFlags.Public))?.GetValue(null))?.ToString() ?? string.Empty;
}
catch
{
return string.Empty;
}
}
private static string TryGetSteamName()
{
try
{
return (((from assembly in AppDomain.CurrentDomain.GetAssemblies()
select assembly.GetType("Steamworks.SteamClient")).FirstOrDefault((Type type) => type != null)?.GetProperty("Name", BindingFlags.Static | BindingFlags.Public))?.GetValue(null))?.ToString() ?? string.Empty;
}
catch
{
return string.Empty;
}
}
private static string FormatNotificationTitle(string createdAt)
{
if (DateTimeOffset.TryParse(createdAt, out var result))
{
return result.ToString("yyyy/MM/dd HH:mm zzz");
}
return "Temporal Echo";
}
private static void SendNotification(string title, string message, float seconds, NotificationType type)
{
//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_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_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_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: 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_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Expected O, but got Unknown
Notifier.Send(new Notification
{
Title = NotificationText.op_Implicit(title),
Message = NotificationText.op_Implicit(message),
Type = type,
ShowTitleOnPopup = true,
PopupLength = seconds
});
}
private static void SendEchoNotification(EchoMessage message)
{
//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_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: 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_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_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected O, but got Unknown
Notifier.Send(new Notification
{
Title = NotificationText.op_Implicit(FormatNotificationTitle(message.createdAt)),
Message = NotificationText.op_Implicit(message.body),
Type = (NotificationType)0,
ShowTitleOnPopup = true,
PopupLength = 5f
});
}
private static void RefreshMyEchoesMenu()
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
//IL_0218: Unknown result type (might be due to invalid IL or missing references)
Page myEchoesPage = EchoBoneMenu.MyEchoesPage;
if (myEchoesPage == null)
{
return;
}
myEchoesPage.RemoveAll();
myEchoesPage.CreateFunction("Refresh List", Color.white, (Action)RefreshMyEchoesMenu);
List<OwnedEchoLevel> ownedEchoLevels = GetOwnedEchoLevels();
if (ownedEchoLevels.Count == 0)
{
myEchoesPage.CreateFunction("No echoes found", Color.gray, (Action)null);
return;
}
foreach (OwnedEchoLevel level2 in ownedEchoLevels.OrderBy<OwnedEchoLevel, string>((OwnedEchoLevel level) => GetLevelMenuName(level.File), StringComparer.OrdinalIgnoreCase))
{
string text = $"{GetLevelMenuName(level2.File)} ({level2.Messages.Count})";
Page val = myEchoesPage.CreatePage(text, Color.white, 10, true);
val.CreateFunction("Show Barcode", Color.gray, (Action)delegate
{
Menu.DisplayDialog("Level Barcode", level2.File.levelBarcode, (Texture2D)null, (Action)null, (Action)null);
});
foreach (EchoMessage item in level2.Messages.OrderByDescending((EchoMessage message) => message.createdAt))
{
EchoMessage capturedMessage = item;
string capturedPath = level2.Path;
string capturedBarcode = level2.File.levelBarcode;
string echoMenuTitle = GetEchoMenuTitle(capturedMessage);
Page val2 = val.CreatePage(echoMenuTitle, Color.cyan, 10, true);
val2.CreateFunction("Show Message", Color.white, (Action)delegate
{
Menu.DisplayDialog("Temporal Echo", capturedMessage.body, (Texture2D)null, (Action)null, (Action)null);
});
val2.CreateFunction("Delete", Color.yellow, (Action)delegate
{
AskDeleteOwnedEcho(capturedPath, capturedBarcode, capturedMessage);
});
}
}
}
private static List<OwnedEchoLevel> GetOwnedEchoLevels()
{
List<OwnedEchoLevel> list = new List<OwnedEchoLevel>();
string authorHash = CurrentAuthorHash();
if (string.IsNullOrWhiteSpace(authorHash) || !Directory.Exists(LevelsDir))
{
return list;
}
string[] files = Directory.GetFiles(LevelsDir, "*.json");
foreach (string text in files)
{
try
{
LevelEchoFile levelEchoFile = JsonSerializer.Deserialize<LevelEchoFile>(File.ReadAllText(text), JsonOptions);
if (levelEchoFile?.messages != null)
{
List<EchoMessage> list2 = levelEchoFile.messages.Where((EchoMessage message) => string.Equals(message.authorHash, authorHash, StringComparison.OrdinalIgnoreCase)).ToList();
if (list2.Count != 0)
{
list.Add(new OwnedEchoLevel
{
Path = text,
File = levelEchoFile,
Messages = list2
});
}
}
}
catch (Exception ex)
{
((MelonBase)_instance).LoggerInstance.Warning("Could not read echo file " + text + ": " + ex.Message);
}
}
return list;
}
private static void AskDeleteOwnedEcho(string path, string levelBarcode, EchoMessage message)
{
string text = EchoText.Limit(message.body, 120);
Menu.DisplayDialog("Delete echo?", "This will delete your echo:\n" + text, Dialog.WarningIcon, (Action)delegate
{
DeleteOwnedEcho(path, levelBarcode, message.uuid);
}, (Action)null);
}
private static void DeleteOwnedEcho(string path, string levelBarcode, string uuid)
{
if (!DeleteOwnedEchoLocal(path, uuid))
{
SendNotification("Temporal Echoes", "Echo was not found or is not yours.", 2f, (NotificationType)1);
return;
}
if (string.Equals(levelBarcode, _currentBarcode, StringComparison.Ordinal))
{
LoadCurrentLevelFile();
RenderCurrentLevelEchoes();
}
RefreshMyEchoesMenu();
Menu.OpenPage(EchoBoneMenu.MyEchoesPage);
SendNotification("Echo Deleted", uuid, 2f, (NotificationType)3);
MelonCoroutines.Start(DeleteEchoFromServer(levelBarcode, uuid));
}
private static bool DeleteOwnedEchoLocal(string path, string uuid)
{
try
{
LevelEchoFile levelEchoFile = JsonSerializer.Deserialize<LevelEchoFile>(File.ReadAllText(path), JsonOptions);
if (levelEchoFile?.messages == null)
{
return false;
}
string authorHash = CurrentAuthorHash();
int num = levelEchoFile.messages.RemoveAll((EchoMessage message) => string.Equals(message.uuid, uuid, StringComparison.OrdinalIgnoreCase) && string.Equals(message.authorHash, authorHash, StringComparison.OrdinalIgnoreCase));
if (num <= 0)
{
return false;
}
File.WriteAllText(path, JsonSerializer.Serialize(levelEchoFile, JsonOptions));
return true;
}
catch (Exception ex)
{
((MelonBase)_instance).LoggerInstance.Warning("Could not delete echo " + uuid + ": " + ex.Message);
return false;
}
}
private static string GetLevelMenuName(LevelEchoFile file)
{
string value = TryGetLevelTitle(file.levelBarcode);
if (!string.IsNullOrWhiteSpace(value))
{
return TrimMenuText(value, 34);
}
if (!string.IsNullOrWhiteSpace(file.levelTitle))
{
return TrimMenuText(file.levelTitle, 34);
}
if (!string.IsNullOrWhiteSpace(file.levelBarcode))
{
return TrimMenuText(file.levelBarcode, 34);
}
return string.IsNullOrWhiteSpace(file.levelHash) ? "Unknown Level" : file.levelHash;
}
private static string TryGetLevelTitle(string barcode)
{
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Expected O, but got Unknown
if (string.IsNullOrWhiteSpace(barcode))
{
return string.Empty;
}
try
{
Crate val = default(Crate);
if (AssetWarehouse.Instance != null && AssetWarehouse.Instance.TryGetCrate(new Barcode(barcode), ref val))
{
return ((val != null) ? ((Scannable)val).Title : null) ?? string.Empty;
}
}
catch
{
return string.Empty;
}
return string.Empty;
}
private static string GetEchoMenuTitle(EchoMessage message)
{
string text = message.createdAt;
if (DateTimeOffset.TryParse(message.createdAt, out var result))
{
text = result.ToString("MM/dd HH:mm");
}
return TrimMenuText(text + " " + message.body, 42);
}
private static string TrimMenuText(string value, int maxLength)
{
value = (value ?? string.Empty).Replace('\n', ' ').Replace('\r', ' ').Trim();
if (value.Length <= maxLength)
{
return value;
}
return value.Substring(0, Math.Max(0, maxLength - 3)) + "...";
}
private static void LoadCurrentLevelFile()
{
if (string.IsNullOrWhiteSpace(_currentBarcode))
{
_currentFile = null;
return;
}
string levelFilePath = GetLevelFilePath(_currentBarcode);
if (!File.Exists(levelFilePath))
{
_currentFile = new LevelEchoFile
{
levelBarcode = _currentBarcode,
levelHash = HashBarcode(_currentBarcode),
levelTitle = _currentLevelTitle,
messages = new List<EchoMessage>()
};
SaveCurrentLevelFile();
return;
}
try
{
_currentFile = JsonSerializer.Deserialize<LevelEchoFile>(File.ReadAllText(levelFilePath), JsonOptions) ?? new LevelEchoFile();
_currentFile.levelBarcode = _currentBarcode;
_currentFile.levelHash = HashBarcode(_currentBarcode);
if (!string.IsNullOrWhiteSpace(_currentLevelTitle))
{
_currentFile.levelTitle = _currentLevelTitle;
}
LevelEchoFile currentFile = _currentFile;
if (currentFile.messages == null)
{
List<EchoMessage> list2 = (currentFile.messages = new List<EchoMessage>());
}
}
catch (Exception value)
{
((MelonBase)_instance).LoggerInstance.Error($"Failed to read echo file {levelFilePath}: {value}");
_currentFile = new LevelEchoFile
{
levelBarcode = _currentBarcode,
levelHash = HashBarcode(_currentBarcode),
levelTitle = _currentLevelTitle,
messages = new List<EchoMessage>()
};
}
}
private static void EnsureCurrentLevelFile()
{
if (_currentFile == null)
{
LoadCurrentLevelFile();
}
}
private static void SaveCurrentLevelFile()
{
if (_currentFile != null && !string.IsNullOrWhiteSpace(_currentBarcode))
{
Directory.CreateDirectory(LevelsDir);
if (!string.IsNullOrWhiteSpace(_currentLevelTitle))
{
_currentFile.levelTitle = _currentLevelTitle;
}
File.WriteAllText(GetLevelFilePath(_currentBarcode), JsonSerializer.Serialize(_currentFile, JsonOptions));
}
}
private static bool HasNearbyEcho(Vector3 position)
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
if (_currentFile?.messages == null)
{
return false;
}
foreach (EchoMessage message in _currentFile.messages)
{
if (Vector3.Distance(message.position.ToVector3(), position) < 0.5f)
{
return true;
}
}
return false;
}
private static string GetLevelFilePath(string barcode)
{
return Path.Combine(LevelsDir, HashBarcode(barcode) + ".json");
}
private static string HashBarcode(string barcode)
{
return HashString(barcode).Substring(0, 16);
}
private static string HashString(string value)
{
using SHA256 sHA = SHA256.Create();
byte[] inArray = sHA.ComputeHash(Encoding.UTF8.GetBytes(value ?? string.Empty));
return Convert.ToHexString(inArray);
}
private static IEnumerator DeleteEchoFromServer(string levelBarcode, string uuid)
{
return _api.DeleteEcho(levelBarcode, uuid, EchoPreferences.AuthorSecret);
}
private static IEnumerator DownloadCurrentLevelFromServer()
{
return _api.DownloadLevel(_currentBarcode, delegate(LevelEchoFile downloaded)
{
_currentFile = downloaded;
_currentFile.levelBarcode = _currentBarcode;
_currentFile.levelHash = HashBarcode(_currentBarcode);
_currentFile.levelTitle = _currentLevelTitle;
SaveCurrentLevelFile();
RenderCurrentLevelEchoes();
});
}
private static IEnumerator UploadEchoToServer(EchoMessage echo)
{
return _api.UploadEcho(_currentBarcode, HashBarcode(_currentBarcode), echo, delegate
{
echo.syncState = "synced";
SaveCurrentLevelFile();
});
}
}
internal sealed class EchoApiClient
{
[CompilerGenerated]
private sealed class <DeleteEcho>d__8 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public string levelBarcode;
public string uuid;
public string authorSecret;
public EchoApiClient <>4__this;
private string <baseUrl>5__1;
private EchoDeleteRequest <payload>5__2;
private HttpClientHandler <handler>5__3;
private HttpClient <client>5__4;
private StringContent <content>5__5;
private HttpRequestMessage <request>5__6;
private Task<HttpResponseMessage> <responseTask>5__7;
private Exception <ex>5__8;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DeleteEcho>d__8(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if ((uint)(num - -6) <= 3u || num == 1)
{
try
{
if ((uint)(num - -6) <= 2u || num == 1)
{
try
{
if ((uint)(num - -6) <= 1u || num == 1)
{
try
{
if (num == -6 || num == 1)
{
try
{
}
finally
{
<>m__Finally4();
}
}
}
finally
{
<>m__Finally3();
}
}
}
finally
{
<>m__Finally2();
}
}
}
finally
{
<>m__Finally1();
}
}
<baseUrl>5__1 = null;
<payload>5__2 = null;
<handler>5__3 = null;
<client>5__4 = null;
<content>5__5 = null;
<request>5__6 = null;
<responseTask>5__7 = null;
<ex>5__8 = null;
<>1__state = -2;
}
private bool MoveNext()
{
bool result;
try
{
switch (<>1__state)
{
default:
result = false;
goto end_IL_0000;
case 0:
<>1__state = -1;
<baseUrl>5__1 = <>4__this._baseUrl();
if (!<>4__this._isEnabled() || string.IsNullOrWhiteSpace(<baseUrl>5__1) || string.IsNullOrWhiteSpace(levelBarcode) || string.IsNullOrWhiteSpace(uuid))
{
result = false;
}
else
{
<payload>5__2 = new EchoDeleteRequest
{
levelBarcode = levelBarcode,
uuid = uuid,
authorSecret = authorSecret
};
<handler>5__3 = CreateHttpHandler();
<>1__state = -3;
<client>5__4 = new HttpClient(<handler>5__3)
{
Timeout = TimeSpan.FromSeconds(20.0)
};
<>1__state = -4;
<content>5__5 = new StringContent(JsonSerializer.Serialize(<payload>5__2, <>4__this._jsonOptions), Encoding.UTF8, "application/json");
<>1__state = -5;
<request>5__6 = new HttpRequestMessage(HttpMethod.Delete, <baseUrl>5__1 + "/api/messages")
{
Content = <content>5__5
};
<>1__state = -6;
try
{
<responseTask>5__7 = <client>5__4.SendAsync(<request>5__6);
}
catch (Exception ex)
{
<ex>5__8 = ex;
<>4__this.ReportHttpFailure("Delete", uuid, <ex>5__8.Message);
result = false;
break;
}
<>2__current = <>4__this.WaitForResponse(<responseTask>5__7);
<>1__state = 1;
result = true;
}
goto end_IL_0000;
case 1:
<>1__state = -6;
if (!<>4__this._isEnabled() || !<>4__this.TryAcceptResponse(<responseTask>5__7, "Delete", uuid))
{
result = false;
break;
}
<>4__this._debugNotify("Echo Deleted Online", uuid, (NotificationType)3);
result = false;
break;
}
<>m__Finally4();
<>m__Finally3();
<>m__Finally2();
<>m__Finally1();
end_IL_0000:;
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
return result;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<handler>5__3 != null)
{
((IDisposable)<handler>5__3).Dispose();
}
}
private void <>m__Finally2()
{
<>1__state = -3;
if (<client>5__4 != null)
{
((IDisposable)<client>5__4).Dispose();
}
}
private void <>m__Finally3()
{
<>1__state = -4;
if (<content>5__5 != null)
{
((IDisposable)<content>5__5).Dispose();
}
}
private void <>m__Finally4()
{
<>1__state = -5;
if (<request>5__6 != null)
{
((IDisposable)<request>5__6).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <DownloadLevel>d__6 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public string barcode;
public Action<LevelEchoFile> applyDownloadedFile;
public EchoApiClient <>4__this;
private string <baseUrl>5__1;
private string <url>5__2;
private UnityWebRequest <request>5__3;
private string <reason>5__4;
private LevelEchoFile <downloaded>5__5;
private Exception <ex>5__6;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DownloadLevel>d__6(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<baseUrl>5__1 = null;
<url>5__2 = null;
<request>5__3 = null;
<reason>5__4 = null;
<downloaded>5__5 = null;
<ex>5__6 = null;
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
<baseUrl>5__1 = <>4__this._baseUrl();
if (!<>4__this._isEnabled() || string.IsNullOrWhiteSpace(<baseUrl>5__1) || string.IsNullOrWhiteSpace(barcode))
{
return false;
}
<url>5__2 = <baseUrl>5__1 + "/api/messages?barcode=" + Uri.EscapeDataString(barcode);
<request>5__3 = UnityWebRequest.Get(<url>5__2);
<request>5__3.timeout = 20;
<>2__current = <request>5__3.SendWebRequest();
<>1__state = 1;
return true;
case 1:
<>1__state = -1;
if (!<>4__this._isEnabled())
{
return false;
}
if (!IsWebRequestSuccess(<request>5__3))
{
<reason>5__4 = $"HTTP {<request>5__3.responseCode} {<request>5__3.error}";
<>4__this._warn("Download skipped/failed: " + <reason>5__4);
<>4__this._debugNotify("Echo Download Failed", <reason>5__4, (NotificationType)2);
return false;
}
try
{
<downloaded>5__5 = JsonSerializer.Deserialize<LevelEchoFile>(<request>5__3.downloadHandler.text, <>4__this._jsonOptions);
if (<downloaded>5__5?.messages == null)
{
return false;
}
applyDownloadedFile(<downloaded>5__5);
<>4__this._debugNotify("Echo Downloaded", $"{<downloaded>5__5.messages.Count} echoes loaded.", (NotificationType)3);
<downloaded>5__5 = null;
}
catch (Exception ex)
{
<ex>5__6 = ex;
<>4__this._warn("Download skipped/failed: " + <ex>5__6.Message);
<>4__this._debugNotify("Echo Download Failed", <ex>5__6.Message, (NotificationType)2);
}
return false;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <UploadEcho>d__7 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public string levelBarcode;
public string levelHash;
public EchoMessage echo;
public Action onSuccess;
public EchoApiClient <>4__this;
private string <baseUrl>5__1;
private EchoUpload <payload>5__2;
private HttpClientHandler <handler>5__3;
private HttpClient <client>5__4;
private StringContent <content>5__5;
private Task<HttpResponseMessage> <responseTask>5__6;
private Exception <ex>5__7;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <UploadEcho>d__7(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if ((uint)(num - -5) <= 2u || num == 1)
{
try
{
if ((uint)(num - -5) <= 1u || num == 1)
{
try
{
if (num == -5 || num == 1)
{
try
{
}
finally
{
<>m__Finally3();
}
}
}
finally
{
<>m__Finally2();
}
}
}
finally
{
<>m__Finally1();
}
}
<baseUrl>5__1 = null;
<payload>5__2 = null;
<handler>5__3 = null;
<client>5__4 = null;
<content>5__5 = null;
<responseTask>5__6 = null;
<ex>5__7 = null;
<>1__state = -2;
}
private bool MoveNext()
{
bool result;
try
{
switch (<>1__state)
{
default:
result = false;
goto end_IL_0000;
case 0:
<>1__state = -1;
<baseUrl>5__1 = <>4__this._baseUrl();
if (!<>4__this._isEnabled() || string.IsNullOrWhiteSpace(<baseUrl>5__1))
{
result = false;
}
else
{
<payload>5__2 = new EchoUpload
{
levelBarcode = levelBarcode,
levelHash = levelHash,
message = echo
};
<handler>5__3 = CreateHttpHandler();
<>1__state = -3;
<client>5__4 = new HttpClient(<handler>5__3)
{
Timeout = TimeSpan.FromSeconds(20.0)
};
<>1__state = -4;
<content>5__5 = new StringContent(JsonSerializer.Serialize(<payload>5__2, <>4__this._jsonOptions), Encoding.UTF8, "application/json");
<>1__state = -5;
try
{
<responseTask>5__6 = <client>5__4.PostAsync(<baseUrl>5__1 + "/api/messages", <content>5__5);
}
catch (Exception ex)
{
<ex>5__7 = ex;
<>4__this.ReportHttpFailure("Upload", echo.uuid, <ex>5__7.Message);
result = false;
break;
}
<>2__current = <>4__this.WaitForResponse(<responseTask>5__6);
<>1__state = 1;
result = true;
}
goto end_IL_0000;
case 1:
<>1__state = -5;
if (!<>4__this._isEnabled())
{
result = false;
break;
}
if (!<>4__this.TryAcceptResponse(<responseTask>5__6, "Upload", echo.uuid))
{
result = false;
break;
}
onSuccess();
<>4__this._debugNotify("Echo Uploaded", echo.uuid, (NotificationType)3);
result = false;
break;
}
<>m__Finally3();
<>m__Finally2();
<>m__Finally1();
end_IL_0000:;
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
return result;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<handler>5__3 != null)
{
((IDisposable)<handler>5__3).Dispose();
}
}
private void <>m__Finally2()
{
<>1__state = -3;
if (<client>5__4 != null)
{
((IDisposable)<client>5__4).Dispose();
}
}
private void <>m__Finally3()
{
<>1__state = -4;
if (<content>5__5 != null)
{
((IDisposable)<content>5__5).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <WaitForResponse>d__9 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public Task<HttpResponseMessage> responseTask;
public EchoApiClient <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WaitForResponse>d__9(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
break;
case 1:
<>1__state = -1;
break;
}
if (!responseTask.IsCompleted)
{
if (!<>4__this._isEnabled())
{
return false;
}
<>2__current = null;
<>1__state = 1;
return true;
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private readonly Func<bool> _isEnabled;
private readonly Func<string> _baseUrl;
private readonly JsonSerializerOptions _jsonOptions;
private readonly Action<string> _warn;
private readonly Action<string, string, NotificationType> _debugNotify;
public EchoApiClient(Func<bool> isEnabled, Func<string> baseUrl, JsonSerializerOptions jsonOptions, Action<string> warn, Action<string, string, NotificationType> debugNotify)
{
_isEnabled = isEnabled;
_baseUrl = baseUrl;
_jsonOptions = jsonOptions;
_warn = warn;
_debugNotify = debugNotify;
}
[IteratorStateMachine(typeof(<DownloadLevel>d__6))]
public IEnumerator DownloadLevel(string barcode, Action<LevelEchoFile> applyDownloadedFile)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DownloadLevel>d__6(0)
{
<>4__this = this,
barcode = barcode,
applyDownloadedFile = applyDownloadedFile
};
}
[IteratorStateMachine(typeof(<UploadEcho>d__7))]
public IEnumerator UploadEcho(string levelBarcode, string levelHash, EchoMessage echo, Action onSuccess)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <UploadEcho>d__7(0)
{
<>4__this = this,
levelBarcode = levelBarcode,
levelHash = levelHash,
echo = echo,
onSuccess = onSuccess
};
}
[IteratorStateMachine(typeof(<DeleteEcho>d__8))]
public IEnumerator DeleteEcho(string levelBarcode, string uuid, string authorSecret)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DeleteEcho>d__8(0)
{
<>4__this = this,
levelBarcode = levelBarcode,
uuid = uuid,
authorSecret = authorSecret
};
}
[IteratorStateMachine(typeof(<WaitForResponse>d__9))]
private IEnumerator WaitForResponse(Task<HttpResponseMessage> responseTask)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WaitForResponse>d__9(0)
{
<>4__this = this,
responseTask = responseTask
};
}
private bool TryAcceptResponse(Task<HttpResponseMessage> responseTask, string operation, string uuid)
{
if (!responseTask.IsCompletedSuccessfully)
{
string reason = responseTask.Exception?.GetBaseException().Message ?? "Unknown error";
ReportHttpFailure(operation, uuid, reason);
return false;
}
HttpResponseMessage result = responseTask.Result;
if (result.IsSuccessStatusCode)
{
return true;
}
string reason2 = $"HTTP {result.StatusCode} {result.ReasonPhrase}";
ReportHttpFailure(operation, uuid, reason2);
return false;
}
private void ReportHttpFailure(string operation, string uuid, string reason)
{
_warn($"{operation} failed for {uuid}: {reason}");
_debugNotify("Echo " + operation + " Failed", reason, (NotificationType)2);
}
private static bool IsWebRequestSuccess(UnityWebRequest request)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Invalid comparison between Unknown and I4
return (int)request.result == 1 && request.responseCode >= 200 && request.responseCode < 300;
}
private static HttpClientHandler CreateHttpHandler()
{
return new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Automatic,
ServerCertificateCustomValidationCallback = (HttpRequestMessage _, X509Certificate2? _, X509Chain? _, SslPolicyErrors _) => true
};
}
}
internal static class EchoBoneMenu
{
private const string DiscordUrl = "https://discord.gg/Qf3r2zKwx";
public static Page MyEchoesPage { get; private set; }
public static void Setup(Action<bool> setModEnabled, Action<string> setDraftBody, Action createEcho, Action reloadEchoes, Action refreshMyEchoes)
{
//IL_000b: 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_0035: 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)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_015a: Unknown result type (might be due to invalid IL or missing references)
//IL_0177: Unknown result type (might be due to invalid IL or missing references)
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
Page val = Page.Root.CreatePage("Temporal Echoes", Color.white, 0, true);
val.CreateBool("Enable Mod", Color.green, EchoPreferences.ModEnabled, setModEnabled);
val.CreateString("Your Name", Color.cyan, EchoPreferences.AuthorName, (Action<string>)EchoPreferences.SetAuthorName);
val.CreateEnum("Echo Color", Color.white, (Enum)EchoPreferences.BubbleColor, (Action<Enum>)EchoPreferences.SetBubbleColor);
val.CreateString("Message Body", Color.white, string.Empty, setDraftBody);
val.CreateBool("Debug Info", Color.cyan, EchoPreferences.DebugInfo, (Action<bool>)EchoPreferences.SetDebugInfo);
val.CreateFunction("Create Echo", Color.green, createEcho);
val.CreateFunction("Reload Echoes", Color.yellow, reloadEchoes);
val.CreateFunction("Join Discord", Color.blue, (Action)delegate
{
Application.OpenURL("https://discord.gg/Qf3r2zKwx");
});
Page val2 = val.CreatePage("Sensitive Words", Color.red, 10, true);
string[] sensitiveWords = EchoContentFilter.SensitiveWords;
foreach (string text in sensitiveWords)
{
val2.CreateFunction(text, Color.red, (Action)null);
}
MyEchoesPage = val.CreatePage("My Echoes", Color.cyan, 10, true);
MyEchoesPage.CreateFunction("Refresh List", Color.white, refreshMyEchoes);
}
}
public enum EchoBubbleColor
{
Red,
Orange,
Yellow,
Green,
Cyan,
Blue,
Purple
}
internal static class EchoBubbleColors
{
public static EchoBubbleColor Parse(string value)
{
EchoBubbleColor result;
return (!Enum.TryParse<EchoBubbleColor>(value, ignoreCase: true, out result)) ? EchoBubbleColor.Orange : result;
}
public static Color LightColor(EchoBubbleColor color)
{
//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_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//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)
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
if (1 == 0)
{
}
Color result = (Color)(color switch
{
EchoBubbleColor.Red => new Color(1f, 0.22f, 0.22f),
EchoBubbleColor.Yellow => new Color(1f, 0.86f, 0.2f),
EchoBubbleColor.Green => new Color(0.32f, 0.95f, 0.38f),
EchoBubbleColor.Cyan => new Color(0.2f, 0.86f, 1f),
EchoBubbleColor.Blue => new Color(0.3f, 0.52f, 1f),
EchoBubbleColor.Purple => new Color(0.78f, 0.38f, 1f),
_ => new Color(1f, 0.72f, 0.22f),
});
if (1 == 0)
{
}
return result;
}
public static string ResourceName(EchoBubbleColor color)
{
if (1 == 0)
{
}
string text = color switch
{
EchoBubbleColor.Red => "echo-chat-red.png",
EchoBubbleColor.Yellow => "echo-chat-yellow.png",
EchoBubbleColor.Green => "echo-chat-green.png",
EchoBubbleColor.Cyan => "echo-chat-cyan.png",
EchoBubbleColor.Blue => "echo-chat-blue.png",
EchoBubbleColor.Purple => "echo-chat-purple.png",
_ => "echo-chat.png",
};
if (1 == 0)
{
}
string text2 = text;
return "TemporalEchoes.Assets." + text2;
}
}
internal static class EchoContentFilter
{
public static readonly string[] SensitiveWords = new string[26]
{
"fuck", "porn", "ponr", "dick", "vagina", "NSFW", "ejaculation", "nigger", "nigga", "faggot",
"retard", "kike", "chink", "spic", "password", "passwd", "pwd", "token", "api_key", "apikey",
"secret", "private_key", "discord.gg", "http://", "https://", "www."
};
public static bool TryFindSensitiveWord(string text, out string word)
{
if (text == null)
{
text = string.Empty;
}
string[] sensitiveWords = SensitiveWords;
foreach (string text2 in sensitiveWords)
{
if (text.IndexOf(text2, StringComparison.OrdinalIgnoreCase) >= 0)
{
word = text2;
return true;
}
}
word = string.Empty;
return false;
}
}
internal static class EchoLimits
{
public const int MaxAuthorNameLength = 50;
public const int MaxEchoBodyLength = 180;
}
public class LevelEchoFile
{
public string levelBarcode { get; set; } = string.Empty;
public string levelHash { get; set; } = string.Empty;
public string levelTitle { get; set; } = string.Empty;
public List<EchoMessage> messages { get; set; } = new List<EchoMessage>();
}
public class EchoMessage
{
public string uuid { get; set; } = string.Empty;
public string body { get; set; } = string.Empty;
public SerializableVector3 position { get; set; } = new SerializableVector3();
public string createdAt { get; set; } = string.Empty;
public string steamId { get; set; } = string.Empty;
public string authorName { get; set; } = string.Empty;
public string authorHash { get; set; } = string.Empty;
public string color { get; set; } = string.Empty;
public string syncState { get; set; } = "pending";
}
public class SerializableVector3
{
public float x { get; set; }
public float y { get; set; }
public float z { get; set; }
public static SerializableVector3 From(Vector3 value)
{
//IL_0007: 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_0021: Unknown result type (might be due to invalid IL or missing references)
return new SerializableVector3
{
x = value.x,
y = value.y,
z = value.z
};
}
public Vector3 ToVector3()
{
//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)
return new Vector3(x, y, z);
}
}
public class EchoUpload
{
public string levelBarcode { get; set; } = string.Empty;
public string levelHash { get; set; } = string.Empty;
public EchoMessage message { get; set; }
}
public class EchoDeleteRequest
{
public string levelBarcode { get; set; } = string.Empty;
public string uuid { get; set; } = string.Empty;
public string authorSecret { get; set; } = string.Empty;
}
internal class EchoMarker
{
public EchoMessage Message;
public GameObject Root;
public Vector3 BasePosition;
public float FloatPhase;
public float LastNotifyTime;
}
internal static class EchoPreferences
{
private const string CategoryName = "TemporalEchoes";
public const string DefaultAuthorName = "NullMan";
private static MelonPreferences_Category _category;
private static MelonPreferences_Entry<bool> _modEnabled;
private static MelonPreferences_Entry<bool> _debugInfo;
private static MelonPreferences_Entry<string> _authorName;
private static MelonPreferences_Entry<string> _authorSecret;
private static MelonPreferences_Entry<string> _bubbleColor;
public static bool ModEnabled => _modEnabled?.Value ?? true;
public static bool DebugInfo => _debugInfo?.Value ?? false;
public static string AuthorName => _authorName?.Value ?? "NullMan";
public static string AuthorSecret => _authorSecret?.Value ?? string.Empty;
public static EchoBubbleColor BubbleColor => EchoBubbleColors.Parse(_bubbleColor?.Value);
public static void Setup()
{
_category = MelonPreferences.CreateCategory("TemporalEchoes", "Temporal Echoes");
_modEnabled = _category.CreateEntry<bool>("ModEnabled", true, "Enable Mod", (string)null, false, false, (ValueValidator)null, (string)null);
_debugInfo = _category.CreateEntry<bool>("DebugInfo", false, "Debug Info", (string)null, false, false, (ValueValidator)null, (string)null);
_authorName = _category.CreateEntry<string>("AuthorName", "NullMan", "Your Name", (string)null, false, false, (ValueValidator)null, (string)null);
_authorSecret = _category.CreateEntry<string>("AuthorSecret", string.Empty, "Author Secret", (string)null, false, false, (ValueValidator)null, (string)null);
_bubbleColor = _category.CreateEntry<string>("BubbleColor", EchoBubbleColor.Orange.ToString(), "Bubble Color", (string)null, false, false, (ValueValidator)null, (string)null);
if (string.IsNullOrWhiteSpace(_authorSecret.Value))
{
_authorSecret.Value = Guid.NewGuid().ToString("N") + Guid.NewGuid().ToString("N");
MelonPreferences.Save();
}
}
public static void SetModEnabled(bool value)
{
_modEnabled.Value = value;
MelonPreferences.Save();
}
public static void SetDebugInfo(bool value)
{
_debugInfo.Value = value;
MelonPreferences.Save();
}
public static void SetAuthorName(string value)
{
_authorName.Value = EchoText.Limit(value ?? string.Empty, 50);
MelonPreferences.Save();
}
public static void SetBubbleColor(Enum value)
{
_bubbleColor.Value = ((EchoBubbleColor)(object)value).ToString();
MelonPreferences.Save();
}
}
internal sealed class EchoRenderer
{
private const float FloatAmplitude = 0.12f;
private const float FloatSpeed = 1.4f;
private const float SpinSpeed = 55f;
private const float TouchRadius = 0.35f;
private const float NotifyCooldownSeconds = 6f;
private readonly Func<bool> _isEnabled;
private readonly Action<EchoMessage> _onEchoTouched;
private readonly Action<string> _logWarning;
private readonly List<EchoMarker> _markers = new List<EchoMarker>();
private readonly Dictionary<EchoBubbleColor, Texture2D> _textures = new Dictionary<EchoBubbleColor, Texture2D>();
private readonly Dictionary<EchoBubbleColor, Sprite> _sprites = new Dictionary<EchoBubbleColor, Sprite>();
private float _lastNotifyTime = -100f;
public EchoRenderer(Func<bool> isEnabled, Action<EchoMessage> onEchoTouched, Action<string> logWarning)
{
_isEnabled = isEnabled;
_onEchoTouched = onEchoTouched;
_logWarning = logWarning;
}
public void LoadIcons()
{
foreach (EchoBubbleColor value in Enum.GetValues(typeof(EchoBubbleColor)))
{
Texture2D val = LoadEmbeddedTexture(EchoBubbleColors.ResourceName(value));
if (!((Object)(object)val == (Object)null))
{
_textures[value] = val;
_sprites[value] = CreateIconSprite(val);
}
}
}
public void RenderLevel(LevelEchoFile file)
{
Clear();
if (!_isEnabled() || file?.messages == null)
{
return;
}
foreach (EchoMessage message in file.messages)
{
Render(message);
}
}
public void Render(EchoMessage echo)
{
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
if (_isEnabled())
{
EchoBubbleColor echoBubbleColor = EchoBubbleColors.Parse(echo.color);
GameObject val = new GameObject("Temporal Echo " + echo.uuid);
val.transform.position = echo.position.ToVector3();
val.transform.localScale = Vector3.one * 0.5f;
SpriteRenderer val2 = val.AddComponent<SpriteRenderer>();
val2.sprite = GetIconSprite(echoBubbleColor);
val2.color = Color.white;
((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0;
((Renderer)val2).receiveShadows = false;
Material val3 = CreateSpriteMaterial(GetIconTexture(echoBubbleColor));
if ((Object)(object)val3 != (Object)null)
{
((Renderer)val2).material = val3;
}
AddGlowLight(val.transform, echoBubbleColor);
_markers.Add(new EchoMarker
{
Message = echo,
Root = val,
BasePosition = val.transform.position,
FloatPhase = Random.Range(0f, (float)Math.PI * 2f),
LastNotifyTime = -100f
});
}
}
public void Clear()
{
foreach (EchoMarker marker in _markers)
{
if ((Object)(object)marker.Root != (Object)null)
{
Object.Destroy((Object)(object)marker.Root);
}
}
_markers.Clear();
}
public void Update()
{
if (_isEnabled() && _markers.Count != 0)
{
Animate();
CheckTouches();
}
}
private void Animate()
{
//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_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
float time = Time.time;
foreach (EchoMarker marker in _markers)
{
if ((Object)(object)marker.Root == (Object)null)
{
continue;
}
Vector3 basePosition = marker.BasePosition;
basePosition.y += Mathf.Sin(time * 1.4f + marker.FloatPhase) * 0.12f;
marker.Root.transform.position = basePosition;
if ((Object)(object)Player.Head != (Object)null)
{
Vector3 val = marker.Root.transform.position - Player.Head.position;
if (((Vector3)(ref val)).sqrMagnitude > 0.001f)
{
marker.Root.transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up);
}
}
marker.Root.transform.Rotate(Vector3.up, 55f * Time.deltaTime, (Space)0);
}
}
private void CheckTouches()
{
//IL_0023: 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_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
Vector3? val = (((Object)(object)Player.LeftHand != (Object)null) ? new Vector3?(((Component)Player.LeftHand).transform.position) : null);
Vector3? val2 = (((Object)(object)Player.RightHand != (Object)null) ? new Vector3?(((Component)Player.RightHand).transform.position) : null);
float time = Time.time;
if (time - _lastNotifyTime < 6f)
{
return;
}
foreach (EchoMarker marker in _markers)
{
if (!((Object)(object)marker.Root == (Object)null))
{
Vector3 position = marker.Root.transform.position;
if ((val.HasValue && Vector3.Distance(val.Value, position) < 0.35f) || (val2.HasValue && Vector3.Distance(val2.Value, position) < 0.35f))
{
marker.LastNotifyTime = time;
_lastNotifyTime = time;
_onEchoTouched(marker.Message);
break;
}
}
}
}
private static Material CreateSpriteMaterial(Texture2D texture)
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Expected O, but got Unknown
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
Shader val = Shader.Find("Sprites/Default");
if ((Object)(object)val == (Object)null)
{
val = Shader.Find("UI/Default");
}
if ((Object)(object)val == (Object)null)
{
val = Shader.Find("Unlit/Transparent");
}
if ((Object)(object)val == (Object)null)
{
return null;
}
Material val2 = new Material(val);
val2.mainTexture = (Texture)(object)texture;
val2.SetTexture("_MainTex", (Texture)(object)texture);
val2.SetColor("_Color", Color.white);
val2.renderQueue = 3000;
return val2;
}
private static void AddGlowLight(Transform parent, EchoBubbleColor bubbleColor)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Expected O, but got Unknown
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("Echo Glow");
val.transform.SetParent(parent, false);
val.transform.localPosition = Vector3.zero;
Light val2 = val.AddComponent<Light>();
val2.type = (LightType)2;
val2.color = EchoBubbleColors.LightColor(bubbleColor);
val2.range = 0.85f;
val2.intensity = 0.8f;
val2.shadows = (LightShadows)0;
}
private Texture2D GetIconTexture(EchoBubbleColor color)
{
if (_textures.TryGetValue(color, out var value))
{
return value;
}
Texture2D value2;
return _textures.TryGetValue(EchoBubbleColor.Orange, out value2) ? value2 : null;
}
private Sprite GetIconSprite(EchoBubbleColor color)
{
if (_sprites.TryGetValue(color, out var value))
{
return value;
}
Sprite value2;
return _sprites.TryGetValue(EchoBubbleColor.Orange, out value2) ? value2 : null;
}
private Texture2D LoadEmbeddedTexture(string resourceName)
{
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: 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)
//IL_0070: Expected O, but got Unknown
using Stream stream = typeof(EchoRenderer).Assembly.GetManifestResourceStream(resourceName);
if (stream == null)
{
_logWarning("Embedded icon not found: " + resourceName);
return null;
}
byte[] array = new byte[stream.Length];
stream.Read(array, 0, array.Length);
Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
{
name = resourceName,
hideFlags = (HideFlags)32
};
if (!ImageConversion.LoadImage(val, Il2CppStructArray<byte>.op_Implicit(array)))
{
_logWarning("Failed to load embedded icon: " + resourceName);
return null;
}
val.Apply();
return val;
}
private static Sprite CreateIconSprite(Texture2D texture)
{
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
Sprite val = Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), Vector2.one * 0.5f, 1000f);
((Object)val).hideFlags = (HideFlags)32;
return val;
}
}
internal static class EchoText
{
public static string Limit(string value, int maxLength)
{
if (value == null)
{
value = string.Empty;
}
return (value.Length <= maxLength) ? value : value.Substring(0, maxLength);
}
}
}