MCPInstall

subnautica-ii-coop-bepinex-mod

BepInEx multiplayer mod for Subnautica 2 with session sync, shared inventory, and API-driven narrative features

Installs
229
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/reason-machines/devtools-skills --skill subnautica-ii-coop-bepinex-mod
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads subnautica-ii-coop-bepinex-mod automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Subnautica II Co-op BepInEx Mod

Skill by ara.so — Devtools Skills collection.

Overview

The Deep Synergy Multiplayer Mod transforms Subnautica 2 into a synchronized cooperative experience. Built on BepInEx, it implements deterministic session synchronization, adaptive difficulty scaling, shared inventory via Merkle tree verification, and optional AI-driven narrative features using OpenAI/Claude APIs.

Core Architecture:

  • BepInEx 6.0.x plugin framework (IL2CPP hooks, no game file modification)
  • WebRTC P2P for decentralized multiplayer (NAT punch-through)
  • Merkle tree inventory sync for distributed state integrity
  • Adaptive scaling based on player count
  • API integrations for procedural narrative generation

Installation

Prerequisites

  1. Subnautica 2 (Steam/GOG version)
  2. BepInEx 6.0.x framework installed

Setup Steps

# 1. Install BepInEx first (if not already installed)
# Download from https://github.com/BepInEx/BepInEx/releases
# Extract to Subnautica 2 game directory

# 2. Download Deep Synergy mod
# Extract to: <Subnautica2>/BepInEx/plugins/DeepSynergy/

# 3. Verify directory structure:
# BepInEx/
#   plugins/
#     DeepSynergy/
#       DeepSynergy.dll
#       Newtonsoft.Json.dll
#   config/
#     synergy_profile.json

File Structure

<Subnautica2>/
├── BepInEx/
│   ├── core/
│   ├── plugins/
│   │   └── DeepSynergy/
│   │       ├── DeepSynergy.dll
│   │       └── libs/
│   └── config/
│       ├── synergy_profile.json
│       └── session_config.xml
└── Subnautica2.exe

Configuration

Session Profile (BepInEx/config/synergy_profile.json)

{
  "session_name": "Ocean Explorers",
  "max_players": 4,
  "difficulty_scale": "adaptive",
  "resource_multiplier": 1.0,
  "oxygen_consumption": 1.0,
  "creature_spawn_divider": 1,
  "enable_pvp": false,
  "friendly_fire": false,
  "shared_blueprints": true,
  "ping_locations_shared": true,
  "time_of_day_sync": "host",
  "voice_chat_integration": "none",
  "network": {
    "port": 25000,
    "max_latency_ms": 200,
    "reconnect_timeout_s": 30,
    "stun_server": "stun:stun.l.google.com:19302"
  },
  "api_integration": {
    "openai": {
      "enabled": false,
      "api_key_env": "OPENAI_API_KEY",
      "model": "gpt-4",
      "role": "narrator"
    },
    "claude": {
      "enabled": false,
      "api_key_env": "ANTHROPIC_API_KEY",
      "model": "claude-3-sonnet-20240229",
      "role": "lore_engine"
    }
  },
  "locale": "en-US",
  "debug_mode": false
}

Configuration Fields

FieldTypeDescription
session_namestringDisplay name for session
max_playersint2-8 player limit
difficulty_scaleenumfixed, adaptive, manual
resource_multiplierfloatMultiplies harvestable resources (0.5-2.0)
oxygen_consumptionfloatOxygen drain rate (0.5-1.5)
creature_spawn_dividerintReduces creature count (1=normal, 2=half)
shared_blueprintsboolBlueprint unlocks sync across players
time_of_day_syncenumhost, all, none

Network Configuration

{
  "network": {
    "port": 25000,
    "max_latency_ms": 200,
    "reconnect_timeout_s": 30,
    "stun_server": "stun:stun.l.google.com:19302",
    "use_relay": false,
    "encryption": true
  }
}

Console Commands

Access via BepInEx terminal (F1 key by default):

# Host a new session
/start_server

# Join existing session
/join_session <session-code>
# Example: /join_session 9B2A-4C7D-E8F1

# Check connection status
/synergy_status

# Adjust difficulty scaling mid-session
/synergy_scale <multiplier>
# Example: /synergy_scale 1.5

# Force world seed synchronization
/seed_override <seed>
# Example: /seed_override 8251

# Trigger AI narration (requires API config)
/api_narrate "<context>"
# Example: /api_narrate "exploring deep caves"

# Disconnect gracefully
/disconnect

# Force inventory resync
/sync_inventory

