REPOBlacklist
BepInEx plugin for R.E.P.O. Host-side blacklist for shop items, level valuables, and enemies via an in-game F3 menu. Strips blacklisted assets from runtime List/Array/Dictionary spawn pools at scene load. Persists globally via JSON file.REPOBlacklist
A BepInEx plugin for R.E.P.O. that lets the host pick which shop items, level valuables, and enemies should never spawn. Selections are made through an in-game IMGUI menu (default hotkey F3) and persist across every save file on the install.
Only the player hosting the lobby's blacklist takes effect — shop, level valuable, and enemy selection all run host-side in REPO and replicate to clients via Photon. Non-hosts can still install the mod and use the menu; their toggles save locally and become active the next time they host.
What the DLL actually does
On plugin Awake(), it:
- Reads its config. A standard BepInEx config file (
BepInEx/config/isaia.repoblacklist.cfg) defines the menu toggle key (defaultF3). - Loads the blacklist. Reads
BepInEx/config/REPOBlacklist.json(created on first save). The file holds three sorted string lists — items, valuables, enemies — keyed by asset name (the UnityObject.nameof the underlying asset). Toggles in the menu write through to this file immediately. - Registers an IMGUI overlay. Adds a
MonoBehaviourthat draws a windowed menu while open. The menu has three tabs (Items / Valuables / Enemies), a text filter, and Refresh, Dump (logs diagnostic info to the BepInEx console), Close, and a per-tab Clear button with a two-click confirmation step. Each row is a checkbox bound to the JSON store. - Subscribes to
SceneManager.sceneLoaded. One frame after each scene loads, runs the runtime filter described below. Also re-runs the filter when the menu is closed. - Applies Harmony input-blocking patches. While the menu is open (
BlockInput == true), the following methods are short-circuited so cursor movement doesn't drive the camera and clicks don't fire weapons:- Legacy:
UnityEngine.Input.GetAxis,GetAxisRaw,GetMouseButton,GetMouseButtonDown,GetMouseButtonUp— return0f/false. - New:
UnityEngine.InputSystem.InputAction.ReadValue<T>()forVector2,float, andVector3— returndefault(T). Bound dynamically via reflection, so the plugin still loads if the new InputSystem assembly is absent. Input.GetKey/GetKeyDown/GetKeyUpare deliberately not patched, so the menu hotkey (and any other plugin hotkeys) keep working.- These patches are always installed but gated on a single static flag — they're inert when the menu is closed.
- Legacy:
Runtime filter
The filter does not Harmony-patch any REPO method. It runs once per scene load (one frame deferred) and once on menu close, and removes blacklisted assets from in-memory pool collections that the game's directors read from. Specifically:
- Type resolution (cached once per session). Walks loaded assemblies and resolves the first match for each category:
Itemfor items;ValuableObjectthenValuablefor valuables;EnemySetupthenEnemiesSetupthenEnemyParentfor enemies. The resolved type names are logged on first use asResolved types: Item=…, Valuable=…, Enemy=…. If a name can't be resolved (e.g. a future REPO rename), that category is skipped silently. - Field discovery (cached per Type). For each
MonoBehaviourtype seen on first scan, reflects over its public + non-public instance fields once and remembers any whose declared type isList<T>,T[], orDictionary<K, V>whereT(orK, orV) is assignable to a resolved category type. The same scan is done once over every type in non-external assemblies for static fields. After this one-time pass, repeat scans skipGetFieldsentirely. - Filter pass. Iterates
Object.FindObjectsOfType<MonoBehaviour>(true)(active scene + DontDestroyOnLoad + inactive, excluding prefab assets) and the cached static-field list. For each matched container, removes any element whoseUnityEngine.Object.name(or, for dictionarystringkeys, the key itself) is present in the blacklist. Lists and dictionaries are mutated in place; arrays are reallocated with the surviving entries and the field reassigned.
The directors then pick from the now-shorter pool, so blacklisted entries are simply never rolled. The approach is intentionally generic — it does not depend on knowing REPO's specific manager class names — but it does mean a blacklist only takes effect on assets reachable through one of the three supported container shapes. If a future REPO build stores a pool in a more exotic structure (a custom weighted-pool class, for example), the Dump button lists every visited field with counts, which makes the gap obvious.
External assemblies (UnityEngine.*, Unity.*, System.*, mscorlib, netstandard, BepInEx*, 0Harmony, HarmonyX*, Mono.Cecil, Microsoft.*, and the plugin itself) are skipped during type resolution and field discovery.
Install
Thunderstore Mod Manager: click Install. Manual: drop REPOBlacklist.dll into <profile>/BepInEx/plugins/REPOBlacklist/. Requires BepInEx-BepInExPack.
The blacklist file at BepInEx/config/REPOBlacklist.json is shared across every save and every lobby on that BepInEx install. Deleting it resets the blacklist to empty.
Compatibility
- Host-side gameplay effect. Only the host's blacklist filters what spawns. A non-host's blacklist does nothing visible — the host has already chosen what to spawn and replicates it via Photon. (The menu and input-blocking are local to whoever opens them, regardless of host status.)
- No Harmony patches on REPO code. Patches target only
UnityEngine.Input(legacy) andUnityEngine.InputSystem.InputAction(new). The runtime filter mutates REPO collection fields via reflection at scene load and on menu close — it does not intercept any REPO method. - No Photon / network traffic of its own. The plugin does not send or receive any custom networked messages.
- Per-install config. Blacklist edits affect only your local install. Each host manages their own list.
Changelog
0.2.2
- Removed the 5-second periodic re-apply that was added in 0.2.0; the filter now runs only on scene load and on menu close. Eliminated a recurring stutter.
- Cached per-Type instance-field match info and switched the scene scan from
Resources.FindObjectsOfTypeAlltoObject.FindObjectsOfType(true)to skip prefab assets.
0.2.1
- Added dynamic Harmony patches for
UnityEngine.InputSystem.InputAction.ReadValue<Vector2/float/Vector3>()so the new InputSystem mouse-look path is also blocked while the menu is open. Bound via reflection.
0.2.0
- Added the input-blocking layer (Harmony prefixes on legacy
UnityEngine.Inputaxis and mouse-button reads) so cursor movement no longer drives the camera while the menu is open. - Filter now walks
Dictionary<,>fields and static fields on game-assembly types, in addition to instanceList<T>/T[]fields, and includes DontDestroyOnLoad and inactive objects in the scan. CatchesStatsManager-style singleton pools. - Added Dump button (logs every visited field for diagnostics) and per-tab Clear button (two-click confirmation).
0.1.0
- Initial implementation: F3 IMGUI menu, JSON blacklist file, scene-load filter pass over
List<T>andT[]fields on sceneMonoBehaviours forItem/ValuableObject/EnemySetupelement types.
