RUMBLE does not support other mod managers. If you want to use a manager, you must use the RUMBLE Mod Manager, a manager specifically designed for this game.
Decompiled source of RallyXAutoSplitter v1.0.0
Mods\RallyXAutoSplitter.dll
Decompiled 11 hours agousing System; using System.Diagnostics; using System.IO; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using HarmonyLib; using Il2CppRUMBLE.Environment.Minigames; using MelonLoader; using Microsoft.CodeAnalysis; using RallyXAutoSplitter; using RallyXAutoSplitter.Adapters; using RallyXAutoSplitter.Services; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Mod), "RallyXAutoSplitter", "1.0.0", "ACutiePi", null)] [assembly: MelonGame("Buckethead Entertainment", "RUMBLE")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("RallyXAutoSplitter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+74ae05adf6839148e184688c55b0f807d2a09f1e")] [assembly: AssemblyProduct("RallyXAutoSplitter")] [assembly: AssemblyTitle("RallyXAutoSplitter")] [assembly: AssemblyVersion("1.0.0.0")] 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; } } } namespace RallyXAutoSplitter { public class Mod : MelonMod { public static Instance Logger { get; private set; } public static AutoSplitterService Service { get; private set; } public static LiveSplitService LiveSplitService { get; private set; } public override void OnInitializeMelon() { Logger = ((MelonBase)this).LoggerInstance; MelonLoggerAdapter logger = new MelonLoggerAdapter(((MelonBase)this).LoggerInstance); TcpClientFactory tcpFactory = new TcpClientFactory(); LiveSplitService = new LiveSplitService(logger, tcpFactory); Service = new AutoSplitterService(LiveSplitService, logger); Logger.Msg($"Connected to LiveSplit Server: {LiveSplitService.EnsureConnected()}"); ((MelonBase)this).HarmonyInstance.PatchAll(); } } } namespace RallyXAutoSplitter.Services { public sealed class AutoSplitterService { private readonly ILogger _logger; private readonly ILiveSplitService _livesplit; private bool _runActive; private short _expectedTrack; public AutoSplitterService(ILiveSplitService livesplit, ILogger logger) { _logger = logger; _livesplit = livesplit; } public void RaceStarted(RockRace race) { RockRaceAdapter race2 = new RockRaceAdapter(race); RaceStarted((IRaceData)race2); } public void RaceStarted(IRaceData race) { short selectedTrackIndex = race.SelectedTrackIndex; if (race.SelectedLapCount != 3) { return; } switch (selectedTrackIndex) { case 0: if (_expectedTrack == 0) { _runActive = true; _expectedTrack = 1; _livesplit.ResetRun(); _livesplit.StartTimer(); _logger.Msg("RaceStarted: Track 0 - Timer started"); } break; case 1: case 2: case 3: case 4: if (_runActive) { if (selectedTrackIndex != _expectedTrack) { _logger.Warning($"Ignoring unexpected track {selectedTrackIndex}, expected {_expectedTrack}"); } else { _expectedTrack++; _livesplit.Split(); _logger.Msg($"RaceStarted: Track {selectedTrackIndex} - Split"); } } break; } } public void RaceEnded(RockRace race, short winningPlayer) { RockRaceAdapter race2 = new RockRaceAdapter(race); RaceEnded((IRaceData)race2, winningPlayer); } public void RaceEnded(IRaceData race, short winningPlayer) { if (winningPlayer == -1) { _livesplit.ResetRun(); ClearRunState(); _logger.Msg("RaceEnded: Detected reset."); } else if (_runActive && race.SelectedTrackIndex == 4 && race.SelectedLapCount == 3) { if (_expectedTrack == 5) { _livesplit.Split(); _logger.Msg("RaceEnded: Track 4 completed - Final split."); } else { _logger.Warning($"Track 4 completed but expected track state was {_expectedTrack}"); } ClearRunState(); } } private void ClearRunState() { _runActive = false; _expectedTrack = 0; _logger.Msg("State cleared."); } } public interface ILiveSplitService { void StartTimer(); void Split(); void ResetRun(); } public interface ITcpClientFactory { ITcpClientWrapper CreateClient(); } public interface ITcpClientWrapper : IDisposable { bool Connected { get; } void Connect(string host, int port); Stream GetStream(); } public class LiveSplitService : ILiveSplitService, IDisposable { private readonly ILogger _logger; private readonly ITcpClientFactory _tcpFactory; private ITcpClientWrapper? _client; private StreamWriter? _writer; private bool _hasWarnedConnectionFailure; public LiveSplitService(ILogger logger, ITcpClientFactory tcpFactory) { _logger = logger; _tcpFactory = tcpFactory; } public void StartTimer() { _logger.Msg("[AUTOSPLITTER] START TIMER"); Send("starttimer"); } public void Split() { _logger.Msg("[AUTOSPLITTER] SPLIT"); Send("split"); } public void ResetRun() { _logger.Msg("[AUTOSPLITTER] RESET"); Send("reset"); } private void Send(string command) { if (!EnsureConnected()) { return; } try { _logger.Msg("-> LiveSplit: " + command); _writer.WriteLine(command); } catch (Exception ex) { _logger.Warning("Failed sending '" + command + "': " + ex.Message); } } public bool EnsureConnected() { try { ITcpClientWrapper? client = _client; if (client != null && client.Connected) { return true; } _client?.Dispose(); _client = _tcpFactory.CreateClient(); _client.Connect("127.0.0.1", 16834); _writer = new StreamWriter(_client.GetStream()) { AutoFlush = true }; if (_hasWarnedConnectionFailure) { _logger.Msg("Connected to LiveSplit."); } _hasWarnedConnectionFailure = false; return true; } catch { if (!_hasWarnedConnectionFailure) { _logger.Warning("Could not connect to LiveSplit. Is the TCP server running?"); _hasWarnedConnectionFailure = true; } return false; } } public void Dispose() { GC.SuppressFinalize(this); _writer?.Dispose(); _client?.Dispose(); } } } namespace RallyXAutoSplitter.Patches { [HarmonyPatch(typeof(ParkMinigame), "StartMinigame")] public static class RockRaceStartPatch { internal static void Postfix(RockRace __instance) { Mod.Service.RaceStarted(__instance); } } [HarmonyPatch(typeof(ParkMinigame), "OnMiniGameEnded")] public static class OnMiniGameEndedPatch { public static void Postfix(RockRace __instance, short winningPlayer) { Mod.Service.RaceEnded(__instance, winningPlayer); } } } namespace RallyXAutoSplitter.Adapters { public interface ILogger { void Msg(string message); void Warning(string message); void Error(string message); } public class MelonLoggerAdapter : ILogger { private readonly Instance _logger; public MelonLoggerAdapter(Instance logger) { _logger = logger; } public void Msg(string message) { _logger.Msg(message); } public void Warning(string message) { _logger.Warning(message); } public void Error(string message) { _logger.Error(message); } } public interface IRaceData { short SelectedTrackIndex { get; } short SelectedLapCount { get; } } public class RockRaceAdapter : IRaceData { private readonly RockRace _race; public short SelectedTrackIndex => _race.selectedTrackIndex; public short SelectedLapCount => _race.selectedLapCount; public RockRaceAdapter(RockRace race) { _race = race; } } public class TcpClientFactory : ITcpClientFactory { public ITcpClientWrapper CreateClient() { return new TcpClientWrapper(new TcpClient()); } } public class TcpClientWrapper : ITcpClientWrapper, IDisposable { private readonly TcpClient _client; public bool Connected => _client.Connected; public TcpClientWrapper(TcpClient client) { _client = client; } public void Connect(string host, int port) { _client.Connect(host, port); } public Stream GetStream() { return _client.GetStream(); } public void Dispose() { _client?.Dispose(); } } }