subnautica-ii-coop-bepinex-mod
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSubnautica II Co-op BepInEx Mod
Subnautica 2 合作模式 BepInEx 模组
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
Deep Synergy多人游戏模组将Subnautica 2转变为同步合作体验。基于BepInEx构建,它实现了确定性会话同步、自适应难度缩放、基于Merkle树验证的共享库存,以及可选的基于OpenAI/Claude API的AI驱动叙事功能。
核心架构:
- BepInEx 6.0.x 插件框架(IL2CPP钩子,无需修改游戏文件)
- WebRTC P2P 用于去中心化多人游戏(NAT穿透)
- Merkle树库存同步 确保分布式状态完整性
- 基于玩家数量的自适应缩放
- API集成 用于程序化叙事生成
Installation
安装
Prerequisites
前置条件
- Subnautica 2 (Steam/GOG version)
- BepInEx 6.0.x framework installed
- Subnautica 2(Steam/GOG版本)
- 已安装BepInEx 6.0.x框架
Setup Steps
设置步骤
bash
undefinedbash
undefined1. Install BepInEx first (if not already installed)
1. 先安装BepInEx(如果尚未安装)
Download from https://github.com/BepInEx/BepInEx/releases
Extract to Subnautica 2 game directory
解压到Subnautica 2游戏目录
2. Download Deep Synergy mod
2. 下载Deep Synergy模组
Extract to: <Subnautica2>/BepInEx/plugins/DeepSynergy/
解压至:<Subnautica2>/BepInEx/plugins/DeepSynergy/
3. Verify directory structure:
3. 验证目录结构:
BepInEx/
BepInEx/
plugins/
plugins/
DeepSynergy/
DeepSynergy/
DeepSynergy.dll
DeepSynergy.dll
Newtonsoft.Json.dll
Newtonsoft.Json.dll
config/
config/
synergy_profile.json
synergy_profile.json
undefinedundefinedFile Structure
文件结构
<Subnautica2>/
├── BepInEx/
│ ├── core/
│ ├── plugins/
│ │ └── DeepSynergy/
│ │ ├── DeepSynergy.dll
│ │ └── libs/
│ └── config/
│ ├── synergy_profile.json
│ └── session_config.xml
└── Subnautica2.exe<Subnautica2>/
├── BepInEx/
│ ├── core/
│ ├── plugins/
│ │ └── DeepSynergy/
│ │ ├── DeepSynergy.dll
│ │ └── libs/
│ └── config/
│ ├── synergy_profile.json
│ └── session_config.xml
└── Subnautica2.exeConfiguration
配置
Session Profile (BepInEx/config/synergy_profile.json
)
BepInEx/config/synergy_profile.json会话配置文件(BepInEx/config/synergy_profile.json
)
BepInEx/config/synergy_profile.jsonjson
{
"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
}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
配置字段
| Field | Type | Description |
|---|---|---|
| string | Display name for session |
| int | 2-8 player limit |
| enum | |
| float | Multiplies harvestable resources (0.5-2.0) |
| float | Oxygen drain rate (0.5-1.5) |
| int | Reduces creature count (1=normal, 2=half) |
| bool | Blueprint unlocks sync across players |
| enum | |
| 字段 | 类型 | 描述 |
|---|---|---|
| string | 会话显示名称 |
| int | 玩家数量限制(2-8人) |
| enum | |
| float | 可采集资源倍数(0.5-2.0) |
| float | 氧气消耗速率(0.5-1.5) |
| int | 减少生物生成数量(1=正常,2=减半) |
| bool | 蓝图解锁在玩家间同步 |
| enum | |
Network Configuration
网络配置
json
{
"network": {
"port": 25000,
"max_latency_ms": 200,
"reconnect_timeout_s": 30,
"stun_server": "stun:stun.l.google.com:19302",
"use_relay": false,
"encryption": true
}
}json
{
"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):
bash
undefined通过BepInEx终端访问(默认按F1键):
bash
undefinedHost a new session
开启新会话
/start_server
/start_server
Join existing session
加入现有会话
/join_session <session-code>
/join_session <session-code>
Example: /join_session 9B2A-4C7D-E8F1
示例:/join_session 9B2A-4C7D-E8F1
Check connection status
检查连接状态
/synergy_status
/synergy_status
Adjust difficulty scaling mid-session
会话中调整难度缩放
/synergy_scale <multiplier>
/synergy_scale <multiplier>
Example: /synergy_scale 1.5
示例:/synergy_scale 1.5
Force world seed synchronization
强制世界种子同步
/seed_override <seed>
/seed_override <seed>
Example: /seed_override 8251
示例:/seed_override 8251
Trigger AI narration (requires API config)
触发AI叙事(需配置API)
/api_narrate "<context>"
/api_narrate "<context>"
Example: /api_narrate "exploring deep caves"
示例:/api_narrate "exploring deep caves"
Disconnect gracefully
优雅断开连接
/disconnect
/disconnect
Force inventory resync
强制库存同步
/sync_inventory
/sync_inventory
List connected peers
列出已连接的玩家
/list_peers
undefined/list_peers
undefinedCode Examples
代码示例
Plugin Entry Point (C#)
Plugin Entry Point (C#)
csharp
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");
}
}
}csharp
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
Session Creation Pattern
csharp
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()}";
}
}
}csharp
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
Inventory Synchronization
csharp
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);
}
}
}csharp
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
AI Narrative Integration
csharp
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();
}
}
}csharp
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
Adaptive Scaling Implementation
csharp
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);
}
}
}csharp
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
Common Patterns
Session Lifecycle
Session Lifecycle
csharp
// 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();csharp
// 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
State Synchronization
csharp
// 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);
}
};csharp
// 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
Error Handling
csharp
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}");
}csharp
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:
Solutions:
- Verify port forwarding (default 25000)
- Check firewall rules for Subnautica2.exe and BepInEx
- Confirm both players have identical mod versions
- Enable debug mode: in config
"debug_mode": true - Try changing STUN server in network config
bash
undefined症状: 无法连接到会话
解决方案:
解决方案:
- 验证端口转发(默认25000)
- 检查Subnautica2.exe和BepInEx的防火墙规则
- 确认所有玩家使用相同版本的模组
- 启用调试模式:在配置中设置
"debug_mode": true - 尝试更改网络配置中的STUN服务器
bash
undefinedCheck logs
查看日志
tail -f BepInEx/LogOutput.log | grep DeepSynergy
tail -f BepInEx/LogOutput.log | grep DeepSynergy
Common error patterns:
常见错误模式:
"WebRTC connection timeout" → NAT/firewall issue
"WebRTC connection timeout" → NAT/防火墙问题
"Version mismatch" → Update mod to matching version
"Version mismatch" → 更新模组至匹配版本
"Session not found" → Invalid session code
"Session not found" → 会话代码无效
undefinedundefinedInventory Desync
库存不同步
Symptom: Players see different inventories
Solutions:
Solutions:
bash
undefined症状: 玩家看到的库存不一致
解决方案:
解决方案:
bash
undefinedForce resync via console
通过控制台强制同步
/sync_inventory
/sync_inventory
Or programmatically
或通过代码实现
inventorySync.ForceFullSync();
Check for conflicting mods:
```bashinventorySync.ForceFullSync();
检查冲突模组:
```bashDisable other inventory mods temporarily
暂时禁用其他库存类模组
Verify DeepSynergy.dll is latest version
确认DeepSynergy.dll为最新版本
undefinedundefinedAPI Integration Not Working
API集成失效
Symptom: AI narration not generating
Solutions:
Solutions:
bash
undefined症状: AI叙事无法生成内容
解决方案:
解决方案:
bash
undefinedVerify environment variables
验证环境变量
echo $OPENAI_API_KEY # Linux/Mac
echo %OPENAI_API_KEY% # Windows
echo $OPENAI_API_KEY # Linux/Mac
echo %OPENAI_API_KEY% # Windows
Test API key
测试API密钥
curl https://api.openai.com/v1/models
-H "Authorization: Bearer $OPENAI_API_KEY"
-H "Authorization: Bearer $OPENAI_API_KEY"
Check config:
```json
{
"api_integration": {
"openai": {
"enabled": true,
"api_key_env": "OPENAI_API_KEY", // Must match env var name
"model": "gpt-4"
}
}
}curl https://api.openai.com/v1/models
-H "Authorization: Bearer $OPENAI_API_KEY"
-H "Authorization: Bearer $OPENAI_API_KEY"
检查配置:
```json
{
"api_integration": {
"openai": {
"enabled": true,
"api_key_env": "OPENAI_API_KEY", // 必须与环境变量名称一致
"model": "gpt-4"
}
}
}Performance Issues
性能问题
Symptom: Lag/stuttering with 3+ players
Solutions:
Solutions:
- Reduce creature spawn rate:
json
{
"creature_spawn_divider": 2 // Halves creature count
}- Lower sync frequency (advanced):
csharp
StateSync.SetUpdateInterval(100); // ms, default 50- Disable API features:
json
{
"api_integration": {
"openai": { "enabled": false },
"claude": { "enabled": false }
}
}症状: 3人及以上玩家时出现卡顿/延迟
解决方案:
解决方案:
- 降低生物生成速率:
json
{
"creature_spawn_divider": 2 // 生物数量减半
}- 降低同步频率(高级设置):
csharp
StateSync.SetUpdateInterval(100); // 毫秒,默认50- 禁用API功能:
json
{
"api_integration": {
"openai": { "enabled": false },
"claude": { "enabled": false }
}
}Log Analysis
日志分析
bash
undefinedbash
undefinedEnable debug logging
启用调试日志
In synergy_profile.json:
在synergy_profile.json中设置:
"debug_mode": true
"debug_mode": true
Key log patterns:
关键日志模式:
"[StateSync] Merkle mismatch" → Inventory desync
"[StateSync] Merkle mismatch" → 库存不同步
"[Network] Peer disconnected" → Connection loss
"[Network] Peer disconnected" → 连接中断
"[API] Rate limit exceeded" → Reduce API calls
"[API] Rate limit exceeded" → 减少API调用次数
undefinedundefinedAdvanced Configuration
高级配置
Custom Scaling Formula
自定义缩放公式
csharp
// 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);
}
}csharp
// 在插件中覆盖默认缩放逻辑
public class CustomScaler : DifficultyScaler
{
protected override float CalculateCreatureMultiplier(int players)
{
// 自定义公式:指数级减少
return Mathf.Pow(0.8f, players - 1);
}
}Session Migration
会话迁移
csharp
// 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();
}csharp
// 实现主机断开时的会话迁移
_channel.OnHostDisconnected += () => {
var survivingPeer = _peers.OrderBy(p => p.Latency).First();
if (survivingPeer.Id == localPlayerId)
{
PromoteToHost();
}
};
private void PromoteToHost()
{
_channel.BecomeHost();
BroadcastFullState();
}Blueprint Sharing
蓝图共享
csharp
// 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());
}
};csharp
// 同步蓝图解锁
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
环境变量
bash
undefinedbash
undefinedAPI keys (never commit these)
API密钥(切勿提交到代码仓库)
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
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"
undefinedexport SYNERGY_PORT=25000
export SYNERGY_STUN_SERVER="stun:custom.server.com:3478"
undefinedCompatibility
兼容性
- 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
- 操作系统: Windows 10/11、macOS 11+、Linux(Ubuntu 20.04+、Steam Deck)
- BepInEx版本: 6.0.x(IL2CPP)
- 游戏版本: Subnautica 2(2026年发布版)
- 冲突模组: 避免同时使用其他多人游戏/库存类模组