From ad77d042de8c1a1f5ff72f3d913e0a5bcbf0cd0e Mon Sep 17 00:00:00 2001 From: framewrk Date: Sat, 19 Sep 2026 15:50:11 +0100 Subject: [PATCH] simplified the README and split parts of it into docs --- README.md | 280 +-------------------------------- docs/common_issues.md | 35 +++++ docs/snippets.md | 57 +++++++ Global_TODO.md => docs/todo.md | 0 4 files changed, 96 insertions(+), 276 deletions(-) create mode 100644 docs/common_issues.md create mode 100644 docs/snippets.md rename Global_TODO.md => docs/todo.md (100%) diff --git a/README.md b/README.md index 36e4cff..804ffa6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Monopoly Poker Mod Helper -This repository contains helper code, notes, and logs for modding `Monopoly Poker`. +This repository contains mods for `Monopoly Poker`. + +Currently a WIP. ## Overview @@ -14,30 +16,6 @@ This repo is focused on reverse engineering game behavior, finding useful types - `BepInEx IL2CPP` - `dnSpy` (for inspecting game assemblies and objects) -## Repository Structure - -### `MonopolyPokerHelper/` -Main mod/helper project code. - -Example files: -- `ClaimMissionHandler.cs` - discovers and claims missions through reflection - -### Assembly-CSharp - -This is a dnSpy dump of the game's main assembly, used for reference and exploration. It is not meant to be built or modified directly. - -## What This Project Is For - -- Uses reflection to inspect game objects and types -- Automates helper actions inside the game -- Stores notes and logs for reverse engineering work - -## Notes - -- The dash character `—` may display incorrectly in the BepInEx console. -- `dnSpy` may need a fix for loading certain assemblies. -- You can use `BepInEx.ConfigurationManager` if you want an in-game configuration UI. - ## Setup 1. Open the solution in Visual Studio. @@ -53,254 +31,4 @@ This is a dnSpy dump of the game's main assembly, used for reference and explora - **BepInEx Docs**: - **Harmony Docs**: -Note UnityExplorer is archived, need to use a fork. - -## Known Issues - -### dnSpy Import Project - -When trying to load a disassembled project that was then reassembled with dnSpy into Visual Studio you'll encounter the error: - -`A numeric comparison was attempted on "$(_TargetFrameworkVersionWithoutV)" that evaluates to "" instead of a number, in condition "('$(TargetFrameworkIdentifier)' != '.NETCoreApp'` - -This is because the project file generated by dnSpy is adding a v to and as well as not including _TargetFrameworkVersionWithoutV. To fix this: - -1. Open the .csproj file in a text editor. -2. Near the top you'll see these two lines: -```xml -v5.0 -.NETCoreApp -``` - -Modify the first line (remove the "v"), and insert the second line, so it looks like this: - -```xml -5.0 -<_TargetFrameworkVersionWithoutV>5.0 -.NETCoreApp -``` - -Load the project as you were, and it should work. - -Mileage may vary with different versions of Visual Studio and .NET SDK, but this is the general fix for the issue. - -[Source](https://www.clearhat.org/post/how-fix-numeric-comparison-was-attempted-_targetframeworkversionwithoutv-evaluates-instead-number-condition-targetframeworkidentifier-netcoreapp) - -### Encoding issues in BepInEx Console - -"—" always shows as ΓòÉ on BepInEx Console. Use the regular hyphen "-" instead. - -## How to Extend This README - -Add new sections when needed: -- setup and build instructions -- file-by-file descriptions -- known issues -- update history - -## Git Stash Reminder - -to git stash staged changes, you can use the following command: - -``` -git stash push --staged -m "message" -``` - -## Modding Guide - -### Tech Stack - -#### What is BepInEx? - -**BepInEx** (Bepis Injector Extendable) is a plugin/mod loader for Unity games. It: - -- Injects itself into the game when it launches -- Loads your plugin DLLs automatically from `BepInEx/plugins/` -- Provides logging, configuration, and modding APIs -- Handles IL2CPP games (compiled to native C++ code, harder to mod than Mono) - -#### What is Harmony? - -**Harmony** is a runtime patching library that lets you modify game methods without changing the original game files: - -- **Prefix**: Run your code BEFORE the original method (can block the original) -- **Postfix**: Run your code AFTER the original method -- **Transpiler**: Modify the method's IL code directly (advanced) - -You patch methods by their type and method name - no compile-time references needed! - ---- - -## 📋 Common Patterns - -### Pattern: Periodic Timer - -```csharp -private static float _timer; -private const float INTERVAL = 10f; - -private static void UpdatePostfix() -{ - _timer += UnityEngine.Time.deltaTime; - if (_timer < INTERVAL) return; - _timer = 0f; - - // Do something every 10 seconds -} -``` - -### Pattern: Singleton Access - -```csharp -// Most game managers use the Singleton pattern -var instance = ManagerType.GetProperty("Instance").GetValue(null); -``` - -### Pattern: Safe Reflection - -```csharp -try -{ - var method = type.GetMethod("MethodName"); - if (method == null) - { - Log.LogWarning("Method not found!"); - return; - } - method.Invoke(instance, args); -} -catch (Exception ex) -{ - Log.LogError($"Reflection error: {ex.Message}"); -} -``` - -### Pattern: Patch All Overloads - -```csharp -var methods = targetType.GetMethods(BindingFlags.Public | BindingFlags.Instance); -foreach (var method in methods) -{ - if (method.Name == "TargetMethod") - { - _harmony.Patch(method, prefix: myPrefix); - } -} -``` - ---- - -## ⚠️ Common Pitfalls - -### 1. **IL2CPP Array Indexing** - -IL2CPP arrays need special handling: - -```csharp -// DON'T: arr[0] -// DO: -var getMethod = array.GetType().GetMethod("get_Item"); -var value = getMethod.Invoke(array, new object[] { 0 }); -``` - -### 2. **Timing Issues** - -Game managers may not be loaded immediately: - -```csharp -// Wait before trying to access managers -if (!_initialDelayDone) -{ - _elapsed += deltaTime; - if (_elapsed < 30f) return; // wait 30 seconds - _initialDelayDone = true; -} -``` - -### 3. **Type Name Collisions** - -Multiple assemblies may have classes with the same name: - -```csharp -// Use full namespace -FindType("YoudaGames.MonopolyPoker.Popups.RollingOffersPopup"); -``` - -### 4. **Harmony Patch Failures** - -Methods might not exist or load later: - -```csharp -try -{ - _harmony.Patch(method, prefix); -} -catch (Exception ex) -{ - Log.LogWarning($"Patch failed: {ex.Message}"); - // Method may load later - try again or check name -} -``` - ---- - -## 🎉 Example Mod Ideas - -Now that you understand the basics, try creating: - -1. **Auto Claimer** - Automatically collect daily rewards (you have this!) -2. **Speed Hack** - Patch animation methods to speed up gameplay -3. **UI Remover** - Block annoying popups (you have this!) -4. **Unlimited Resources** - Modify currency values via reflection -5. **Auto Player** - Hook game actions and make decisions automatically -6. **Visual Tweaks** - Modify Unity UI elements at runtime - ---- - -## 🐛 Debugging Tips - -### View Logs - -``` -/BepInEx/LogOutput.log -``` - -### Enable Debug Logging - -In your plugin: - -```csharp -Log.LogDebug("Debug message"); // Will show if debug level enabled -``` - -In `BepInEx/config/BepInEx.cfg`: - -```ini -[Logging.Console] -LogLevels = Fatal, Error, Warning, Message, Info, Debug -``` - - -## UnityExplorer Console Snippets - -### Dumping GameObject Information - -Replace the type with the one you need, e.g., `YoudaGames.MonopolyPoker.Popups.BoardwalkJackpotRollPopup`. - -``` -var instances = UnityEngine.Object.FindObjectsOfType(); - -foreach (var popup in instances) -{ - Console.WriteLine($"--- Begin for instance {popup} ---"); - var type = popup.GetType(); - foreach (var prop in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) - { - Console.WriteLine($"{prop.Name}: {prop.GetValue(popup)}"); - } - Console.WriteLine("--- End ---"); -} -``` - - -TODO: Perhaps seperate this README into a docs folder and link to it from the main README, to keep the main README concise and focused on project overview and setup. \ No newline at end of file +Note UnityExplorer is archived, need to use a fork. \ No newline at end of file diff --git a/docs/common_issues.md b/docs/common_issues.md new file mode 100644 index 0000000..206badf --- /dev/null +++ b/docs/common_issues.md @@ -0,0 +1,35 @@ + +# Common Issues + +## dnSpy Import Project + +When trying to load a disassembled project that was then reassembled with dnSpy into Visual Studio you'll encounter the error: + +`A numeric comparison was attempted on "$(_TargetFrameworkVersionWithoutV)" that evaluates to "" instead of a number, in condition "('$(TargetFrameworkIdentifier)' != '.NETCoreApp'` + +This is because the project file generated by dnSpy is adding a v to and as well as not including _TargetFrameworkVersionWithoutV. To fix this: + +1. Open the .csproj file in a text editor. +2. Near the top you'll see these two lines: +```xml +v5.0 +.NETCoreApp +``` + +Modify the first line (remove the "v"), and insert the second line, so it looks like this: + +```xml +5.0 +<_TargetFrameworkVersionWithoutV>5.0 +.NETCoreApp +``` + +Load the project as you were, and it should work. + +Mileage may vary with different versions of Visual Studio and .NET SDK, but this is the general fix for the issue. + +[Source](https://www.clearhat.org/post/how-fix-numeric-comparison-was-attempted-_targetframeworkversionwithoutv-evaluates-instead-number-condition-targetframeworkidentifier-netcoreapp) + +## Encoding issues in BepInEx Console + +"—" always shows as ΓòÉ on BepInEx Console. Use the regular hyphen "-" instead. \ No newline at end of file diff --git a/docs/snippets.md b/docs/snippets.md new file mode 100644 index 0000000..b52fb4b --- /dev/null +++ b/docs/snippets.md @@ -0,0 +1,57 @@ + +# Snippets + +## Periodic Timer + +```csharp +private static float _timer; +private const float INTERVAL = 10f; + +private static void UpdatePostfix() +{ + _timer += UnityEngine.Time.deltaTime; + if (_timer < INTERVAL) return; + _timer = 0f; + + // Do something every 10 seconds +} +``` + +## Singleton Access + +```csharp +// Most game managers use the Singleton pattern +var instance = ManagerType.GetProperty("Instance").GetValue(null); +``` + +## Safe Reflection + +```csharp +try +{ + var method = type.GetMethod("MethodName"); + if (method == null) + { + Log.LogWarning("Method not found!"); + return; + } + method.Invoke(instance, args); +} +catch (Exception ex) +{ + Log.LogError($"Reflection error: {ex.Message}"); +} +``` + +## Patch All Overloads + +```csharp +var methods = targetType.GetMethods(BindingFlags.Public | BindingFlags.Instance); +foreach (var method in methods) +{ + if (method.Name == "TargetMethod") + { + _harmony.Patch(method, prefix: myPrefix); + } +} +``` diff --git a/Global_TODO.md b/docs/todo.md similarity index 100% rename from Global_TODO.md rename to docs/todo.md