Skip to content

Commit fc1d549

Browse files
committed
Started implementing client side creation of GameState
1 parent 21b16a1 commit fc1d549

5 files changed

Lines changed: 236 additions & 6 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,4 +404,6 @@ build/
404404
dist/
405405
*.spec
406406

407-
# End of https://www.toptal.com/developers/gitignore/api/csharp
407+
# End of https://www.toptal.com/developers/gitignore/api/csharp
408+
409+
run.bat

src/Managers/Multiplayer.cs

Lines changed: 215 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
using HarmonyLib;
2+
using Il2CppMicrosoft.AspNetCore.SignalR.Client;
3+
using PolyMod.ViewModels;
24
using Polytopia.Data;
5+
using PolytopiaBackendBase;
6+
using PolytopiaBackendBase.Common;
7+
using PolytopiaBackendBase.Game;
8+
using PolytopiaBackendBase.Game.BindingModels;
9+
using UnityEngine;
10+
using Newtonsoft.Json;
311

412
namespace PolyMod.Managers;
513

614
public static class Multiplayer
715
{
816
internal const string DEFAULT_SERVER_URL = "https://dev.polydystopia.xyz";
17+
internal const string LOCAL_SERVER_URL = "http://localhost:5051/";
918
private const string GldMarker = "##GLD:";
1019
internal static bool allowGldMods = false;
1120

@@ -18,10 +27,10 @@ internal static void Init()
1827
Harmony.CreateAndPatchAll(typeof(Multiplayer));
1928
BuildConfig buildConfig = BuildConfigHelper.GetSelectedBuildConfig();
2029
buildConfig.buildServerURL = BuildServerURL.Custom;
21-
buildConfig.customServerURL = Plugin.config.backendUrl;
30+
buildConfig.customServerURL = LOCAL_SERVER_URL;
2231

23-
Plugin.logger.LogInfo($"Polydystopia> Server URL set to: {Plugin.config.backendUrl}");
24-
Plugin.logger.LogInfo("Polydystopia> GLD patches applied");
32+
Plugin.logger.LogInfo($"Server URL set to: {Plugin.config.backendUrl}");
33+
Plugin.logger.LogInfo("GLD patches applied");
2534
}
2635

2736
[HarmonyPostfix]
@@ -39,6 +48,17 @@ private static void StartScreen_Start(StartScreen __instance)
3948
__instance.weeklyChallengesButton.gameObject.SetActive(false);
4049
}
4150

