| name | subnautica-ii-deep-synergy-multiplayer-mod |
| description | BepInEx-based cooperative multiplayer mod for Subnautica 2 with synchronized session management, shared inventory, and dynamic scaling |
| triggers | ["install subnautica 2 multiplayer mod","set up deep synergy coop","configure bepinex subnautica multiplayer","create subnautica 2 coop session","sync multiplayer state subnautica","troubleshoot subnautica multiplayer mod","configure deep synergy session","use subnautica coop commands"] |
Subnautica II Deep Synergy Multiplayer Mod
Skill by ara.so — Devtools Skills collection.
Overview
The Deep Synergy Multiplayer Mod transforms Subnautica 2 into a synchronized cooperative experience using the BepInEx modding framework. It implements deterministic session synchronization (DSS), adaptive dynamic scaling (ADS), and peer-to-peer connectivity via WebRTC for seamless multiplayer gameplay.
Key Architecture:
- BepInEx Plugin - Hooks into Unity IL2CPP runtime without modifying game files
- Decentralized Session Management - No central servers; uses NAT punch-through
- Merkle Tree Inventory - Blockchain-inspired state verification across peers
- Adaptive Scaling - Creature spawns and resource availability adjust to player count
Installation
Prerequisites
- Subnautica 2 installed (Steam/GOG)
- BepInEx 6.0.x for IL2CPP Unity games
Step-by-Step Installation
<Game Directory>/
├── BepInEx/
│ ├── plugins/
│ │ ├── DeepSynergy.dll
│ │ ├── SyncEngine.dll
│ │ └── NetworkCore.dll
│ └── config/
│ └── synergy_profile.json
Verify Installation
Launch Subnautica 2. Check BepInEx console for:
[Info :BepInEx] Deep Synergy Multiplayer Mod v2.1.0 loaded
[Info :DeepSynergy] Session Manager initialized
[Info :DeepSynergy] State Synchronizer ready
Configuration
Profile Configuration File
Create/edit BepInEx/config/synergy_profile.json:
{
"session_name": "Ocean Explorers",
"max_players": 4,
"difficulty_scale": "adaptive",
"resource_multiplier": 1.5,
"oxygen_consumption": 0.85,
"creature_spawn_divider": 2,
"enable_pvp": false,
"friendly_fire": false,
"shared_blueprints": true,
"ping_locations_shared": true,
"time_of_day_sync": "all",
"voice_chat_integration": "discord_rpc",
"network": {
"port_range":
Configuration Fields
| Field | Type | Description |
|---|
session_name | string | Display name for hosted session |
max_players | int | Maximum concurrent players (2-8) |
difficulty_scale | enum | adaptive, fixed, manual |
resource_multiplier | float | Multiplier for harvestable resources (0.5-3.0) |
oxygen_consumption | float | Oxygen drain rate (0.5-1.5, lower = slower) |
creature_spawn_divider | int | Divide creature spawn rates (2 = half) |
shared_blueprints | bool | All players share blueprint unlocks |
ping_locations_shared | bool | Map pings visible to all players |
Locale Configuration
Add to synergy_profile.json:
{
"locale": "en_US",
"locale_override": true
}
Supported: en_US, zh_CN, ja_JP, de_DE, fr_FR, pt_BR, ru_RU, es_ES, ko_KR
In-Game Commands
Access via BepInEx console (F5 by default):
Session Management
/start_server
/join_session 9B2A-4C7D-E8F1
/disconnect
/synergy_status
Runtime Configuration
/synergy_scale 1.5
/seed_override 8251
/pvp_toggle
/kick_player 2
AI Integration Commands
/api_narrate "exploring the deep sea trench"
/lore_creature "Shadow Leviathan"
/narrator_hint
Code Examples
Programmatic Session Control
If extending the mod via BepInEx plugins:
using DeepSynergy.Core;
using BepInEx;
using UnityEngine;
[BepInPlugin("com.yourmod.extension", "Session Extension", "1.0.0")]
public class SessionExtension : BaseUnityPlugin
{
private SessionManager sessionManager;
void Awake()
{
sessionManager = SessionManager.Instance;
sessionManager.OnPlayerJoined += HandlePlayerJoined;
sessionManager.OnStateSync += HandleStateSync;
}
void HandlePlayerJoined(PlayerInfo player)
{
Logger.LogInfo($"Player {player.DisplayName} joined");
var config = sessionManager.Config;
if (config.SharedBlueprints)
{
SyncBlueprintsToPlayer(player);
}
}
void HandleStateSync(SyncEvent syncEvent)
{
if (syncEvent.Type == SyncType.Inventory)
{
VerifyInventoryIntegrity(syncEvent.MerkleHash);
}
}
void SyncBlueprintsToPlayer(PlayerInfo player)
{
var blueprints = BlueprintManager.GetUnlockedBlueprints();
sessionManager.SendToPlayer(player.Id, blueprints);
}
void VerifyInventoryIntegrity()
{
localHash = InventoryHasher.ComputeMerkleRoot();
(localHash != merkleHash)
{
Logger.LogWarning();
sessionManager.RequestFullSync();
}
}
}
Custom Scaling Logic
using DeepSynergy.Scaling;
public class CustomScalingRule : IScalingRule
{
public void ApplyScaling(int playerCount)
{
var config = SessionManager.Instance.Config;
float resourceScale = config.ResourceMultiplier * Mathf.Sqrt(playerCount);
ResourceSpawner.SetGlobalMultiplier(resourceScale);
int spawnDivider = config.CreatureSpawnDivider * playerCount;
CreatureManager.SetSpawnRateDivider(spawnDivider);
float oxygenMod = config.OxygenConsumption / Mathf.Log(playerCount + 1);
PlayerOxygenSystem.SetConsumptionRate(oxygenMod);
}
}
ScalingEngine.RegisterRule(new CustomScalingRule());
Inventory Synchronization Hook
using DeepSynergy.Sync;
public class InventorySyncHook : MonoBehaviour
{
void OnItemPickup(Item item)
{
var syncData = new InventorySyncData
{
PlayerId = LocalPlayer.Id,
ItemId = item.TechType,
Quantity = 1,
Timestamp = NetworkTime.CurrentTime
};
StateSynchronizer.BroadcastEvent(syncData);
}
void OnItemCraft(TechType techType)
{
if (SessionManager.Instance.Config.SharedBlueprints)
{
var unlockData = new BlueprintUnlockData
{
TechType = techType,
UnlockedBy = LocalPlayer.Id
};
StateSynchronizer.BroadcastUnlock(unlockData);
}
}
}
Common Patterns
Host Migration on Disconnect
void OnHostDisconnect()
{
if (SessionManager.Instance.IsHost)
{
var nextHost = SessionManager.Instance.GetNextEligibleHost();
if (nextHost != null)
{
Logger.LogInfo($"Migrating host to {nextHost.DisplayName}");
SessionManager.Instance.MigrateHostTo(nextHost.Id);
var sessionState = SessionStateSerializer.Capture();
SessionManager.Instance.SendStateSnapshot(nextHost.Id, sessionState);
}
else
{
Logger.LogWarning("No eligible host for migration, ending session");
SessionManager.Instance.EndSession();
}
}
}
Conflict Resolution
void ResolveInventoryConflict(InventoryConflict conflict)
{
var localTimestamp = conflict.LocalEvent.Timestamp;
var remoteTimestamp = conflict.RemoteEvent.Timestamp;
if (remoteTimestamp > localTimestamp)
{
ApplyInventoryEvent(conflict.RemoteEvent);
}
else if (remoteTimestamp < localTimestamp)
{
StateSynchronizer.BroadcastEvent(conflict.LocalEvent, priority: true);
}
else
{
if (conflict.RemoteEvent.PlayerId > conflict.LocalEvent.PlayerId)
{
ApplyInventoryEvent(conflict.RemoteEvent);
}
}
}
API Integration Example
using System.Net.Http;
using Newtonsoft.Json;
public class NarrativeEngine
{
private static readonly HttpClient client = new HttpClient();
public async Task<string> GenerateNarrative(string context)
{
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
if (string.IsNullOrEmpty(apiKey))
{
Logger.LogWarning("OPENAI_API_KEY not set");
return null;
}
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey);
var request = new
{
model = "gpt-4",
messages = new[]
{
new { role = "system", content = "You are a narrative generator for Subnautica 2 multiplayer sessions." },
new { role = "user", content = $"Generate a journal entry: {context}" }
},
max_tokens = 150
};
var response = await client.PostAsync(
"https://api.openai.com/v1/chat/completions",
new StringContent(JsonConvert.SerializeObject(request), System.Text.Encoding.UTF8, "application/json")
);
result = JsonConvert.DeserializeObject<>(
response.Content.ReadAsStringAsync()
);
result.choices[].message.content;
}
}
Troubleshooting
Session Connection Failures
Problem: Cannot join session, timeout errors
Solutions:
- Verify firewall allows UDP ports
7777-7787
- Check NAT type:
Strict NAT requires manual port forwarding
- Increase timeout in config:
"network": {
"timeout_seconds": 60
}
Diagnostic command:
/network_diagnostics
Inventory Desynchronization
Problem: Players see different inventory states
Solutions:
- Force full resync:
/synergy_resync inventory
- Check network latency:
/synergy_status
- Verify Merkle hash integrity:
var localHash = InventoryHasher.ComputeMerkleRoot();
Logger.LogInfo($"Local inventory hash: {localHash}");
High CPU Usage
Problem: Performance degradation with multiple players
Solutions:
- Reduce
creature_spawn_divider in config
- Disable API integrations if enabled
- Lower
max_players count
- Adjust sync frequency in
BepInEx/config/DeepSynergy.cfg:
[Synchronization]
StateUpdateHz = 10
BepInEx Load Errors
Problem: Mod not loading, missing dependencies
Check:
<Game Directory>/BepInEx/LogOutput.log
Solution:
- Reinstall BepInEx 6.0.x IL2CPP variant
- Remove conflicting mods from
plugins/ folder
- Verify game version matches mod compatibility (check release notes)
Session Code Invalid
Problem: "Invalid session code" when joining
Solutions:
- Verify code format:
XXXX-XXXX-XXXX (12 hex characters)
- Check host is still running:
/synergy_status on host
- Ensure matching mod versions across all players
- Regenerate session:
/restart_server on host
API Integration Not Working
Problem: Narrative/lore generation fails
Check:
- Environment variables set:
echo $OPENAI_API_KEY
echo $ANTHROPIC_API_KEY
- API enabled in config:
"api_integration": {
"openai": { "enabled": true }
}
- Network connectivity to API endpoints
- Check BepInEx console for API error messages
Performance Optimization
Recommended Settings for 4+ Players
{
"max_players": 4,
"creature_spawn_divider": 3,
"resource_multiplier": 2.0,
"network": {
"state_update_hz": 10,
"position_update_hz": 20,
"inventory_sync_batch": true
},
"optimization": {
"async_state_processing": true,
"threaded_merkle_computation": true,
"delta_compression": true
}
}
Monitoring Performance
void Update()
{
var perfStats = SessionManager.Instance.GetPerformanceStats();
if (perfStats.AverageLatency > 150)
{
Logger.LogWarning($"High latency detected: {perfStats.AverageLatency}ms");
}
if (perfStats.PacketLossRate > 0.05)
{
Logger.LogWarning($"Packet loss: {perfStats.PacketLossRate * 100}%");
}
}
Advanced Usage
Custom Session Events
using DeepSynergy.Events;
public class BaseConstructedEvent : ISyncEvent
{
public string PlayerId { get; set; }
public Vector3 Position { get; set; }
public string BasePartType { get; set; }
public long Timestamp { get; set; }
}
void OnBasePartPlaced(BasePartType type, Vector3 position)
{
var evt = new BaseConstructedEvent
{
PlayerId = LocalPlayer.Id,
Position = position,
BasePartType = type.ToString(),
Timestamp = NetworkTime.CurrentTime
};
StateSynchronizer.BroadcastCustomEvent(evt);
}
void OnCustomEventReceived(ISyncEvent evt)
{
if (evt is BaseConstructedEvent baseEvt)
{
Logger.LogInfo($"Player {baseEvt.PlayerId} built {baseEvt.BasePartType}");
}
}
This skill covers the essential knowledge needed to install, configure, use, and troubleshoot the Deep Synergy Multiplayer Mod for Subnautica 2 using BepInEx.