-
-
Notifications
You must be signed in to change notification settings - Fork 10
v1.3 #139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
IExploitableMan
wants to merge
17
commits into
main
Choose a base branch
from
dev1.3
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
v1.3 #139
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
3967b7c
Minor stuff
johnklipi 8176701
Merge branch 'main' into dev1.3
Artemis21 8bc3eb0
Merge
johnklipi e77df1f
Added dystopia stuff
johnklipi 21b16a1
Added bool which lets u disable gld mods
johnklipi fc1d549
Started implementing client side creation of GameState
johnklipi 3db6ce2
fixed malformed player account id
johnklipi 03498e8
Moved stuff a bit and finished implementing client side creation of g…
johnklipi 06ba9b2
unhardcoded backend url
johnklipi 8080356
Merge branch 'main' into dev1.3
johnklipi 9a9f213
added half working modded games
johnklipi f09d591
merge
johnklipi e67a728
Implemented working online battles with mods
johnklipi 209adf2
merge
johnklipi 12bf062
removed all references to old start screen
johnklipi 9b5d3ae
minor stuff
johnklipi 50ab1ed
updated to 2.17.2
johnklipi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,374 @@ | ||
| using HarmonyLib; | ||
| using Il2CppMicrosoft.AspNetCore.SignalR.Client; | ||
| using PolyMod.Multiplayer.ViewModels; | ||
| using Polytopia.Data; | ||
| using PolytopiaBackendBase; | ||
| using PolytopiaBackendBase.Common; | ||
| using PolytopiaBackendBase.Game; | ||
| using PolytopiaBackendBase.Game.BindingModels; | ||
| using UnityEngine; | ||
| using Newtonsoft.Json; | ||
|
|
||
| namespace PolyMod.Multiplayer; | ||
|
|
||
| public static class Client | ||
| { | ||
| internal const string DEFAULT_SERVER_URL = "https://dev.polydystopia.xyz"; | ||
| internal const string LOCAL_SERVER_URL = "http://localhost:5051/"; | ||
| private const string GldMarker = "##GLD:"; | ||
| internal static bool allowGldMods = false; | ||
|
|
||
| // Cache parsed GLD by game Seed to handle rewinds/reloads | ||
| private static readonly Dictionary<int, GameLogicData> _gldCache = new(); | ||
| private static readonly Dictionary<int, int> _versionCache = new(); // Seed -> modGldVersion | ||
|
|
||
| internal static void Init() | ||
| { | ||
| Harmony.CreateAndPatchAll(typeof(Client)); | ||
| BuildConfig buildConfig = BuildConfigHelper.GetSelectedBuildConfig(); | ||
| buildConfig.buildServerURL = BuildServerURL.Custom; | ||
| buildConfig.customServerURL = LOCAL_SERVER_URL; | ||
|
|
||
| Plugin.logger.LogInfo($"Multiplayer> Server URL set to: {Plugin.config.backendUrl}"); | ||
| Plugin.logger.LogInfo("Multiplayer> GLD patches applied"); | ||
| } | ||
|
|
||
| [HarmonyPostfix] | ||
| [HarmonyPatch(typeof(MultiplayerSelectionScreen), nameof(MultiplayerSelectionScreen.Show))] | ||
| public static void MultiplayerScreen_Show(MultiplayerSelectionScreen __instance) | ||
| { | ||
| __instance.TournamentsButton.gameObject.SetActive(false); | ||
| } | ||
|
|
||
| [HarmonyPostfix] | ||
| [HarmonyPatch(typeof(StartScreen_UI2), nameof(StartScreen_UI2.RunLayout))] | ||
| private static void StartScreen_UI2_RunLayout(StartScreen_UI2 __instance) | ||
| { | ||
| __instance.highscoreButton.gameObject.SetActive(false); | ||
| __instance.weeklyChallengeButton.gameObject.SetActive(false); | ||
| } | ||
|
|
||
| [HarmonyPostfix] | ||
| [HarmonyPatch(typeof(SystemInfo), nameof(SystemInfo.deviceUniqueIdentifier), MethodType.Getter)] | ||
| public static void SteamClient_get_SteamId(ref string __result) | ||
| { | ||
| if (Plugin.config.overrideDeviceId != string.Empty) | ||
| { | ||
| __result = Plugin.config.overrideDeviceId; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| [HarmonyPrefix] | ||
| [HarmonyPatch(typeof(ClientBase), nameof(ClientBase.SendCommand))] | ||
| private static bool ClientBase_SendCommand( | ||
| ClientBase __instance, | ||
| CommandBase command) | ||
| { | ||
|
|
||
| Plugin.logger.LogInfo("Multiplayer> ClientBase_SendCommand"); | ||
| Il2CppSystem.Threading.Tasks.Task<ServerResponse<BoolResponseViewModel>> task = new(); | ||
| var taskCompletionSource = new Il2CppSystem.Threading.Tasks.TaskCompletionSource<ServerResponse<BoolResponseViewModel>>(); | ||
|
|
||
| _ = ClientBase_SendCommand_Async(taskCompletionSource, __instance, command); | ||
|
|
||
| task = taskCompletionSource.Task; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private static async System.Threading.Tasks.Task ClientBase_SendCommand_Async( | ||
| Il2CppSystem.Threading.Tasks.TaskCompletionSource<ServerResponse<BoolResponseViewModel>> tcs, | ||
| ClientBase client, | ||
| CommandBase command) | ||
| { | ||
| try | ||
| { | ||
| if (!client.CurrentGameId.HasValue) | ||
| { | ||
| Console.Write("Tried to perform and send command but no GameId was set"); | ||
| return; | ||
| } | ||
| if (!ClientActionManager.CanReceiveCommand(command, client.GameState)) | ||
| { | ||
| Console.Write("Tried to send invalid command"); | ||
| return; | ||
| } | ||
| uint currentResetId = client.resets; | ||
| int count = client.GameState.CommandStack.Count; | ||
| var list = new Il2CppSystem.Collections.Generic.List<CommandBase>(); | ||
| list.Add(command); | ||
| client.ActionManager.ExecuteCommands(list); | ||
| await client.SendCommandToServer(command, count); | ||
|
|
||
| var serializedGameState = SerializationHelpers.ToByteArray(client.GameState, client.GameState.Version); | ||
|
|
||
| var succ = GameStateSummary.FromGameStateByteArray(serializedGameState, | ||
| out GameStateSummary stateSummary, out var gameState); | ||
|
|
||
| var serializedGameSummary = SerializationHelpers.ToByteArray(stateSummary, gameState.Version); | ||
|
|
||
|
|
||
| client.GameState.TryGetPlayer(client.GameState.CurrentPlayer, out PlayerState playerState); | ||
| var currentPlayerId = ""; | ||
| if(playerState.AccountId.HasValue) | ||
| { | ||
| currentPlayerId = playerState.AccountId.Value.ToString(); | ||
| } | ||
| var setupGameDataViewModel = new ModdedGameStateViewModel | ||
| { | ||
| gameId = client.gameId.ToString(), | ||
| serializedGameState = serializedGameState, | ||
| serializedGameSummary = serializedGameSummary, | ||
| gameSettingsJson = "", | ||
| currentPlayerId = currentPlayerId, | ||
| IsEndTurnCommand = command.GetCommandType() == CommandType.EndTurn | ||
| }; | ||
|
|
||
|
|
||
|
|
||
| var setupData = System.Text.Json.JsonSerializer.Serialize(setupGameDataViewModel); | ||
|
|
||
| var serverResponse = await PolytopiaBackendAdapter.Instance.HubConnection.InvokeAsync<ServerResponse<BoolResponseViewModel>>( | ||
| "UpdateGameStateModded", | ||
| setupData, | ||
| Il2CppSystem.Threading.CancellationToken.None | ||
| ); | ||
| tcs.SetResult(serverResponse); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Plugin.logger.LogError("Multiplayer> Error during HandleSendCommandModded: " + ex.Message); | ||
| tcs.SetException(new Il2CppSystem.Exception(ex.Message)); | ||
| } | ||
| } | ||
|
|
||
| [HarmonyPrefix] | ||
| [HarmonyPatch(typeof(BackendAdapter), nameof(BackendAdapter.StartLobbyGame))] | ||
| private static bool BackendAdapter_StartLobbyGame_Modded( | ||
| ref Il2CppSystem.Threading.Tasks.Task<ServerResponse<LobbyGameViewModel>> __result, | ||
| BackendAdapter __instance, | ||
| StartLobbyBindingModel model) | ||
| { | ||
| Plugin.logger.LogInfo("Multiplayer> BackendAdapter_StartLobbyGame_Modded"); | ||
| var taskCompletionSource = new Il2CppSystem.Threading.Tasks.TaskCompletionSource<ServerResponse<LobbyGameViewModel>>(); | ||
|
|
||
| _ = BackendAdapter_StartLobbyGame_Async(taskCompletionSource, __instance, model); | ||
|
|
||
| __result = taskCompletionSource.Task; | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| private static async System.Threading.Tasks.Task BackendAdapter_StartLobbyGame_Async( | ||
| Il2CppSystem.Threading.Tasks.TaskCompletionSource<ServerResponse<LobbyGameViewModel>> tcs, | ||
| BackendAdapter instance, | ||
| StartLobbyBindingModel model) | ||
| { | ||
| try | ||
| { | ||
| var lobbyResponse = await PolytopiaBackendAdapter.Instance.GetLobby(new GetLobbyBindingModel | ||
| { | ||
| LobbyId = model.LobbyId | ||
| }); | ||
|
|
||
| Plugin.logger.LogInfo($"Multiplayer> Lobby processed {lobbyResponse.Success}"); | ||
| LobbyGameViewModel lobbyGameViewModel = lobbyResponse.Data; | ||
| Plugin.logger.LogInfo("Multiplayer> Lobby received"); | ||
|
|
||
| (byte[] serializedGameState, string gameSettingsJson) = CreateMultiplayerGame( | ||
| lobbyGameViewModel, | ||
| VersionManager.GameVersion, | ||
| VersionManager.GameLogicDataVersion | ||
| ); | ||
|
|
||
| Plugin.logger.LogInfo("Multiplayer> GameState and Settiings created"); | ||
|
|
||
| var succ = GameStateSummary.FromGameStateByteArray(serializedGameState, | ||
| out GameStateSummary stateSummary, out var gameState); | ||
|
|
||
| var serializedGameSummary = SerializationHelpers.ToByteArray(stateSummary, gameState.Version); | ||
| var setupGameDataViewModel = new ModdedGameStateViewModel | ||
| { | ||
| lobbyId = lobbyGameViewModel.Id.ToString(), | ||
| serializedGameState = serializedGameState, | ||
| serializedGameSummary = serializedGameSummary, | ||
| gameSettingsJson = gameSettingsJson | ||
| }; | ||
|
|
||
| var setupData = System.Text.Json.JsonSerializer.Serialize(setupGameDataViewModel); | ||
|
|
||
| var serverResponse = await instance.HubConnection.InvokeAsync<ServerResponse<LobbyGameViewModel>>( | ||
| "StartLobbyGameModded", | ||
| setupData, | ||
| Il2CppSystem.Threading.CancellationToken.None | ||
| ); | ||
| Plugin.logger.LogInfo("Multiplayer> Invoked StartLobbyGameModded"); | ||
| tcs.SetResult(serverResponse); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| Plugin.logger.LogError("Multiplayer> Error during HandleStartLobbyGameModded: " + ex.Message); | ||
| tcs.SetException(new Il2CppSystem.Exception(ex.Message)); | ||
| } | ||
| } | ||
|
|
||
| public static (byte[] serializedGameState, string gameSettingsJson) CreateMultiplayerGame(LobbyGameViewModel lobby, | ||
| int gameVersion, int gameLogicVersion) | ||
| { | ||
| var lobbyMapSize = lobby.MapSize; | ||
| var settings = new GameSettings(); | ||
| settings.ApplyLobbySettings(lobby); | ||
| if (settings.LiveGamePreset) | ||
| { | ||
| settings.SetLiveModePreset(); | ||
| } | ||
| foreach (var participatorViewModel in lobby.Participators) | ||
| { | ||
| var humanPlayer = new PlayerData | ||
| { | ||
| type = PlayerDataType.LocalUser, | ||
| state = PlayerDataFriendshipState.Accepted, | ||
| knownTribe = true, | ||
| tribe = (TribeType)participatorViewModel.SelectedTribe, | ||
| tribeMix = TribeType.None, // TribeMix is byte too | ||
| skinType = (SkinType)participatorViewModel.SelectedTribeSkin, | ||
| defaultName = participatorViewModel.GetNameInternal() | ||
| }; | ||
| humanPlayer.profile.id = participatorViewModel.UserId; | ||
| humanPlayer.profile.SetName(participatorViewModel.GetNameInternal()); | ||
| SerializationHelpers.FromByteArray<AvatarState>(participatorViewModel.AvatarStateData, out var avatarState); | ||
| humanPlayer.profile.avatarState = avatarState; | ||
|
|
||
| settings.AddPlayer(humanPlayer); | ||
| } | ||
|
|
||
| foreach (var botDifficulty in lobby.Bots) | ||
| { | ||
| var botGuid = Il2CppSystem.Guid.NewGuid(); | ||
|
|
||
| var botPlayer = new PlayerData | ||
| { | ||
| type = PlayerDataType.Bot, | ||
| state = PlayerDataFriendshipState.Accepted, | ||
| knownTribe = true, | ||
| tribe = Enum.GetValues<TribeType>().Where(t => t != TribeType.None) | ||
| .OrderBy(x => Il2CppSystem.Guid.NewGuid()).First() | ||
| }; | ||
| ; | ||
| botPlayer.botDifficulty = (BotDifficulty)botDifficulty; | ||
| botPlayer.skinType = SkinType.Default; | ||
| botPlayer.defaultName = "Bot" + botGuid; | ||
| botPlayer.profile.id = botGuid; | ||
|
|
||
| settings.AddPlayer(botPlayer); | ||
| } | ||
|
|
||
| GameState gameState = new GameState() | ||
| { | ||
| Version = gameVersion, | ||
| Settings = settings, | ||
| PlayerStates = new Il2CppSystem.Collections.Generic.List<PlayerState>() | ||
| }; | ||
|
|
||
| for (int index = 0; index < settings.GetPlayerCount(); ++index) | ||
| { | ||
| PlayerData player = settings.GetPlayer(index); | ||
| if (player.type != PlayerDataType.Bot) | ||
| { | ||
| var nullableGuid = new Il2CppSystem.Nullable<Il2CppSystem.Guid>(player.profile.id); | ||
| if (!nullableGuid.HasValue) | ||
| { | ||
| throw new Exception("GUID was not set properly!"); | ||
| } | ||
| PlayerState playerState = new PlayerState() | ||
| { | ||
| Id = (byte)(index + 1), | ||
| AccountId = nullableGuid, | ||
| AutoPlay = player.type == PlayerDataType.Bot, | ||
| UserName = player.GetNameInternal(), | ||
| tribe = player.tribe, | ||
| tribeMix = player.tribeMix, | ||
| hasChosenTribe = true, | ||
| skinType = player.skinType | ||
| }; | ||
| gameState.PlayerStates.Add(playerState); | ||
| Plugin.logger.LogInfo($"Multiplayer> Created player: {playerState}"); | ||
| } | ||
| else | ||
| { | ||
| GameStateUtils.AddAIOpponent(gameState, GameStateUtils.GetRandomPickableTribe(gameState), | ||
| GameSettings.HandicapFromDifficulty(player.botDifficulty), player.skinType); | ||
| } | ||
| } | ||
|
|
||
| GameStateUtils.SetPlayerColors(gameState); | ||
| GameStateUtils.AddNaturePlayer(gameState); | ||
|
|
||
| Plugin.logger.LogInfo("Multiplayer> Creating world..."); | ||
|
|
||
| ushort num = (ushort)Math.Max(lobbyMapSize, | ||
| (int)MapDataExtensions.GetMinimumMapSize(gameState.PlayerCount)); | ||
| gameState.Map = new MapData(num, num); | ||
| MapGeneratorSettings generatorSettings = settings.GetMapGeneratorSettings(); | ||
| new MapGenerator().Generate(gameState, generatorSettings); | ||
|
|
||
| Plugin.logger.LogInfo($"Multiplayer> Creating initial state for {gameState.PlayerCount} players..."); | ||
|
|
||
| foreach (PlayerState player in gameState.PlayerStates) | ||
| { | ||
| foreach (PlayerState otherPlayer in gameState.PlayerStates) | ||
| player.aggressions[otherPlayer.Id] = 0; | ||
|
|
||
| if (player.Id != byte.MaxValue && gameState.GameLogicData.TryGetData(player.tribe, out TribeData tribeData)) | ||
| { | ||
| player.Currency = tribeData.startingStars; | ||
| TileData tile = gameState.Map.GetTile(player.startTile); | ||
| UnitState unitState = ActionUtils.TrainUnitScored(gameState, player, tile, tribeData.startingUnit); | ||
| unitState.attacked = false; | ||
| unitState.moved = false; | ||
| } | ||
| } | ||
|
|
||
| Plugin.logger.LogInfo("Multiplayer> Session created successfully"); | ||
|
|
||
| gameState.CommandStack.Add((CommandBase)new StartMatchCommand((byte)1)); | ||
|
|
||
| var serializedGameState = SerializationHelpers.ToByteArray(gameState, gameState.Version); | ||
|
|
||
| return (serializedGameState, | ||
| JsonConvert.SerializeObject(gameState.Settings)); | ||
| } | ||
|
|
||
| // FIX FOR NATURE PLAYER. BOTS ARENT IMPLEMENTED YET | ||
|
|
||
| [HarmonyPrefix] | ||
| [HarmonyPatch(typeof(GameState), nameof(GameState.EndPlayerTurn))] | ||
| private static bool GameState_EndPlayerTurn(GameState __instance, bool newTurn = false) | ||
| { | ||
| Console.Write("GameState_EndPlayerTurn"); | ||
| __instance.CurrentPlayerIndex++; | ||
| if (__instance.CurrentPlayerIndex >=__instance. PlayerStates.Count) | ||
| { | ||
| __instance.CurrentPlayerIndex = 0; | ||
| newTurn = true; | ||
| } | ||
|
|
||
| var currentPlayer = __instance.PlayerStates[__instance.CurrentPlayerIndex]; | ||
| if (!currentPlayer.IsAlive(__instance)) | ||
| { | ||
| __instance.EndPlayerTurn(newTurn); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Recursion?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. copied from vanilla code |
||
| } | ||
| else if (newTurn) | ||
| { | ||
| __instance.CurrentTurn++; | ||
| } | ||
|
|
||
| if(currentPlayer.AutoPlay) | ||
| { | ||
| __instance.CommandStack.Add(new EndTurnCommand(currentPlayer.Id)); | ||
| } | ||
| Console.Write("finished"); | ||
| return false; | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How this even compiles btw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
idk can you stop reviewing something i didnt submit for review thx