Skip to content

Commit 9a9f213

Browse files
committed
added half working modded games
1 parent 8080356 commit 9a9f213

5 files changed

Lines changed: 516 additions & 114 deletions

File tree

src/Multiplayer/Client.cs

Lines changed: 182 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ internal static void Init()
2727
Harmony.CreateAndPatchAll(typeof(Client));
2828
BuildConfig buildConfig = BuildConfigHelper.GetSelectedBuildConfig();
2929
buildConfig.buildServerURL = BuildServerURL.Custom;
30-
buildConfig.customServerURL = Plugin.config.backendUrl;
30+
buildConfig.customServerURL = LOCAL_SERVER_URL;
3131

3232
Plugin.logger.LogInfo($"Multiplayer> Server URL set to: {Plugin.config.backendUrl}");
3333
Plugin.logger.LogInfo("Multiplayer> GLD patches applied");
@@ -59,130 +59,203 @@ public static void SteamClient_get_SteamId(ref string __result)
5959
}
6060

6161

62-
/// <summary>
63-
/// After GameState deserialization, check for trailing GLD version ID and set mockedGameLogicData.
64-
/// The server appends "##GLD:" + modGldVersion (int) after the normal serialized data.
65-
/// </summary>
66-
[HarmonyPostfix]
67-
[HarmonyPatch(typeof(GameState), nameof(GameState.Deserialize))]
68-
private static void Deserialize_Postfix(GameState __instance, BinaryReader __0)
69-
{
70-
if(!allowGldMods) return;
62+
// /// <summary>
63+
// /// After GameState deserialization, check for trailing GLD version ID and set mockedGameLogicData.
64+
// /// The server appends "##GLD:" + modGldVersion (int) after the normal serialized data.
65+
// /// </summary>
66+
// [HarmonyPostfix]
67+
// [HarmonyPatch(typeof(GameState), nameof(GameState.Deserialize))]
68+
// private static void Deserialize_Postfix(GameState __instance, BinaryReader __0)
69+
// {
70+
// if(!allowGldMods) return;
71+
72+
// Plugin.logger?.LogDebug("Deserialize_Postfix: Entered");
73+
74+
// try
75+
// {
76+
// var reader = __0;
77+
// if (reader == null)
78+
// {
79+
// Plugin.logger?.LogWarning("Deserialize_Postfix: reader is null");
80+
// return;
81+
// }
82+
83+
// var position = reader.BaseStream.Position;
84+
// var length = reader.BaseStream.Length;
85+
// var remaining = length - position;
86+
87+
// Plugin.logger?.LogDebug($"Deserialize_Postfix: Stream position={position}, length={length}, remaining={remaining}");
88+
89+
// // Check if there's more data after normal deserialization
90+
// if (position >= length)
91+
// {
92+
// Plugin.logger?.LogDebug("Deserialize_Postfix: No trailing data (position >= length)");
93+
94+
// var sd = __instance.Seed;
95+
// if (_gldCache.TryGetValue(sd, out var cachedGld))
96+
// {
97+
// __instance.mockedGameLogicData = cachedGld;
98+
// var cachedVersion = _versionCache.GetValueOrDefault(sd, -1);
99+
// Plugin.logger?.LogInfo($"Deserialize_Postfix: Applied cached GLD for Seed={sd}, ModGldVersion={cachedVersion}");
100+
// }
101+
// return;
102+
// }
103+
104+
// Plugin.logger?.LogDebug($"Deserialize_Postfix: Found {remaining} bytes of trailing data, attempting to read marker");
105+
106+
// var marker = reader.ReadString();
107+
// Plugin.logger?.LogDebug($"Deserialize_Postfix: Read marker string: '{marker}'");
108+
109+
// if (marker != GldMarker)
110+
// {
111+
// Plugin.logger?.LogDebug($"Deserialize_Postfix: Marker mismatch - expected '{GldMarker}', got '{marker}'");
112+
// return;
113+
// }
114+
115+
// Plugin.logger?.LogInfo($"Deserialize_Postfix: Found GLD marker '{GldMarker}'");
116+
117+
// var modGldVersion = reader.ReadInt32();
118+
// Plugin.logger?.LogInfo($"Deserialize_Postfix: Found embedded ModGldVersion: {modGldVersion}");
119+
120+
// Plugin.logger?.LogDebug($"Deserialize_Postfix: Fetching GLD from server for version {modGldVersion}");
121+
// var gldJson = FetchGldById(modGldVersion);
122+
// if (string.IsNullOrEmpty(gldJson))
123+
// {
124+
// Plugin.logger?.LogError($"Deserialize_Postfix: Failed to fetch GLD for ModGldVersion: {modGldVersion}");
125+
// return;
126+
// }
127+
128+
// Plugin.logger?.LogDebug($"Deserialize_Postfix: Parsing GLD JSON ({gldJson.Length} chars)");
129+
130+
// var customGld = new GameLogicData();
131+
// customGld.Parse(gldJson);
132+
// __instance.mockedGameLogicData = customGld;
133+
134+
// // Cache for subsequent deserializations (rewinds, reloads)
135+
// var seed = __instance.Seed;
136+
// _gldCache[seed] = customGld;
137+
// _versionCache[seed] = modGldVersion;
138+
139+
// Plugin.logger?.LogInfo($"Deserialize_Postfix: Successfully set mockedGameLogicData from ModGldVersion: {modGldVersion}, cached for Seed={seed}");
140+
// }
141+
// catch (EndOfStreamException)
142+
// {
143+
// Plugin.logger?.LogDebug("Deserialize_Postfix: EndOfStreamException - no trailing data");
144+
// }
145+
// catch (Exception ex)
146+
// {
147+
// Plugin.logger?.LogError($"Deserialize_Postfix: Exception: {ex.GetType().Name}: {ex.Message}");
148+
// Plugin.logger?.LogDebug($"Deserialize_Postfix: Stack trace: {ex.StackTrace}");
149+
// }
150+
// }
151+
152+
// /// <summary>
153+
// /// Fetch GLD from server using ModGldVersion ID
154+
// /// </summary>
155+
// private static string? FetchGldById(int modGldVersion)
156+
// {
157+
// if(!allowGldMods) return null;
158+
// try
159+
// {
160+
// using var client = new HttpClient();
161+
// var url = $"{Plugin.config.backendUrl.TrimEnd('/')}/api/mods/gld/{modGldVersion}";
162+
// Plugin.logger?.LogDebug($"FetchGldById: Requesting URL: {url}");
163+
164+
// var response = client.GetAsync(url).Result;
165+
// Plugin.logger?.LogDebug($"FetchGldById: Response status: {response.StatusCode}");
166+
167+
// if (response.IsSuccessStatusCode)
168+
// {
169+
// var gld = response.Content.ReadAsStringAsync().Result;
170+
// Plugin.logger?.LogInfo($"FetchGldById: Successfully fetched mod GLD ({gld.Length} chars)");
171+
// return gld;
172+
// }
173+
174+
// var errorContent = response.Content.ReadAsStringAsync().Result;
175+
// Plugin.logger?.LogError($"FetchGldById: Failed with status {response.StatusCode}: {errorContent}");
176+
// }
177+
// catch (Exception ex)
178+
// {
179+
// Plugin.logger?.LogError($"FetchGldById: Exception: {ex.GetType().Name}: {ex.Message}");
180+
// if (ex.InnerException != null)
181+
// {
182+
// Plugin.logger?.LogError($"FetchGldById: Inner exception: {ex.InnerException.Message}");
183+
// }
184+
// }
185+
// return null;
186+
// }
71187

72-
Plugin.logger?.LogDebug("Deserialize_Postfix: Entered");
73-
74-
try
75-
{
76-
var reader = __0;
77-
if (reader == null)
78-
{
79-
Plugin.logger?.LogWarning("Deserialize_Postfix: reader is null");
80-
return;
81-
}
82-
83-
var position = reader.BaseStream.Position;
84-
var length = reader.BaseStream.Length;
85-
var remaining = length - position;
86-
87-
Plugin.logger?.LogDebug($"Deserialize_Postfix: Stream position={position}, length={length}, remaining={remaining}");
188+
[HarmonyPrefix]
189+
[HarmonyPatch(typeof(ClientBase), nameof(ClientBase.SendCommand))]
190+
private static bool ClientBase_SendCommand(
191+
ClientBase __instance,
192+
CommandBase command)
193+
{
88194

89-
// Check if there's more data after normal deserialization
90-
if (position >= length)
91-
{
92-
Plugin.logger?.LogDebug("Deserialize_Postfix: No trailing data (position >= length)");
195+
Plugin.logger.LogInfo("Multiplayer> ClientBase_SendCommand");
196+
Il2CppSystem.Threading.Tasks.Task<ServerResponse<BoolResponseViewModel>> task = new();
197+
var taskCompletionSource = new Il2CppSystem.Threading.Tasks.TaskCompletionSource<ServerResponse<BoolResponseViewModel>>();
93198

94-
var sd = __instance.Seed;
95-
if (_gldCache.TryGetValue(sd, out var cachedGld))
96-
{
97-
__instance.mockedGameLogicData = cachedGld;
98-
var cachedVersion = _versionCache.GetValueOrDefault(sd, -1);
99-
Plugin.logger?.LogInfo($"Deserialize_Postfix: Applied cached GLD for Seed={sd}, ModGldVersion={cachedVersion}");
100-
}
101-
return;
102-
}
199+
_ = HandleSendCommandModded(taskCompletionSource, __instance, command);
103200

104-
Plugin.logger?.LogDebug($"Deserialize_Postfix: Found {remaining} bytes of trailing data, attempting to read marker");
201+
task = taskCompletionSource.Task;
105202

106-
var marker = reader.ReadString();
107-
Plugin.logger?.LogDebug($"Deserialize_Postfix: Read marker string: '{marker}'");
203+
return false;
204+
}
108205

109-
if (marker != GldMarker)
206+
private static async System.Threading.Tasks.Task HandleSendCommandModded(
207+
Il2CppSystem.Threading.Tasks.TaskCompletionSource<ServerResponse<BoolResponseViewModel>> tcs,
208+
ClientBase client,
209+
CommandBase command)
210+
{
211+
try
212+
{
213+
if (!client.CurrentGameId.HasValue)
110214
{
111-
Plugin.logger?.LogDebug($"Deserialize_Postfix: Marker mismatch - expected '{GldMarker}', got '{marker}'");
215+
Console.Write("Tried to perform and send command but no GameId was set");
112216
return;
113217
}
114-
115-
Plugin.logger?.LogInfo($"Deserialize_Postfix: Found GLD marker '{GldMarker}'");
116-
117-
var modGldVersion = reader.ReadInt32();
118-
Plugin.logger?.LogInfo($"Deserialize_Postfix: Found embedded ModGldVersion: {modGldVersion}");
119-
120-
Plugin.logger?.LogDebug($"Deserialize_Postfix: Fetching GLD from server for version {modGldVersion}");
121-
var gldJson = FetchGldById(modGldVersion);
122-
if (string.IsNullOrEmpty(gldJson))
218+
if (!ClientActionManager.CanReceiveCommand(command, client.GameState))
123219
{
124-
Plugin.logger?.LogError($"Deserialize_Postfix: Failed to fetch GLD for ModGldVersion: {modGldVersion}");
220+
Console.Write("Tried to send invalid command");
125221
return;
126222
}
223+
uint currentResetId = client.resets;
224+
int count = client.GameState.CommandStack.Count;
225+
var list = new Il2CppSystem.Collections.Generic.List<CommandBase>();
226+
list.Add(command);
227+
client.ActionManager.ExecuteCommands(list);
228+
await client.SendCommandToServer(command, count);
127229

128-
Plugin.logger?.LogDebug($"Deserialize_Postfix: Parsing GLD JSON ({gldJson.Length} chars)");
230+
var serializedGameState = SerializationHelpers.ToByteArray(client.GameState, client.GameState.Version);
129231

130-
var customGld = new GameLogicData();
131-
customGld.Parse(gldJson);
132-
__instance.mockedGameLogicData = customGld;
232+
var succ = GameStateSummary.FromGameStateByteArray(serializedGameState,
233+
out GameStateSummary stateSummary, out var gameState);
133234

134-
// Cache for subsequent deserializations (rewinds, reloads)
135-
var seed = __instance.Seed;
136-
_gldCache[seed] = customGld;
137-
_versionCache[seed] = modGldVersion;
235+
var serializedGameSummary = SerializationHelpers.ToByteArray(stateSummary, gameState.Version);
138236

139-
Plugin.logger?.LogInfo($"Deserialize_Postfix: Successfully set mockedGameLogicData from ModGldVersion: {modGldVersion}, cached for Seed={seed}");
140-
}
141-
catch (EndOfStreamException)
142-
{
143-
Plugin.logger?.LogDebug("Deserialize_Postfix: EndOfStreamException - no trailing data");
144-
}
145-
catch (Exception ex)
146-
{
147-
Plugin.logger?.LogError($"Deserialize_Postfix: Exception: {ex.GetType().Name}: {ex.Message}");
148-
Plugin.logger?.LogDebug($"Deserialize_Postfix: Stack trace: {ex.StackTrace}");
149-
}
150-
}
151-
152-
/// <summary>
153-
/// Fetch GLD from server using ModGldVersion ID
154-
/// </summary>
155-
private static string? FetchGldById(int modGldVersion)
156-
{
157-
if(!allowGldMods) return null;
158-
try
159-
{
160-
using var client = new HttpClient();
161-
var url = $"{Plugin.config.backendUrl.TrimEnd('/')}/api/mods/gld/{modGldVersion}";
162-
Plugin.logger?.LogDebug($"FetchGldById: Requesting URL: {url}");
163-
164-
var response = client.GetAsync(url).Result;
165-
Plugin.logger?.LogDebug($"FetchGldById: Response status: {response.StatusCode}");
166-
167-
if (response.IsSuccessStatusCode)
237+
var setupGameDataViewModel = new SetupGameStateViewModel
168238
{
169-
var gld = response.Content.ReadAsStringAsync().Result;
170-
Plugin.logger?.LogInfo($"FetchGldById: Successfully fetched mod GLD ({gld.Length} chars)");
171-
return gld;
172-
}
239+
gameId = client.gameId.ToString(),
240+
serializedGameState = serializedGameState,
241+
serializedGameSummary = serializedGameSummary,
242+
gameSettingsJson = ""
243+
};
244+
245+
var setupData = System.Text.Json.JsonSerializer.Serialize(setupGameDataViewModel);
173246

174-
var errorContent = response.Content.ReadAsStringAsync().Result;
175-
Plugin.logger?.LogError($"FetchGldById: Failed with status {response.StatusCode}: {errorContent}");
247+
var serverResponse = await PolytopiaBackendAdapter.Instance.HubConnection.InvokeAsync<ServerResponse<BoolResponseViewModel>>(
248+
"UpdateGameStateModded",
249+
setupData,
250+
Il2CppSystem.Threading.CancellationToken.None
251+
);
252+
tcs.SetResult(serverResponse);
176253
}
177254
catch (Exception ex)
178255
{
179-
Plugin.logger?.LogError($"FetchGldById: Exception: {ex.GetType().Name}: {ex.Message}");
180-
if (ex.InnerException != null)
181-
{
182-
Plugin.logger?.LogError($"FetchGldById: Inner exception: {ex.InnerException.Message}");
183-
}
256+
Plugin.logger.LogError("Multiplayer> Error during HandleSendCommandModded: " + ex.Message);
257+
tcs.SetException(new Il2CppSystem.Exception(ex.Message));
184258
}
185-
return null;
186259
}
187260

188261
[HarmonyPrefix]
@@ -226,10 +299,15 @@ private static async System.Threading.Tasks.Task HandleStartLobbyGameModded(
226299

227300
Plugin.logger.LogInfo("Multiplayer> GameState and Settiings created");
228301

229-
var setupGameDataViewModel = new SetupGameDataViewModel
302+
var succ = GameStateSummary.FromGameStateByteArray(serializedGameState,
303+
out GameStateSummary stateSummary, out var gameState);
304+
305+
var serializedGameSummary = SerializationHelpers.ToByteArray(stateSummary, gameState.Version);
306+
var setupGameDataViewModel = new SetupGameStateViewModel
230307
{
231308
lobbyId = lobbyGameViewModel.Id.ToString(),
232309
serializedGameState = serializedGameState,
310+
serializedGameSummary = serializedGameSummary,
233311
gameSettingsJson = gameSettingsJson
234312
};
235313

0 commit comments

Comments
 (0)