# List connected peers
/list_peers

Code Examples

Plugin Entry Point (C#)

using BepInEx;
using BepInEx.IL2CPP;
using HarmonyLib;
using UnityEngine;

namespace DeepSynergy
{
    [BepInPlugin(GUID, Name, Version)]
    public class DeepSynergyPlugin : BasePlugin
    {
        public const string GUID = "com.deepsynergy.subnautica2";
        public const string Name = "Deep Synergy Multiplayer";
        public const string Version = "1.0.0";

        private SessionManager _sessionManager;
        private StateSync _stateSync;

        public override void Load()
        {
            Log.LogInfo("Loading Deep Synergy...");
            
            // Load configuration
            var config = ConfigLoader.LoadProfile();
            
            // Initialize core systems
            _sessionManager = new SessionManager(config);
            _stateSync = new StateSync(config);
            
            // Apply Harmony patches
            var harmony = new Harmony(GUID);
            harmony.PatchAll();
            
            Log.LogInfo("Deep Synergy loaded successfully");
        }
    }
}

Session Creation Pattern

using DeepSynergy.Core;
using System;

namespace DeepSynergy.Session
{
    public class SessionManager
    {
        private SynergyConfig _config;
        private WebRTCChannel _channel;

        public SessionManager(SynergyConfig config)
        {
            _config = config;
        }

        public string CreateSession()
        {
            var sessionCode = GenerateSessionCode();
            _channel = new WebRTCChannel(_config.Network);
            
            _channel.OnPeerConnected += HandlePeerConnect;
            _channel.OnDataReceived += HandleStateUpdate;
            
            _channel.StartHost(_config.Network.Port);
            
            Console.WriteLine($"Session created: {sessionCode}");
            return sessionCode;
        }

        public bool JoinSession(string sessionCode)
        {
            _channel = new WebRTCChannel(_config.Network);
            
            _channel.OnConnected += () => {
                Console.WriteLine("Connected to session");
                SyncInitialState();
            };
            
            return _channel.Connect(sessionCode);
        }

        private void HandleStateUpdate(byte[] data)
        {
            var update = StateUpdate.Deserialize(data);
            _stateSync.ApplyUpdate(update);
        }

        private string GenerateSessionCode()
        {
            var guid = Guid.NewGuid();
            return $"{guid.ToString().Substring(0, 4).ToUpper()}-" +
                   $"{guid.ToString().Substring(4, 4).ToUpper()}-" +
                   $"{guid.ToString().Substring(8, 4).ToUpper()}";
        }
    }
}

Inventory Synchronization

using System.Security.Cryptography;
using System.Text;

namespace DeepSynergy.Sync
{
    public class InventorySync
    {
        private Dictionary<string, InventoryState> _playerInventories;
        
        public string ComputeMerkleRoot(InventoryState inventory)
        {
            var hashes = new List<string>();
            
            foreach (var item in inventory.Items.OrderBy(i => i.Id))
            {
                var itemData = $"{item.Id}:{item.Quantity}:{item.Metadata}";
                hashes.Add(ComputeHash(itemData));
            }
            
            return BuildMerkleTree(hashes);
        }

        public void SyncInventory(string playerId, InventoryState state)
        {
            var localHash = ComputeMerkleRoot(state);
            var remoteHash = RequestRemoteHash(playerId);
            
            if (localHash != remoteHash)
            {
                // Resolve conflict using timestamp authority
                var resolvedState = ConflictResolver.Resolve(
                    state, 
                    GetRemoteState(playerId)
                );
                
                ApplyInventoryState(playerId, resolvedState);
                BroadcastUpdate(playerId, resolvedState);
            }
        }

        private string ComputeHash(string data)
        {
            using (var sha256 = SHA256.Create())
            {
                var bytes = Encoding.UTF8.GetBytes(data);
                var hash = sha256.ComputeHash(bytes);
                return BitConverter.ToString(hash).Replace("-", "");
            }
        }

        private string BuildMerkleTree(List<string> hashes)
        {
            if (hashes.Count == 1) return hashes[0];
            
            var newLevel = new List<string>();
            for (int i = 0; i < hashes.Count; i += 2)
            {
                var combined = i + 1 < hashes.Count 
                    ? hashes[i] + hashes[i + 1] 
                    : hashes[i];
                newLevel.Add(ComputeHash(combined));
            }
            
            return BuildMerkleTree(newLevel);
        }
    }
}

AI Narrative Integration

using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

namespace DeepSynergy.AI
{
    public class NarrativeEngine
    {
        private readonly HttpClient _client;
        private readonly string _apiKey;
        private readonly string _model;

        public NarrativeEngine(string apiKeyEnv, string model)
        {
            _apiKey = Environment.GetEnvironmentVariable(apiKeyEnv);
            _model = model;
            _client = new HttpClient();
        }

        public async Task<string> GenerateNarration(string context)
        {
            if (string.IsNullOrEmpty(_apiKey))
            {
                return "API key not configured";
            }

            var request = new
            {
                model = _model,
                messages = new[]
                {
                    new { role = "system", content = "You are a narrator for a co-op ocean survival game. Generate brief, atmospheric journal entries based on player actions." },
                    new { role = "user", content = $"Context: {context}" }
                },
                max_tokens = 150,
                temperature = 0.7
            };

            _client.DefaultRequestHeaders.Clear();
            _client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");

            var response = await _client.PostAsJsonAsync(
                "https://api.openai.com/v1/chat/completions", 
                request
            );

            var result = await response.Content.ReadAsStringAsync();
            var json = JsonDocument.Parse(result);
            
            return json.RootElement
                .GetProperty("choices")[0]
                .GetProperty("message")
                .GetProperty("content")
                .GetString();
        }
    }
}

Adaptive Scaling Implementation

namespace DeepSynergy.Scaling
{
    public class DifficultyScaler
    {
        private SynergyConfig _config;
        private int _playerCount;

        public void UpdatePlayerCount(int count)
        {
            _playerCount = count;
            
            if (_config.DifficultyScale == "adaptive")
            {
                AdjustGameParameters();
            }
        }

        private void AdjustGameParameters()
        {
            // Scale creature spawns inversely with player count
            var creatureMultiplier = 1.0f / (_playerCount * _config.CreatureSpawnDivider);
            Game.CreatureSpawner.SetGlobalMultiplier(creatureMultiplier);
            
            // Scale resource availability
            var resourceMultiplier = _config.ResourceMultiplier * 
                Mathf.Lerp(1.0f, 1.5f, (_playerCount - 1) / 7f);
            Game.ResourceManager.SetMultiplier(resourceMultiplier);
            
            // Adjust oxygen consumption (more players = slightly more efficient)
            var oxygenEfficiency = _config.OxygenConsumption * 
                Mathf.Lerp(1.0f, 0.85f, (_playerCount - 1) / 7f);
            Game.Player.SetOxygenConsumption(oxygenEfficiency);
        }
    }
}

Common Patterns

Session Lifecycle

// Host workflow
var session = new SessionManager(config);
var sessionCode = session.CreateSession();
Console.WriteLine($"Share code: {sessionCode}");

// Client workflow
var session = new SessionManager(config);
session.JoinSession("9B2A-4C7D-E8F1");

// Graceful disconnect
session.Disconnect();

State Synchronization

// Send state update
var update = new StateUpdate
{
    Timestamp = DateTime.UtcNow.Ticks,
    PlayerId = localPlayerId,
    Type = UpdateType.InventoryChange,
    Data = SerializeInventory()
};
_channel.Broadcast(update.Serialize());

// Receive and apply update
_channel.OnDataReceived += (data) => {
    var update = StateUpdate.Deserialize(data);
    if (ConflictResolver.ShouldApply(update))
    {
        ApplyStateUpdate(update);
    }
};

Error Handling

try
{
    session.JoinSession(sessionCode);
}
catch (SessionNotFoundException)
{
    Console.WriteLine("Session not found. Check code.");
}
catch (NetworkTimeoutException)
{
    Console.WriteLine("Connection timeout. Check firewall/NAT.");
}
catch (VersionMismatchException ex)
{
    Console.WriteLine($"Mod version mismatch: {ex.Message}");
}

Troubleshooting

Connection Issues

Symptom: Cannot connect to session
Solutions:

  1. Verify port forwarding (default 25000)
  2. Check firewall rules for Subnautica2.exe and BepInEx
  3. Confirm both players have identical mod versions
  4. Enable debug mode: "debug_mode": true in config
  5. Try changing STUN server in network config
# Check logs
tail -f BepInEx/LogOutput.log | grep DeepSynergy

# Common error patterns:
# "WebRTC connection timeout" → NAT/firewall issue
# "Version mismatch" → Update mod to matching version
# "Session not found" → Invalid session code

Inventory Desync

Symptom: Players see different inventories
Solutions:

# Force resync via console
/sync_inventory

# Or programmatically
inventorySync.ForceFullSync();

Check for conflicting mods:

# Disable other inventory mods temporarily
# Verify DeepSynergy.dll is latest version

API Integration Not Working

Symptom: AI narration not generating
Solutions:

# Verify environment variables
echo $OPENAI_API_KEY  # Linux/Mac
echo %OPENAI_API_KEY%  # Windows

# Test API key
curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY"

Check config:

{
  "api_integration": {
    "openai": {
      "enabled": true,
      "api_key_env": "OPENAI_API_KEY",  // Must match env var name
      "model": "gpt-4"
    }
  }
}

Performance Issues

Symptom: Lag/stuttering with 3+ players
Solutions:

  1. Reduce creature spawn rate:
{
  "creature_spawn_divider": 2  // Halves creature count
}
  1. Lower sync frequency (advanced):
StateSync.SetUpdateInterval(100);  // ms, default 50
  1. Disable API features:
{
  "api_integration": {
    "openai": { "enabled": false },
    "claude": { "enabled": false }
  }
}

Log Analysis

# Enable debug logging
# In synergy_profile.json:
"debug_mode": true

# Key log patterns:
# "[StateSync] Merkle mismatch" → Inventory desync
# "[Network] Peer disconnected" → Connection loss
# "[API] Rate limit exceeded" → Reduce API calls

Advanced Configuration

Custom Scaling Formula

// Override default scaling in plugin
public class CustomScaler : DifficultyScaler
{
    protected override float CalculateCreatureMultiplier(int players)
    {
        // Custom formula: exponential reduction
        return Mathf.Pow(0.8f, players - 1);
    }
}

Session Migration

// Implement host migration on disconnect
_channel.OnHostDisconnected += () => {
    var survivingPeer = _peers.OrderBy(p => p.Latency).First();
    if (survivingPeer.Id == localPlayerId)
    {
        PromoteToHost();
    }
};

private void PromoteToHost()
{
    _channel.BecomeHost();
    BroadcastFullState();
}

Blueprint Sharing

// Sync blueprint unlocks
Game.BlueprintManager.OnUnlock += (blueprint) => {
    if (_config.SharedBlueprints)
    {
        var update = new StateUpdate
        {
            Type = UpdateType.BlueprintUnlock,
            Data = Encoding.UTF8.GetBytes(blueprint.Id)
        };
        _channel.Broadcast(update.Serialize());
    }
};

Environment Variables

# API keys (never commit these)
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."

# Optional network overrides
export SYNERGY_PORT=25000
export SYNERGY_STUN_SERVER="stun:custom.server.com:3478"

Compatibility

  • OS: Windows 10/11, macOS 11+, Linux (Ubuntu 20.04+, Steam Deck)
  • BepInEx: 6.0.x (IL2CPP)
  • Game Version: Subnautica 2 (2026 release)
  • Conflicting Mods: Avoid other multiplayer/inventory mods

FAQ

What does the subnautica-ii-coop-bepinex-mod skill do?

BepInEx multiplayer mod for Subnautica 2 with session sync, shared inventory, and API-driven narrative features

How do I install subnautica-ii-coop-bepinex-mod?

Run npx skills add https://github.com/reason-machines/devtools-skills --skill subnautica-ii-coop-bepinex-mod in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/subnautica-ii-coop-bepinex-mod/ (personal) or .claude/skills/subnautica-ii-coop-bepinex-mod/ (project).

Which AI agents can use subnautica-ii-coop-bepinex-mod?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is subnautica-ii-coop-bepinex-mod safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains subnautica-ii-coop-bepinex-mod?

It is published by reason-machines in reason-machines/devtools-skills. It has 229 recorded installs.

More from reason-machines/devtools-skills

View all →

cc-switch-cli

reason-machines/devtools-skills

Cross-platform CLI tool for managing Claude Code, Codex, Gemini, OpenCode & OpenClaw providers, MCP servers, prompts, skills, and proxies.

AI & Agents316 installs

wx-cli-wechat-local-data

reason-machines/devtools-skills

Open the skill to read its SKILL.md instructions.

284 installs

chrome-devtools-mcp-automation

reason-machines/devtools-skills

Use Chrome DevTools MCP to control Chrome, debug web apps, analyze performance, and automate browser tasks via MCP tools

AI & Agents265 installs

officecli-office-automation

reason-machines/devtools-skills

Use OfficeCLI to create, read, and edit Word, Excel, and PowerPoint files from the command line with AI-friendly commands.

cli-anything-agent-native-software

reason-machines/devtools-skills

Generate and use command-line interfaces that make any software controllable by AI agents with structured JSON output

AI & Agents258 installs

autocli-web-scraping

reason-machines/devtools-skills

Open the skill to read its SKILL.md instructions.

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs