using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using PTV.NetcodePatcher;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Video;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("PTV")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PTV")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b31351bc-9f05-4a83-ab84-20ca395fc6df")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: NetcodePatchedAssembly]
internal class <Module>
{
static <Module>()
{
}
}
namespace PTV
{
[BepInPlugin("Gavinboy3000.PTV", "PTV", "1.0.0")]
public class PTV : BaseUnityPlugin
{
public const string modGUID = "Gavinboy3000.PTV";
public const string modName = "PTV";
public const string modVersion = "1.0.0";
public static ConfigEntry<bool> configBeginWithIntro;
public static ConfigEntry<bool> configTVInShop;
private readonly Harmony harmony = new Harmony("Gavinboy3000.PTV");
public static ManualLogSource logger = new ManualLogSource("Gavinboy3000.PTV");
public static GameObject networkPrefab;
private void Awake()
{
configBeginWithIntro = ((BaseUnityPlugin)this).Config.Bind<bool>("Misc", "Begin With Intro", true, "Puts main intro at the beginning of every shuffle.");
configTVInShop = ((BaseUnityPlugin)this).Config.Bind<bool>("QOL", "TV in Shop", true, "Makes it so the television is always in the shop.");
NetcodePatcher();
logger = ((BaseUnityPlugin)this).Logger;
string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "asset");
AssetBundle val = AssetBundle.LoadFromFile(text);
networkPrefab = val.LoadAsset<GameObject>("Assets/NetworkHandler.prefab");
networkPrefab.AddComponent<NetworkHandler>();
VideoManager.Load(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location));
harmony.PatchAll();
logger.LogInfo((object)"PTV version 1.0.0 has loaded!");
}
private static void NetcodePatcher()
{
Type[] types = Assembly.GetExecutingAssembly().GetTypes();
Type[] array = types;
foreach (Type type in array)
{
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic);
MethodInfo[] array2 = methods;
foreach (MethodInfo methodInfo in array2)
{
object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false);
if (customAttributes.Length != 0)
{
methodInfo.Invoke(null, null);
}
}
}
}
}
[HarmonyPatch(typeof(TVScript))]
internal static class TVScriptPatch
{
private static MethodInfo setMatMethod = typeof(TVScript).GetMethod("SetTVScreenMaterial", BindingFlags.Instance | BindingFlags.NonPublic);
private static MethodInfo onEnableMethod = typeof(TVScript).GetMethod("OnEnable", BindingFlags.Instance | BindingFlags.NonPublic);
private static TVScript tv;
private static RenderTexture renderTexture;
private static VideoPlayer currentVP;
private static VideoPlayer nextVP;
private static int currentIndex = -1;
private static int nextIndex = 0;
private static double forceTime = -1.0;
[HarmonyPrefix]
[HarmonyPatch("Update")]
public static bool Update(TVScript __instance)
{
tv = __instance;
if ((Object)(object)currentVP != (Object)null)
{
return false;
}
currentVP = ((Component)tv).GetComponent<VideoPlayer>();
renderTexture = currentVP.targetTexture;
if (VideoManager.Videos.Count < 1)
{
return false;
}
if (!NetworkHandler.Hosting())
{
return false;
}
TurnTVOnOff(tv, on: false);
VideoManager.ShuffledVideos = new List<string>(VideoManager.Videos);
VideoManager.Shuffle();
currentIndex = -1;
nextIndex = 0;
PrepareNextVideo(tv);
return false;
}
[HarmonyPrefix]
[HarmonyPatch("TurnTVOnOff")]
public static bool TurnTVOnOff(TVScript __instance, bool on)
{
tv = __instance;
if (VideoManager.Videos.Count < 1)
{
return false;
}
tv.tvOn = on;
if (on)
{
PlayNextVideo();
tv.tvSFX.PlayOneShot(tv.switchTVOn);
WalkieTalkie.TransmitOneShotAudio(tv.tvSFX, tv.switchTVOn, 1f);
}
else
{
tv.video.Stop();
tv.tvSFX.PlayOneShot(tv.switchTVOff);
WalkieTalkie.TransmitOneShotAudio(tv.tvSFX, tv.switchTVOff, 1f);
}
setMatMethod.Invoke(tv, new object[1] { on });
return false;
}
[HarmonyPrefix]
[HarmonyPatch("TVFinishedClip")]
public static bool TVFinishedClip(TVScript __instance, VideoPlayer source)
{
tv = __instance;
PlayNextVideo();
return false;
}
[HarmonyPostfix]
[HarmonyPatch(typeof(Terminal), "RotateShipDecorSelection")]
public static void AddTV(Terminal __instance)
{
if (!PTV.configTVInShop.Value)
{
return;
}
int num = -1;
for (int i = 0; i < StartOfRound.Instance.unlockablesList.unlockables.Count; i++)
{
if (StartOfRound.Instance.unlockablesList.unlockables[i].unlockableName == "Television")
{
num = i;
break;
}
}
if (num != -1)
{
TerminalNode shopSelectionNode = StartOfRound.Instance.unlockablesList.unlockables[num].shopSelectionNode;
if (!__instance.ShipDecorSelection.Contains(shopSelectionNode))
{
__instance.ShipDecorSelection.Add(shopSelectionNode);
}
}
}
private static void PrepareNextVideo(TVScript tv)
{
if ((Object)(object)nextVP != (Object)null && ((Component)nextVP).gameObject.activeInHierarchy)
{
Object.Destroy((Object)(object)nextVP);
}
nextVP = ((Component)tv).gameObject.AddComponent<VideoPlayer>();
nextVP.playOnAwake = false;
nextVP.source = (VideoSource)1;
nextVP.controlledAudioTrackCount = 1;
nextVP.audioOutputMode = (VideoAudioOutputMode)1;
nextVP.SetTargetAudioSource((ushort)0, tv.tvSFX);
nextVP.skipOnDrop = true;
if (NetworkHandler.Hosting())
{
nextVP.url = "file://" + VideoManager.ShuffledVideos[nextIndex];
}
else
{
nextVP.url = "file://" + VideoManager.Videos[nextIndex];
}
nextVP.Prepare();
}
private static void PlayNextVideo(int index = -1)
{
if (!((Object)(object)nextVP == (Object)null))
{
VideoPlayer val = currentVP;
tv.video = (currentVP = nextVP);
nextVP = null;
Object.Destroy((Object)(object)val);
onEnableMethod.Invoke(tv, new object[0]);
currentIndex = nextIndex;
if (NetworkHandler.Hosting())
{
nextIndex = VideoManager.NextIndex(currentIndex);
}
else if (index != -1)
{
nextIndex = index;
}
if (NetworkHandler.Hosting())
{
UpdateInfo();
}
tv.video.targetTexture = renderTexture;
tv.video.Play();
PrepareNextVideo(tv);
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(NetworkManager), "SetSingleton")]
public static void Init()
{
NetworkManager.Singleton.OnClientConnectedCallback += ClientConnected;
}
private static void ClientConnected(ulong id)
{
if (NetworkHandler.Hosting() && id != 0)
{
if (tv.tvOn)
{
UpdateInfo(1);
}
else
{
UpdateInfo(0);
}
}
}
private static void ForceTime(VideoPlayer source)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Expected O, but got Unknown
if (forceTime != -1.0)
{
source.time = forceTime;
source.prepareCompleted -= new EventHandler(ForceTime);
forceTime = -1.0;
}
}
public static void RecievedInfo(int current, double time, int next, int tvOn)
{
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_010b: Expected O, but got Unknown
if (NetworkHandler.Hosting())
{
return;
}
switch (tvOn)
{
case 0:
TurnTVOnOff(tv, on: false);
break;
case 1:
nextIndex = current;
PrepareNextVideo(tv);
TurnTVOnOff(tv, on: true);
break;
}
if (currentIndex != current)
{
if (tv.tvOn)
{
nextIndex = current;
PrepareNextVideo(tv);
PlayNextVideo(next);
}
else
{
currentIndex = current;
nextIndex = next;
PrepareNextVideo(tv);
}
}
else if (nextIndex != next)
{
nextIndex = next;
PrepareNextVideo(tv);
}
double num = time - currentVP.time;
if (Mathf.Abs((float)num) >= 1f)
{
forceTime = time;
currentVP.prepareCompleted += new EventHandler(ForceTime);
}
}
private static void UpdateInfo(int tvOn = -1)
{
NetworkHandler.instance.UpdateInfoClientRpc(VideoManager.GetTrueIndex(currentIndex), currentVP.time, VideoManager.GetTrueIndex(nextIndex), tvOn);
}
}
internal static class VideoManager
{
public static List<string> Videos = new List<string>();
public static List<string> ShuffledVideos;
public static void Load(string basePath)
{
string text = Path.Combine(basePath, "Videos");
if (!Directory.Exists(basePath))
{
return;
}
string[] files;
if (!Directory.Exists(text))
{
files = Directory.GetFiles(basePath, "*.mp4");
if (files.Length < 1)
{
return;
}
Directory.CreateDirectory(text);
for (int i = 0; i < files.Length; i++)
{
string destFileName = Path.Combine(text, Path.GetFileName(files[i]));
File.Move(files[i], destFileName);
}
}
files = Directory.GetFiles(text, "*.mp4");
Videos.AddRange(files);
PTV.logger.LogInfo((object)$"Successfully loaded {Videos.Count} videos!");
}
public static void Shuffle()
{
int num = ShuffledVideos.Count;
while (num > 1)
{
num--;
int index = Random.Range(0, num + 1);
string value = ShuffledVideos[index];
ShuffledVideos[index] = ShuffledVideos[num];
ShuffledVideos[num] = value;
}
if (PTV.configBeginWithIntro.Value)
{
string text = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Videos", "MainIntro.mp4");
int num2 = ShuffledVideos.IndexOf(text);
if (num2 != -1)
{
string value2 = ShuffledVideos[0];
ShuffledVideos[0] = text;
ShuffledVideos[num2] = value2;
}
}
}
public static int GetTrueIndex(int index)
{
if (index < 0)
{
index = 0;
}
int num = Videos.IndexOf(ShuffledVideos[index]);
if (num == -1)
{
return index;
}
return num;
}
public static int NextIndex(int index)
{
if (index + 1 >= Videos.Count)
{
Shuffle();
}
return (index + 1) % Videos.Count;
}
}
[HarmonyPatch]
public class NetworkObjectManager
{
[HarmonyPostfix]
[HarmonyPatch(typeof(GameNetworkManager), "Start")]
private static void Init(GameNetworkManager __instance)
{
((Component)__instance).GetComponent<NetworkManager>().AddNetworkPrefab(PTV.networkPrefab);
}
[HarmonyPostfix]
[HarmonyPatch(typeof(StartOfRound), "Awake")]
private static void SpawnNetworkHandler()
{
if (NetworkHandler.Hosting())
{
GameObject val = Object.Instantiate<GameObject>(PTV.networkPrefab);
val.GetComponent<NetworkObject>().Spawn(false);
}
}
}
public class NetworkHandler : NetworkBehaviour
{
public static NetworkHandler instance;
public static bool Hosting()
{
return NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer;
}
public override void OnNetworkSpawn()
{
instance = this;
((NetworkBehaviour)this).OnNetworkSpawn();
}
[ClientRpc]
public void UpdateInfoClientRpc(int current, double time, int next, int tvOn)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Invalid comparison between Unknown and I4
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Invalid comparison between Unknown and I4
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: 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_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: 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)
NetworkManager networkManager = ((NetworkBehaviour)this).NetworkManager;
if (networkManager != null && networkManager.IsListening)
{
if ((int)base.__rpc_exec_stage != 2 && (networkManager.IsServer || networkManager.IsHost))
{
ClientRpcParams val = default(ClientRpcParams);
FastBufferWriter val2 = ((NetworkBehaviour)this).__beginSendClientRpc(2660303476u, val, (RpcDelivery)0);
BytePacker.WriteValueBitPacked(val2, current);
((FastBufferWriter)(ref val2)).WriteValueSafe<double>(ref time, default(ForPrimitives));
BytePacker.WriteValueBitPacked(val2, next);
BytePacker.WriteValueBitPacked(val2, tvOn);
((NetworkBehaviour)this).__endSendClientRpc(ref val2, 2660303476u, val, (RpcDelivery)0);
}
if ((int)base.__rpc_exec_stage == 2 && (networkManager.IsClient || networkManager.IsHost))
{
TVScriptPatch.RecievedInfo(current, time, next, tvOn);
}
}
}
protected override void __initializeVariables()
{
((NetworkBehaviour)this).__initializeVariables();
}
[RuntimeInitializeOnLoadMethod]
internal static void InitializeRPCS_NetworkHandler()
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Expected O, but got Unknown
NetworkManager.__rpc_func_table.Add(2660303476u, new RpcReceiveHandler(__rpc_handler_2660303476));
}
private static void __rpc_handler_2660303476(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams)
{
//IL_0023: 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)
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
NetworkManager networkManager = target.NetworkManager;
if (networkManager != null && networkManager.IsListening)
{
int current = default(int);
ByteUnpacker.ReadValueBitPacked(reader, ref current);
double time = default(double);
((FastBufferReader)(ref reader)).ReadValueSafe<double>(ref time, default(ForPrimitives));
int next = default(int);
ByteUnpacker.ReadValueBitPacked(reader, ref next);
int tvOn = default(int);
ByteUnpacker.ReadValueBitPacked(reader, ref tvOn);
target.__rpc_exec_stage = (__RpcExecStage)2;
((NetworkHandler)(object)target).UpdateInfoClientRpc(current, time, next, tvOn);
target.__rpc_exec_stage = (__RpcExecStage)0;
}
}
protected internal override string __getTypeName()
{
return "NetworkHandler";
}
}
}
namespace PTV.NetcodePatcher
{
[AttributeUsage(AttributeTargets.Module)]
internal class NetcodePatchedAssemblyAttribute : Attribute
{
}
}