using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Steamworks;
using UnityEngine;
using UnityEngine.Networking;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("PlaylistFromCollection")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PlaylistFromCollection")]
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4989fb15-1650-4253-90f9-444ecbcf43a2")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace PlaylistFromCollection
{
public static class CollectionHandler
{
public static bool ValidateUrl(string url, out string errorMessage)
{
if (url.StartsWith("https://steamcommunity.com/sharedfiles/filedetails/?id="))
{
Regex regex = new Regex("^(https:\\/\\/steamcommunity\\.com\\/sharedfiles\\/filedetails\\/\\?id=)\\d*$");
if (regex.IsMatch(url))
{
errorMessage = "THERE WAS NO ERROR YIPPEE!!!";
return true;
}
errorMessage = "THIS STEAM LINK IS NOT A WORKSHOP ITEM";
return false;
}
if (url.StartsWith("https:"))
{
errorMessage = "THIS LINK IS NOT A VALID LINK";
return false;
}
if (url == "")
{
errorMessage = "YOU DIDN'T SUBMIT ANYTHING";
return false;
}
errorMessage = "CANNOT PARSE. PLEASE TRY AGAIN";
return false;
}
public static IEnumerator ParseCollection(string url)
{
MenuPanelManager MPM = G.Sys.MenuPanelManager_;
SteamworksUGC UGC = G.Sys.SteamworksManager_.UGC_;
UGC.SetupSteamProgressText("Downloading Playlist...");
UGC.progressBar_.value = 0f;
UnityWebRequest request = new UnityWebRequest(url);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.method = "GET";
yield return request.Send();
if (request.isError)
{
Mod.Log.LogError((object)"Failed to request the link!");
string error = request.error;
if (error == null)
{
error = "Unknown Error!";
}
Mod.Log.LogError((object)error);
MPM.ShowError("Failed to connect to the web page", "Error", (OnButtonClicked)null, (Pivot)4);
MPM.MenuInputEnabled_ = true;
UGC.DestroySteamProgressText();
yield break;
}
UGC.ProgressTextLabel_.text = "Parsing the web page...";
UGC.progressBar_.value = 0.5f;
Mod.Instance.collectionIDs = new Dictionary<ulong, bool>();
Mod.Instance.playlistTitle = string.Empty;
try
{
string requestHTML = request.downloadHandler.text;
if (!requestHTML.Contains("<span class=\"breadcrumb_separator\">> </span><a data-panel=\"{"noFocusRing":true}\" href=\"https://steamcommunity.com/workshop/browse/?section=collections&appid=233610\">Collections</a>"))
{
MPM.ShowError("This Workshop link is not a Distance Workshop Collection link! \nPlease submit a link that is a Distance Workshop Collection", "Error", (OnButtonClicked)null, (Pivot)4);
MPM.MenuInputEnabled_ = true;
UGC.DestroySteamProgressText();
yield break;
}
Regex MatchAllIDs = new Regex("(?<={\"id\":\")\\d*");
if (MatchAllIDs.IsMatch(requestHTML))
{
MatchCollection matchIDs = MatchAllIDs.Matches(requestHTML);
WorkshopLevelInfo wInfo = default(WorkshopLevelInfo);
foreach (Match mID in matchIDs)
{
Mod.Log.LogInfo((object)("ID Found: " + mID.Value));
bool levelInstalled = false;
try
{
levelInstalled = UGC.storedPublishedFileIDs_.TryGetWorkshopLevelInfo((ulong)Convert.ToInt64(mID.Value), ref wInfo);
if (!string.IsNullOrEmpty(wInfo.title_))
{
Mod.Log.LogInfo((object)("Level Already Installed: " + wInfo.title_));
}
}
catch (Exception)
{
Mod.Log.LogWarning((object)"Level is not already installed: Will attempt to download");
}
Mod.Instance.collectionIDs.Add((ulong)Convert.ToInt64(mID.Value), levelInstalled);
wInfo = null;
}
}
else
{
Mod.Log.LogWarning((object)"Failed to find any level IDs in the collection");
}
Regex matchTitle = new Regex("(?<=<div class=\"workshopItemTitle\">).*(?=</div>)");
if (matchTitle.IsMatch(requestHTML))
{
Match mTitle = matchTitle.Match(requestHTML);
Mod.Log.LogInfo((object)("Title: " + mTitle.Value));
Mod.Instance.playlistTitle = mTitle.Value;
}
if (Mod.Instance.collectionIDs.Count == 0)
{
MPM.ShowError("This collection has no levels!", "Error", (OnButtonClicked)null, (Pivot)4);
MPM.MenuInputEnabled_ = true;
UGC.DestroySteamProgressText();
yield break;
}
}
catch (Exception ex2)
{
Exception e = ex2;
Mod.Log.LogError((object)"Encountered an error when parsing the link!");
string error2 = e.ToString();
if (error2 == null)
{
error2 = "Unknown Error!";
}
Mod.Log.LogError((object)error2);
MPM.ShowError("Failed to parse the web page", "Error", (OnButtonClicked)null, (Pivot)4);
MPM.MenuInputEnabled_ = true;
UGC.DestroySteamProgressText();
yield break;
}
List<PublishedFileId_t> pFileIds = new List<PublishedFileId_t>();
foreach (KeyValuePair<ulong, bool> kvp in Mod.Instance.collectionIDs)
{
if (!kvp.Value)
{
Mod.Log.LogInfo((object)("Level ID to Download: " + kvp.Key));
pFileIds.Add(new PublishedFileId_t(kvp.Key));
}
}
if (pFileIds.Count > 0)
{
Mod.Log.LogInfo((object)"Beginning download process...");
UGC.StartWorkshopLevelsUpdate((WorkshopUpdateType)1, pFileIds.ToArray(), new OnPanelPop(ConvertLevelsToPlaylist), (OnPanelPop)delegate
{
MPM.ShowError("Failed to download workshop levels from the collection", "Error", (OnButtonClicked)null, (Pivot)4);
});
}
else
{
ConvertLevelsToPlaylist();
}
MPM.MenuInputEnabled_ = true;
UGC.DestroySteamProgressText();
}
private static void ConvertLevelsToPlaylist()
{
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Expected O, but got Unknown
//IL_016e: Unknown result type (might be due to invalid IL or missing references)
//IL_0175: Expected O, but got Unknown
//IL_018e: Unknown result type (might be due to invalid IL or missing references)
MenuPanelManager menuPanelManager_ = G.Sys.MenuPanelManager_;
SteamworksUGC uGC_ = G.Sys.SteamworksManager_.UGC_;
List<LevelNameAndPathPair> list = new List<LevelNameAndPathPair>();
Mod.Log.LogInfo((object)"Preparing to create playlist...");
WorkshopLevelInfo val = default(WorkshopLevelInfo);
foreach (KeyValuePair<ulong, bool> collectionID in Mod.Instance.collectionIDs)
{
uGC_.storedPublishedFileIDs_.TryGetWorkshopLevelInfo(collectionID.Key, ref val);
if (val.tags_.Contains(((object)Unsafe.As<GameModeID, GameModeID>(ref Mod.Instance.playlistGameMode)/*cast due to .constrained prefix*/).ToString()))
{
Mod.Log.LogInfo((object)("Level Title: " + val.title_ + " Path: " + Resource.GetAbsoluteLevelPath(val.relativePath_)));
list.Add(new LevelNameAndPathPair(val.title_, Resource.GetAbsoluteLevelPath(val.relativePath_)));
continue;
}
Mod.Log.LogWarning((object)("Level " + val.title_ + " is not " + ((object)Unsafe.As<GameModeID, GameModeID>(ref Mod.Instance.playlistGameMode)/*cast due to .constrained prefix*/).ToString() + " Mode. Skipping"));
}
if (list.Count > 0)
{
Mod.Log.LogInfo((object)"Creating Playlist...");
LevelSet val2 = new LevelSet();
val2.resourcesLevelNameAndPathPairsInSet_ = list;
LevelPlaylist val3 = LevelPlaylist.Create(val2, Mod.Instance.playlistTitle, Mod.Instance.playlistGameMode);
val3.Awake();
val3.Save();
menuPanelManager_.ShowError("Return to the main menu to refresh the menu list", "Playlist Installed", (OnButtonClicked)null, (Pivot)4);
}
else
{
Mod.Log.LogWarning((object)"None of the levels were the correct mode! No Playlist Created");
menuPanelManager_.ShowError("The collection had no " + ((object)Unsafe.As<GameModeID, GameModeID>(ref Mod.Instance.playlistGameMode)/*cast due to .constrained prefix*/).ToString() + " Mode levels.", "Playlist Error", (OnButtonClicked)null, (Pivot)4);
}
}
}
[BepInPlugin("Distance.PlaylistFromCollection", "Playlist From Collection", "1.0.0")]
public class Mod : BaseUnityPlugin
{
private const string modGUID = "Distance.PlaylistFromCollection";
private const string modName = "Playlist From Collection";
private const string modVersion = "1.0.0";
public string playlistTitle;
public GameModeID playlistGameMode = (GameModeID)1;
private static readonly Harmony harmony = new Harmony("Distance.PlaylistFromCollection");
public static ManualLogSource Log = new ManualLogSource("Playlist From Collection");
public static Mod Instance;
public Dictionary<ulong, bool> collectionIDs { get; set; } = new Dictionary<ulong, bool>();
private void Awake()
{
Object.DontDestroyOnLoad((Object)(object)this);
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
}
Log = Logger.CreateLogSource("Distance.PlaylistFromCollection");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Initializing Playlist From Collections...");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Loading...");
harmony.PatchAll();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded!");
}
public void OnConfigChanged(object sender, EventArgs e)
{
SettingChangedEventArgs e2 = (SettingChangedEventArgs)(object)((e is SettingChangedEventArgs) ? e : null);
if (e2 != null)
{
}
}
}
}
namespace PlaylistFromCollection.Patches
{
[HarmonyPatch(typeof(LevelGridMenu), "CreateEntries")]
internal static class LevelGridMenu__CreateEntries
{
private class AddPlaylistEntry : PlaylistEntry
{
private LevelGridMenu gridMenu;
public AddPlaylistEntry(LevelGridMenu menu, LevelPlaylist playlist)
: base(menu, "Add Playlist", playlist, (Type)0, false, (UnlockStyle)7, true)
{
gridMenu = menu;
}
public override void OnClick()
{
//IL_0008: 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_0024: Expected O, but got Unknown
//IL_0024: Expected O, but got Unknown
InputPromptPanel.Create(new OnSubmit(SearchCollectionOnSteam), new OnPop(InputPop), "Enter Playlist Link:", (string)null);
((Component)gridMenu).gameObject.SetActive(false);
}
private bool SearchCollectionOnSteam(out string errorMessage, string input)
{
if (CollectionHandler.ValidateUrl(input, out errorMessage))
{
((MonoBehaviour)Mod.Instance).StartCoroutine(CollectionHandler.ParseCollection(input));
return true;
}
return false;
}
private void InputPop()
{
((Component)gridMenu).gameObject.SetActive(true);
}
}
[HarmonyPostfix]
internal static void CustomPlaylist(LevelGridMenu __instance)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Invalid comparison between Unknown and I4
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Invalid comparison between Unknown and I4
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Invalid comparison between Unknown and I4
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Invalid comparison between Unknown and I4
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Invalid comparison between Unknown and I4
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Invalid comparison between Unknown and I4
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Invalid comparison between Unknown and I4
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Expected O, but got Unknown
//IL_010f: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0134: Unknown result type (might be due to invalid IL or missing references)
bool flag = (int)((LevelSelectMenuAbstract)__instance).displayType_ == 0 || (int)((LevelSelectMenuAbstract)__instance).displayType_ == 1;
bool flag2 = (int)((LevelSelectMenuAbstract)__instance).displayType_ == 2;
bool flag3 = (int)__instance.modeID_ == 1;
bool flag4 = (int)__instance.modeID_ == 8;
bool flag5 = (int)__instance.modeID_ == 2;
bool flag6 = (int)__instance.modeID_ == 5;
bool flag7 = (int)__instance.modeID_ == 13;
if (!flag)
{
return;
}
string[] legacyFileNames_ = G.Sys.LevelSets_.LegacyFileNames_;
List<LevelNameAndPathPair> allLevelNameAndPathPairs = G.Sys.LevelSets_.GetSet((GameModeID)1).GetAllLevelNameAndPathPairs();
LevelSet val = new LevelSet();
for (int i = 0; i < allLevelNameAndPathPairs.Count; i++)
{
string fileNameWithoutExtension = Resource.GetFileNameWithoutExtension(allLevelNameAndPathPairs[i].levelPath_);
if (legacyFileNames_.Contains(fileNameWithoutExtension))
{
val.AddLevel(allLevelNameAndPathPairs[i].levelName_, Resource.GetAbsoluteOfficialLevelPath(fileNameWithoutExtension), (LevelType)2, (string)null);
break;
}
}
if (val.ResourcesLevelsCount_ > 0)
{
__instance.AddEntry((PlaylistEntry)(object)new AddPlaylistEntry(__instance, LevelPlaylist.Create(val, "Add Playlist", __instance.modeID_)), "");
Mod.Instance.playlistGameMode = __instance.modeID_;
Mod.Log.LogInfo((object)("Mode ID: " + ((object)Unsafe.As<GameModeID, GameModeID>(ref __instance.modeID_)/*cast due to .constrained prefix*/).ToString()));
}
((UIExButtonContainer)__instance.buttonList_).SortAndUpdateVisibleButtons();
}
}
}