-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLCDirectLan.cs
More file actions
254 lines (216 loc) · 12.5 KB
/
LCDirectLan.cs
File metadata and controls
254 lines (216 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/**
* This source code is part of LCDirectLAN project,
* LCDirectLAN is a mod for Lethal Company that is built around BepInEx to fix and enhances LAN lobbies.
*
* Project Repository:
* https://github.com/TIRTAGT/LCDirectLAN
*
* This project is open source and are released under the MIT License,
* for more information, please read the LICENSE file in the project repository.
*
* Copyright (c) 2024 Matthew Tirtawidjaja <matthew@tirtagt.xyz>
**/
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
namespace LCDirectLAN
{
[BepInPlugin(LCDirectLan.PLUGIN_GUID, LCDirectLan.PLUGIN_NAME, LCDirectLan.PLUGIN_VERSION)]
public class LCDirectLan : BaseUnityPlugin
{
public const string PLUGIN_GUID = "TIRTAGT.LCDirectLAN";
public const string PLUGIN_NAME = "LCDirectLAN";
/// <summary>
/// Version of the plugin that follows semantic versioning format<br/>
///
/// <b>Major</b> - Major version number, incremented when there are significant changes that breaks compatibility<br/>
/// <b>Minor</b> - Minor version number, incremented when there are changes that breaks compatibility<br/>
/// <b>Build</b> - Build number, incremented when there are changes that doesn't break any compatibility<br/>
/// </summary>
public const string PLUGIN_VERSION = "1.1.3";
/// <summary>
/// Version of the plugin assembly that follows "major.minor.build.revision" format<br/>
///
/// <b>Major</b> - Major version number, incremented when there are significant changes that breaks compatibility<br/>
/// <b>Minor</b> - Minor version number, incremented when there are changes that breaks compatibility<br/>
/// <b>Build</b> - Build number, incremented when there are changes that doesn't break any compatibility<br/>
/// <b>Revision</b> - Revision number, 00000 (for Debug/Development) or 10101 (for Release)<br/>
/// </summary>
#if DEBUG
public const string PLUGIN_ASSEMBLY_VERSION = PLUGIN_VERSION + ".00000";
public const string PLUGIN_COMPILE_CONFIG = "Debug";
#else
public const string PLUGIN_ASSEMBLY_VERSION = PLUGIN_VERSION + ".10101";
public const string PLUGIN_COMPILE_CONFIG = "Release";
#endif
private readonly Harmony HarmonyLib = new Harmony(LCDirectLan.PLUGIN_GUID);
private static LCDirectLan Instance;
private static bool IsAlreadyAwake = false;
private static ConfigFile config;
public static bool IsOnLanMode = false;
public LCDirectLan()
{
if (LCDirectLan.Instance != null)
{
this.Logger.LogWarning($"{LCDirectLan.PLUGIN_GUID} is already loaded.");
return;
}
LCDirectLan.Instance = this;
}
/// <summary>
/// Called after the plugin/mod class is initialized on BepInEx (usually before the game starts)
/// </summary>
private void Awake()
{
if (IsAlreadyAwake)
{
this.Logger.LogWarning($"{LCDirectLan.PLUGIN_GUID} is already woken up.");
return;
}
IsAlreadyAwake = true;
config = new ConfigFile(Paths.ConfigPath + $"/{LCDirectLan.PLUGIN_GUID}.cfg", true)
{
SaveOnConfigSet = false
};
/* Default network configuration when joining LAN lobbies */
config.Bind<bool>("Join", "CreateExtraButton", true, new ConfigDescription("Add \"Direct LAN Join\" button to start menu, this will move other buttons on the UI and may not be compatible with other mods that also changes the start UI.\nDisabling this will override the game's default Join button function instead of creating a new button"));
config.Bind<string>("Join", "DefaultAddress", "127.0.0.1", new ConfigDescription("Default IP/Hostname, change-able in the game"));
config.Bind<ushort>("Join", "DefaultPort", 7777, new ConfigDescription("Default Port, change-able in the game"));
config.Bind<bool>("Join", "RememberLastJoinSettings", false, new ConfigDescription("Overwrite the default join configuration values when changed in the game"));
config.Bind<byte>("Join", "HideRawJoinData", 3, new ConfigDescription("Do not display or save the recently joined server data to avoid Server Leak, useful for streamers.\nThis config accepts a bitwise value\nExamples:\n0: Show anything (Disabled/No Hiding)\n1: Hide IP Address\n2. Hide Port Number\n3. Hide IP and Port\n4. Hide Hostname\n7. Hide all of them (IP,Port,Hostname)", new AcceptableValueList<byte>(new byte[] { 0, 1, 2, 3, 4, 7 })));
config.Bind<bool>("Join", "SRVHost_PreferIPv6", false, new ConfigDescription("Prefer IPv6 over IPv4 for SRV Record host lookup when using DNS SRV Record to join"));
/* Default network configuration when hosting LAN lobbies */
config.Bind<bool>("Host", "ListenOnIPv6", false, new ConfigDescription("Should the game listen on IPv6 when hosting instead of IPv4 ?\nDual-Stack Listening is not supported due to the game Unity Transport version."));
config.Bind<ushort>("Host", "DefaultPort", 7777, new ConfigDescription("Default Port for hosting, the default vanilla port is 7777"));
/* Custom Username Feature / Patches */
config.Bind<bool>("Custom Username", "Enabled", false, new ConfigDescription("Enable CustomUsernamePatch ?\nThis patch requires both Server and Client(s) to work correctly, but having this enabled doesn't interfere with vanilla players (or players that didn't have this enabled)."));
config.Bind<string>("Custom Username", "HostDefaultUsername", "Lethal Hoster", new ConfigDescription("Default Username when Hosting a game"));
config.Bind<bool>("Custom Username", "CreateHostUsernameInput", true, new ConfigDescription("Create input field for Host Username in the game's host configuration window, this will move other elements on the UI and may not be compatible with other mods that also changes the host UI.\nAdjust the offsets if necessary\n\nRequires the MergeDefaultUsername to be disabled in order to work correctly"));
config.Bind<Vector3>("Custom Username", "HostUsernameInput_Offset_Y", new Vector3(3, -4, -4), new ConfigDescription("Y Offsets for multiple UI elements that were moved to make room for HostUsernameInput if it is enabled\n\nX value is the Header and the Remote/Local selection button\nY value is the confirm button\nZ value is the back button"));
config.Bind<string>("Custom Username", "JoinDefaultUsername", "Lethal Player", new ConfigDescription("Default Username when Joining a game\nChange-able in the game"));
config.Bind<bool>("Custom Username", "MergeDefaultUsername", false, new ConfigDescription("Copy/Merge/Use JoinDefaultUsername as HostDefaultUsername too, breaks CreateHostUsernameInput functionality"));
/* Game Join/Connect Timeout settings */
config.Bind<bool>("Unity Networking", "Enabled", false, new ConfigDescription("Enable UnityNetworkingPatch ?"));
config.Bind<short>("Unity Networking", "ConnectTimeout", -1, new ConfigDescription("Override the game connect/join timeout in seconds (any values lower than 1 will disable this)"));
/* Latency HUD Patches */
config.Bind<bool>("Latency HUD", "Enabled", true, new ConfigDescription("Enable LatencyHUDPatch ?"));
config.Bind<bool>("Latency HUD", "DisableCustomLatencyRPC", false, new ConfigDescription($"Disable {LCDirectLan.PLUGIN_NAME}'s custom RPC and replace use UnityTransport's GetCurrentRtt() for measuring latency, additional warning feature will be disabled too"));
config.Bind<bool>("Latency HUD", "HideHUDWhileHosting", true, new ConfigDescription("Hide the Latency HUD when hosting a game, since measuring latency to host (yourself) is not really useful"));
config.Bind<bool>("Latency HUD", "RTTMeasurement", true, new ConfigDescription("Measure Round Trip Time (RTT) instead of one-way latency, which is a more accurate latency representation"));
config.Bind<bool>("Latency HUD", "DisplayWarningOnFailure", true, new ConfigDescription("Display an in-game warning when there is a problem with LatencyHUDPatch functionality"));
config.Bind<float>("Latency HUD", "Offset_X", 0.0F, new ConfigDescription("Adjust the X position of the Latency HUD\nHigher value moves the HUD to the right, lower value moves the HUD to the left"));
config.Bind<float>("Latency HUD", "Offset_Y", 0.0F, new ConfigDescription("Adjust the Y position of the Latency HUD\nHigher value moves the HUD to the top, lower value moves the HUD to the bottom"));
config.Bind<float>("Latency HUD", "TextSize", 13.0F, new ConfigDescription("Adjust font size of the Latency HUD (Minimum: 9)"));
config.Save();
// Inject script to detect if we are started in LAN or Online mode
Patches.PreInitSceneScriptPatch.SetLateInjector(InjectPatches);
HarmonyLib.PatchAll(typeof(Patches.PreInitSceneScriptPatch));
this.Logger.LogInfo($"{LCDirectLan.PLUGIN_NAME} is loaded");
}
/// <summary>
/// A dedicated inject method to apply patches (allows early or late patching behavior)
/// </summary>
private void InjectPatches() {
// Do not inject any further patches if we are not in LAN mode
if (!LCDirectLan.IsOnLanMode) {
this.Logger.LogError($"{LCDirectLan.PLUGIN_NAME} should not be injected when game is started on Online (steam) mode");
return;
}
HarmonyLib.PatchAll(typeof(Patches.ConfigurableLAN.MenuManagerPatch));
// Only apply username patches if user wants to
if (GetConfig<bool>("Custom Username", "Enabled"))
{
HarmonyLib.PatchAll(typeof(Patches.CustomUsername.PlayerControllerBPatch));
HarmonyLib.PatchAll(typeof(Patches.CustomUsername.UsernameRPC));
}
// Only apply Unity Networking when the user wants to
if (GetConfig<bool>("Unity Networking", "Enabled"))
{
HarmonyLib.PatchAll(typeof(Patches.UnityNetworking.UnityTransportPatch));
}
// Only apply Latency HUD patches when the user wants to
if (GetConfig<bool>("Latency HUD", "Enabled"))
{
HarmonyLib.PatchAll(typeof(Patches.LatencyHUD.HUDManagerPatch));
HarmonyLib.PatchAll(typeof(Patches.LatencyHUD.LatencyRPC));
}
this.Logger.LogInfo($"{LCDirectLan.PLUGIN_NAME} patches are injected");
}
/// <summary>
/// Send a log to the exposed BepInEx Logging system
/// </summary>
/// <param name="level">The BepInEx LogLevel</param>
/// <param name="data">The log data</param>
public static void Log(BepInEx.Logging.LogLevel level, object data)
{
LCDirectLan.Instance.Logger.Log(level, data);
}
/// <summary>
/// Get a value from Main section of the current BepInEx Runtime Configuration
/// </summary>
/// <typeparam name="T">The config data type</typeparam>
/// <param name="key">The config key</param>
/// <returns>The value casted to the data type, otherwise the default value for that data type</returns>
public static T GetConfig<T>(string key)
{
return LCDirectLan.GetConfig<T>("Main", key);
}
/// <summary>
/// Get a value from the current BepInEx Runtime Configuration
/// </summary>
/// <typeparam name="T">The config data type</typeparam>
/// <param name="section">The config section</param>
/// <param name="key">The config key</param>
/// <returns>The value casted to the data type, otherwise the default value for that data type</returns>
public static T GetConfig<T>(string section, string key)
{
if (config == null) { return default; }
if (config.TryGetEntry<T>(section, key, out ConfigEntry<T> a))
{
return a.Value;
}
LCDirectLan.Log(LogLevel.Warning, $"Cannot get config key {key}, no such key on {section}");
return default;
}
/// <summary>
/// Set a new value on the current BepInEx Runtime Configuration
/// </summary>
/// <typeparam name="T">The config data type</typeparam>
/// <param name="key">The config key</param>
/// <param name="value">The new config value</param>
/// <returns>True on success, false on failure</returns>
public static bool SetConfig<T>(string key, T value)
{
return LCDirectLan.SetConfig<T>(key, value);
}
/// <summary>
/// Set a new value on the current BepInEx Runtime Configuration
/// </summary>
/// <typeparam name="T">The config data type</typeparam>
/// <param name="section">The config section</param>
/// <param name="key">The config key</param>
/// <param name="value">The new config value</param>
/// <returns>True on success, false on failure</returns>
public static bool SetConfig<T>(string section, string key, T value)
{
if (config == null) { return false; }
if (!config.TryGetEntry<T>(section, key, out ConfigEntry<T> a))
{
LCDirectLan.Log(LogLevel.Warning, $"Cannot set config key {key} to {value}, no such key on {section}");
return false;
}
a.Value = value;
return true;
}
/// <summary>
/// Writes current BepInEx Runtime Configuration to disk
/// </summary>
public static void SaveConfig()
{
if (config == null) { return; }
config.Save();
}
}
}