using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("CaptainHook")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CaptainHook")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e944f7cd-03c8-4672-bfbe-e19ac9566a48")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CaptainHookMod;
[BepInPlugin("Caenos.CaptainHook", "CaptainHook", "2.5.3")]
public class CaptainHook : BaseUnityPlugin
{
internal static ManualLogSource Logger;
private HttpListener httpListener;
private Thread httpThread;
private ConfigEntry<int> configPort;
private ConfigEntry<string> configIP;
private ConfigEntry<string> configBotName;
private static DateTime serverStartTime;
private void Awake()
{
Logger = ((BaseUnityPlugin)this).Logger;
configPort = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ServerPort", 25662, "Port used for the HTTP server.");
configIP = ((BaseUnityPlugin)this).Config.Bind<string>("General", "ServerIP", "127.0.0.1", "Public IP the Discord bot should connect to.");
configBotName = ((BaseUnityPlugin)this).Config.Bind<string>("General", "BotDisplayName", "CaptainHook", "Display name used by the bot.");
Logger.LogInfo((object)$"[{configBotName.Value}] Config loaded. Listening on {configIP.Value}:{configPort.Value}");
serverStartTime = DateTime.UtcNow;
StartHttpServer();
}
private void StartHttpServer()
{
try
{
httpListener = new HttpListener();
httpListener.Prefixes.Add($"http://*:{configPort.Value}/");
httpListener.Start();
Logger.LogInfo((object)$"[{configBotName.Value}] HTTP server listening on port {configPort.Value}.");
httpThread = new Thread(HttpServerLoop);
httpThread.IsBackground = true;
httpThread.Start();
}
catch (Exception arg)
{
Logger.LogError((object)$"[{configBotName.Value}] Failed to start HTTP server: {arg}");
}
}
private void HttpServerLoop()
{
while (httpListener != null && httpListener.IsListening)
{
try
{
HttpListenerContext context = httpListener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
string text = request.Url.AbsolutePath.Trim(new char[1] { '/' }).ToLower();
string text2 = "";
switch (text)
{
case "stats":
text2 = GeneratePlayerStats();
break;
case "ping":
text2 = "\ud83c\udfd3 Pong!";
break;
case "version":
text2 = "\ud83d\udee0\ufe0f " + configBotName.Value + " v2.5.3 — Made by Caenos";
break;
case "uptime":
text2 = $"⏱\ufe0f Uptime: {DateTime.UtcNow - serverStartTime:hh\\:mm\\:ss}";
break;
case "whereis":
text2 = FindPlayerZone(request.QueryString["name"]);
break;
case "day":
text2 = GetWorldTime();
break;
default:
response.StatusCode = 404;
text2 = "404 Not Found";
break;
}
byte[] bytes = Encoding.UTF8.GetBytes(text2);
response.ContentEncoding = Encoding.UTF8;
response.ContentType = "text/plain; charset=utf-8";
response.ContentLength64 = bytes.Length;
response.OutputStream.Write(bytes, 0, bytes.Length);
response.OutputStream.Close();
}
catch (Exception arg)
{
Logger.LogError((object)$"[{configBotName.Value}] HTTP server error: {arg}");
}
}
}
private string GeneratePlayerStats()
{
try
{
ZNet instance = ZNet.instance;
List<ZNetPeer> list = ((instance != null) ? instance.GetConnectedPeers() : null);
if (list == null || list.Count == 0)
{
return "\ud83d\udc7b No players online in the spooky Underworld.";
}
List<string> list2 = (from p in list
select p.m_playerName into name
where !string.IsNullOrEmpty(name)
select name).Distinct().ToList();
return string.Format("{0} Player(s) online in the spooky Underworld: {1}", list2.Count, string.Join(", ", list2));
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed to get player stats: {arg}");
return "⚠\ufe0f Error getting player info.";
}
}
private string FindPlayerZone(string name)
{
//IL_0051: 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_0070: 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)
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
if (string.IsNullOrEmpty(name))
{
return "⚠\ufe0f You must provide a player name.";
}
List<ZNetPeer> connectedPeers = ZNet.instance.GetConnectedPeers();
foreach (ZNetPeer item in connectedPeers)
{
if (item.m_playerName.Equals(name, StringComparison.OrdinalIgnoreCase))
{
ZDO zDO = ZDOMan.instance.GetZDO(item.m_characterID);
if (zDO != null)
{
Vector3 position = zDO.GetPosition();
Biome biome = WorldGenerator.instance.GetBiome(position);
return $"\ud83d\udee1\ufe0f {name} is currently in the {biome}.";
}
return "\ud83d\udee1\ufe0f " + name + " is online but their position is unknown.";
}
}
return "❓ Player '" + name + "' not found.";
}
private string GetWorldTime()
{
try
{
int day = EnvMan.instance.GetDay();
float dayFraction = EnvMan.instance.GetDayFraction();
float num = (float)(day - 1) * 2400f + dayFraction * 2400f;
int num2 = (int)(num % 2400f / 100f);
int num3 = (int)(num % 100f * 0.6f);
return $"\ud83d\udcc5 Day {day}, {num2:D2}:{num3:D2} in Valheim.";
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed to get world time: {arg}");
return "⚠\ufe0f Could not read in-game time.";
}
}
private void OnDestroy()
{
if (httpListener != null)
{
httpListener.Stop();
httpListener.Close();
httpListener = null;
}
httpThread?.Abort();
}
}