51+
[HarmonyPostfix]
52+
[HarmonyPatch(typeof(SystemInfo), nameof(SystemInfo.deviceUniqueIdentifier), MethodType.Getter)]
53+
public static void SteamClient_get_SteamId(ref string __result)
54+
{
55+
if (Plugin.config.overrideDeviceId != string.Empty)
56+
{
57+
__result = Plugin.config.overrideDeviceId;
58+
}
59+
}
60+
61+
4262
/// <summary>
4363
/// After GameState deserialization, check for trailing GLD version ID and set mockedGameLogicData.
4464
/// The server appends "##GLD:" + modGldVersion (int) after the normal serialized data.
@@ -164,4 +184,196 @@ private static void Deserialize_Postfix(GameState __instance, BinaryReader __0)
164184
}
165185
return null;
166186
}
187+
188+
[HarmonyPrefix]
189+
[HarmonyPatch(typeof(BackendAdapter), nameof(BackendAdapter.StartLobbyGame))]
190+
private static bool BackendAdapter_StartLobbyGame(
191+
ref Il2CppSystem.Threading.Tasks.Task<ServerResponse<LobbyGameViewModel>> __result,
192+
BackendAdapter __instance,
193+
StartLobbyBindingModel model)
194+
{
195+
Plugin.logger.LogInfo("BackendAdapter_StartLobbyGame");
196+
_ = HandleStartLobbyGameAsync(__instance, model);
197+
return true;
198+
}
199+
200+
private static async Task HandleStartLobbyGameAsync(BackendAdapter instance, StartLobbyBindingModel model)
201+
{
202+
try
203+
{
204+
var lobbyResponse = await PolytopiaBackendAdapter.Instance.GetLobby(new GetLobbyBindingModel
205+
{
206+
LobbyId = model.LobbyId
207+
});
208+
Plugin.logger.LogInfo($"Lobby processed {lobbyResponse.Success}");
209+
LobbyGameViewModel lobbyGameViewModel = lobbyResponse.Data;
210+
Plugin.logger.LogInfo("Lobby received");
211+
212+
(byte[] serializedGameState, string gameSettingsJson) = CreateMultiplayerGame(
213+
lobbyGameViewModel,
214+
VersionManager.GameVersion,
215+
VersionManager.GameLogicDataVersion
216+
);
217+
218+
Plugin.logger.LogInfo("Game data created");
219+
220+
var setupGameDataViewModel = new SetupGameDataViewModel
221+
{
222+
lobbyId = lobbyGameViewModel.Id.ToString(),
223+
serializedGameState = serializedGameState,
224+
gameSettingsJson = gameSettingsJson
225+
};
226+
227+
var setupData = System.Text.Json.JsonSerializer.Serialize(setupGameDataViewModel);
228+
229+
var serverResponse = await instance.HubConnection.InvokeAsync<ServerResponse<BoolResponseViewModel>>(
230+
"SetupGameData",
231+
setupData,
232+
Il2CppSystem.Threading.CancellationToken.None
233+
);
234+
235+
Plugin.logger.LogInfo("Setup complete: " + serverResponse.Success);
236+
}
237+
catch (Exception ex)
238+
{
239+
Plugin.logger.LogInfo("Error: " + ex.Message);
240+
}
241+
}
242+
243+
public static (byte[] serializedGameState, string gameSettingsJson) CreateMultiplayerGame(LobbyGameViewModel lobby,
244+
int gameVersion, int gameLogicVersion)
245+
{
246+
Console.Write(1);
247+
Console.Write(lobby == null);
248+
var lobbyMapSize = lobby.MapSize;
249+
Console.Write(11);
250+
var settings = new GameSettings();
251+
Console.Write(111);
252+
settings.ApplyLobbySettings(lobby);
253+
Console.Write(111);
254+
if (settings.LiveGamePreset)
255+
{
256+
settings.SetLiveModePreset();
257+
}
258+
Console.Write(3);
259+
foreach (var participatorViewModel in lobby.Participators)
260+
{
261+
Console.Write(4);
262+
if (participatorViewModel.SelectedTribe == 0) participatorViewModel.SelectedTribe = 2; //TODO: Remove later
263+
264+
var humanPlayer = new PlayerData
265+
{
266+
type = PlayerDataType.LocalUser,
267+
state = PlayerDataFriendshipState.Accepted,
268+
knownTribe = true,
269+
tribe = (TribeType)participatorViewModel.SelectedTribe,
270+
tribeMix = (TribeType)participatorViewModel.SelectedTribe, //?
271+
skinType = (SkinType)participatorViewModel.SelectedTribeSkin,
272+
defaultName = participatorViewModel.GetNameInternal()
273+
};
274+
humanPlayer.profile.id = participatorViewModel.UserId;
275+
humanPlayer.profile.SetName(participatorViewModel.GetNameInternal());
276+
SerializationHelpers.FromByteArray<AvatarState>(participatorViewModel.AvatarStateData, out var avatarState);
277+
humanPlayer.profile.avatarState = avatarState;
278+
279+
settings.AddPlayer(humanPlayer);
280+
Console.Write(5);
281+
}
282+
Console.Write(6);
283+
foreach (var botDifficulty in lobby.Bots)
284+
{
285+
var botGuid = Il2CppSystem.Guid.NewGuid();
286+
287+
var botPlayer = new PlayerData
288+
{
289+
type = PlayerDataType.Bot,
290+
state = PlayerDataFriendshipState.Accepted,
291+
knownTribe = true,
292+
tribe = Enum.GetValues<TribeType>().Where(t => t != TribeType.None)
293+
.OrderBy(x => Il2CppSystem.Guid.NewGuid()).First()
294+
};
295+
;
296+
botPlayer.botDifficulty = (BotDifficulty)botDifficulty;
297+
botPlayer.skinType = SkinType.Default; //TODO
298+
botPlayer.defaultName = "Bot" + botGuid;
299+
botPlayer.profile.id = botGuid;
300+
301+
settings.AddPlayer(botPlayer);
302+
}
303+
304+
GameState gameState = new GameState()
305+
{
306+
Version = gameVersion,
307+
Settings = settings,
308+
PlayerStates = new Il2CppSystem.Collections.Generic.List<PlayerState>()
309+
};
310+
311+
for (int index = 0; index < settings.GetPlayerCount(); ++index)
312+
{
313+
PlayerData player = settings.GetPlayer(index);
314+
if (player.type != PlayerDataType.Bot)
315+
{
316+
Il2CppSystem.Nullable<Il2CppSystem.Guid> nullableGuid = new()
317+
{
318+
value = player.profile.id
319+
};
320+
PlayerState playerState = new PlayerState()
321+
{
322+
Id = (byte)(index + 1),
323+
AccountId = nullableGuid,
324+
AutoPlay = player.type == PlayerDataType.Bot,
325+
UserName = player.GetNameInternal(),
326+
tribe = player.tribe,
327+
tribeMix = player.tribeMix,
328+
hasChosenTribe = true,
329+
skinType = player.skinType
330+
};
331+
gameState.PlayerStates.Add(playerState);
332+
Plugin.logger.LogInfo($"Created player: {playerState}");
333+
}
334+
else
335+
{
336+
GameStateUtils.AddAIOpponent(gameState, GameStateUtils.GetRandomPickableTribe(gameState),
337+
GameSettings.HandicapFromDifficulty(player.botDifficulty), player.skinType);
338+
}
339+
}
340+
341+
GameStateUtils.SetPlayerColors(gameState);
342+
GameStateUtils.AddNaturePlayer(gameState);
343+
Plugin.logger.LogInfo("Creating world...");
344+
ushort num = (ushort)Math.Max(lobbyMapSize,
345+
(int)MapDataExtensions.GetMinimumMapSize(gameState.PlayerCount));
346+
gameState.Map = new MapData(num, num);
347+
MapGeneratorSettings generatorSettings = settings.GetMapGeneratorSettings();
348+
new MapGenerator().Generate(gameState, generatorSettings);
349+
Plugin.logger.LogInfo($"Creating initial state for {gameState.PlayerCount} players...");
350+
351+
foreach (PlayerState playerState3 in gameState.PlayerStates)
352+
{
353+
foreach (PlayerState playerState4 in gameState.PlayerStates)
354+
playerState3.aggressions[playerState4.Id] = 0;
355+
if (playerState3.Id != byte.MaxValue)
356+
{
357+
playerState3.Currency = 55;
358+
TribeData data3;
359+
UnitData data4;
360+
if (gameState.GameLogicData.TryGetData(playerState3.tribe, out data3) &&
361+
gameState.GameLogicData.TryGetData(data3.startingUnit.type, out data4))
362+
{
363+
TileData tile = gameState.Map.GetTile(playerState3.startTile);
364+
UnitState unitState = ActionUtils.TrainUnitScored(gameState, playerState3, tile, data4);
365+
unitState.attacked = false;
366+
unitState.moved = false;
367+
}
368+
}
369+
}
370+
371+
Plugin.logger.LogInfo("Session created successfully");
372+
gameState.CommandStack.Add((CommandBase)new StartMatchCommand((byte)1));
373+
374+
var serializedGameState = SerializationHelpers.ToByteArray(gameState, gameState.Version);
375+
376+
return (serializedGameState,
377+
JsonConvert.SerializeObject(gameState.Settings));
378+
}
167379
}

src/Plugin.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ public partial class Plugin : BepInEx.Unity.IL2CPP.BasePlugin
2222
/// <param name="updatePrerelease">Whether to include pre-release versions when updating.</param>
2323
internal record PolyConfig(
2424
bool debug = false,
25-
string backendUrl = Multiplayer.DEFAULT_SERVER_URL,
2625
bool autoUpdate = true,
2726
bool updatePrerelease = false,
28-
bool allowUnsafeIndexes = false
27+
bool allowUnsafeIndexes = false,
28+
string backendUrl = Multiplayer.DEFAULT_SERVER_URL,
29+
string overrideDeviceId = ""
2930
);
3031

3132
/// <summary>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace PolyMod.ViewModels;
2+
3+
public interface IMonoServerResponseData
4+
{
5+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
namespace PolyMod.ViewModels;
3+
public class SetupGameDataViewModel : IMonoServerResponseData
4+
{
5+
public string lobbyId { get; set; }
6+
7+
public byte[] serializedGameState { get; set; }
8+
9+
public string gameSettingsJson { get; set; }
10+
}

0 commit comments

Comments
 (0)