Skip to content

Commit

Permalink
make the Change Connection Information button actually show a popup t…
Browse files Browse the repository at this point in the history
…hat lets you make conn info changes (WIP)
  • Loading branch information
Ixrec committed Mar 9, 2025
1 parent f6a2790 commit 3606cab
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 19 deletions.
49 changes: 38 additions & 11 deletions Source/MainMenu/APSaveManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,15 @@ private static void SetChangeButtonVisible(bool visible, Transform saveSlotButto
var changeTextGO = changeButtonT.Find("Text (TMP)");
changeTextGO.GetComponent<TMPro.TextMeshProUGUI>().text = "Change\nConnection\nInformation";

// unbreak some button implementation details that Object.Instantiate() couldn't magically handle for us
var changeUICB = changeButtonT.GetComponent<UIControlButton>();
AccessTools.FieldRefAccess<UIControlButton, AbstractConditionComp[]>("_onSubmitActions").Invoke(changeUICB) = []; // get rid of the Delete actions

// unbreak some button implementation details that Object.Instantiate() couldn't magically handle for us
AccessTools.FieldRefAccess<UIControlButton, UIControlGroup>("belongGroup").Invoke(changeUICB) = changeButtonT.GetComponentInParent<UIControlGroup>();
AccessTools.FieldRefAccess<UIControlButton, AbstractConditionComp[]>("_activateConditions").Invoke(changeUICB) = []; // this just needs to be non-null
AccessTools.FieldRefAccess<UIControlButton, AutoDisableAnimator>("_autoDisableAnimator").Invoke(changeUICB) = changeButtonT.GetComponent<AutoDisableAnimator>();
AccessTools.FieldRefAccess<UIControlButton, Selectable>("_button").Invoke(changeUICB) = changeButtonT.GetComponent<Button>();
// The last thing this button needs to work can't be done here; see the OnBecome(Not)Interactable patches below
// The last things this button needs to work can't be done here; see the patches below
} else if (saveSlotButtonsT.childCount == 5) {
var changePaddingT = saveSlotButtonsT.GetChild(3);
changePaddingT.gameObject.SetActive(visible);
Expand Down Expand Up @@ -210,6 +212,22 @@ public static void UIControlButton_OnBecomeNotInteractable(UIControlButton __ins
}
}

[HarmonyPrefix, HarmonyPatch(typeof(UIControlButton), "SubmitImplementation")]
public static async void UIControlButton_SubmitImplementation(UIControlButton __instance) {
Log.Info($"UIControlButton_SubmitImplementation {__instance.name}");
if (__instance.name.StartsWith("APRandomizer_ChangeConnectionInfo_Slot")) {
int slotIndex = int.Parse(__instance.name.Substring("APRandomizer_ChangeConnectionInfo_Slot".Length));
var oldConnData = apSaveSlots[slotIndex].apConnectionData;

HideSaveMenu();
var newConnData = await ConnectionAndPopups.ChangeConnectionInfo(oldConnData);
UnHideSaveMenu();

apSaveSlots[slotIndex].apConnectionData = newConnData;
ScheduleWriteToCurrentSaveFile();
}
}

/*
* In the end I decided not to use SaveManager._fileSystem directly, but
* since I did figure out how to "break in" and use it, here's how:
Expand All @@ -234,6 +252,22 @@ public static void UIControlButton_OnBecomeNotInteractable(UIControlButton __ins
wab.Invoke(fileSystem, new object[] { "saveslot0", bytes });
*/

private static void HideSaveMenu() {
// Since Unity IMGUI popups can't be modal over RCG UI, we need to manually disable and re-enable the UI behind the popup
var saveSlotMenuGO = GameObject.Find("MenuLogic/MainMenuLogic/Providers/StartGame SaveSlotPanel");
// It's slightly visually nicer if we avoid disabling the "BG" part of the menu by targeting only these child GOs
saveSlotMenuGO.transform.GetChild(1).gameObject.SetActive(false); // Title Text
saveSlotMenuGO.transform.GetChild(3).gameObject.SetActive(false); // SlotGroup (contains the 4 big buttons)
saveSlotMenuGO.transform.GetChild(4).gameObject.SetActive(false); // SavePanel_BackButton

}
private static void UnHideSaveMenu() {
var saveSlotMenuGO = GameObject.Find("MenuLogic/MainMenuLogic/Providers/StartGame SaveSlotPanel");
saveSlotMenuGO.transform.GetChild(1).gameObject.SetActive(true);
saveSlotMenuGO.transform.GetChild(3).gameObject.SetActive(true);
saveSlotMenuGO.transform.GetChild(4).gameObject.SetActive(true);
}

// since "async bool" isn't a thing, we need two patches to properly
// insert our UI and networking code before the vanilla code
[HarmonyPrefix, HarmonyPatch(typeof(StartMenuLogic), "CreateOrLoadSaveSlotAndPlay")]
Expand All @@ -256,12 +290,7 @@ public static async void StartMenuLogic_CreateOrLoadSaveSlotAndPlay_EnsureAPConn
return;
}

// Since Unity IMGUI popups can't be modal over RCG UI, we need to manually disable and re-enable the UI behind the popup
var saveSlotMenuGO = GameObject.Find("MenuLogic/MainMenuLogic/Providers/StartGame SaveSlotPanel");
// It's slightly visually nicer if we avoid disabling the "BG" part of the menu by targeting only these child GOs
saveSlotMenuGO.transform.GetChild(1).gameObject.SetActive(false); // Title Text
saveSlotMenuGO.transform.GetChild(3).gameObject.SetActive(false); // SlotGroup (contains the 4 big buttons)
saveSlotMenuGO.transform.GetChild(4).gameObject.SetActive(false); // SavePanel_BackButton
HideSaveMenu();

try {
selectedSlotIndex = slotIndex;
Expand All @@ -283,9 +312,7 @@ public static async void StartMenuLogic_CreateOrLoadSaveSlotAndPlay_EnsureAPConn
selectedSlotIndex = -1;
Log.Warning($"GetConnectionInfoFromUser threw: {ex.Message} with stack:\n{ex.StackTrace}");
} finally {
saveSlotMenuGO.transform.GetChild(1).gameObject.SetActive(true);
saveSlotMenuGO.transform.GetChild(3).gameObject.SetActive(true);
saveSlotMenuGO.transform.GetChild(4).gameObject.SetActive(true);
UnHideSaveMenu();
}
}

Expand Down
51 changes: 43 additions & 8 deletions Source/MainMenu/ConnectionAndPopups.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public static void UpdateStyles() {
private enum ConnectionPopup {
None,
InputConnectionInfo,
ChangeConnectionInfo,
Connecting,
ConnectionError,
RoomIdMismatchWarning,
Expand All @@ -70,6 +71,7 @@ private enum ConnectionPopup {

public static TaskCompletionSource<APRandomizerSaveData>? connected = null;
public static TaskCompletionSource<bool>? roomIdMismatchTCS = null;
public static TaskCompletionSource<APConnectionData>? savedChanges = null;

public static async Task<APRandomizerSaveData> GetConnectionInfoFromUser(APRandomizerSaveData? apSaveData) {
currentPopup = ConnectionPopup.InputConnectionInfo;
Expand All @@ -86,6 +88,17 @@ public static async Task<APRandomizerSaveData> GetConnectionInfoFromUser(APRando
return await connected.Task;
}

public static async Task<APConnectionData> ChangeConnectionInfo(APConnectionData acd) {
currentPopup = ConnectionPopup.ChangeConnectionInfo;

InputPopup_Server = $"{acd.hostname}:{acd.port}";
InputPopup_Slot = acd.slotName;
InputPopup_Password = acd.password;

savedChanges = new TaskCompletionSource<APConnectionData>();
return await savedChanges.Task;
}

public static async Task ResumePreviousConnection(APRandomizerSaveData apSaveData) {
currentPopup = ConnectionPopup.Connecting;
ConnectionPopups_ApSaveDataRef = apSaveData;
Expand All @@ -99,17 +112,21 @@ public static async Task ResumePreviousConnection(APRandomizerSaveData apSaveDat
AttemptToConnect();

Check warning on line 112 in Source/MainMenu/ConnectionAndPopups.cs

View workflow job for this annotation

GitHub Actions / build

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
await connected.Task;
}

private static void ConnectButtonClicked() {
ToastManager.Toast($"Connect button clicked: Server = {InputPopup_Server}, Slot = {InputPopup_Slot}, Password = {InputPopup_Password}");

private static APConnectionData ParseConnDataFromPopupInputs() {
APConnectionData apConnData = new();
var split = InputPopup_Server.Split(':');
apConnData.hostname = split[0];
// if the player left out a port number, use the default localhost port of 38281
apConnData.port = (split.Length > 1) ? int.Parse(split[1]) : 38281;
apConnData.slotName = InputPopup_Slot;
apConnData.password = InputPopup_Password;
return apConnData;
}

private static void ConnectButtonClicked() {
ToastManager.Toast($"Connect button clicked: Server = {InputPopup_Server}, Slot = {InputPopup_Slot}, Password = {InputPopup_Password}");

APConnectionData apConnData = ParseConnDataFromPopupInputs();

if (ConnectionPopups_ApSaveDataRef == null) {
var newApSaveData = new APRandomizerSaveData();
Expand All @@ -124,6 +141,14 @@ private static void ConnectButtonClicked() {
AttemptToConnect();

Check warning on line 141 in Source/MainMenu/ConnectionAndPopups.cs

View workflow job for this annotation

GitHub Actions / build

Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
}

private static void SaveInfoButtonClicked() {
ToastManager.Toast($"Save info button clicked: Server = {InputPopup_Server}, Slot = {InputPopup_Slot}, Password = {InputPopup_Password}");

APConnectionData apConnData = ParseConnDataFromPopupInputs();

savedChanges.SetResult(apConnData);

Check warning on line 149 in Source/MainMenu/ConnectionAndPopups.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
}

private static void CancelClickedAfterError() {
ToastManager.Toast($"Cancel button clicked after error = {ConnectionPopups_DisplayWarningOrError}");
currentPopup = ConnectionPopup.None;
Expand Down Expand Up @@ -275,7 +300,9 @@ public static void OnGUI() {
UpdateStyles();

if (currentPopup == ConnectionPopup.InputConnectionInfo) {
DrawInputPopup();
DrawInputThenConnectPopup();
} else if (currentPopup == ConnectionPopup.ChangeConnectionInfo) {
DrawInputThenSavePopup();
} else if (currentPopup == ConnectionPopup.Connecting) {
DrawConnectingPopup();
} else if (currentPopup == ConnectionPopup.ConnectionError) {
Expand All @@ -290,7 +317,15 @@ private static Rect GetPopupWindowRectangle() {
return new Rect((Screen.width - windowWidth) / 2, (Screen.height - windowHeight) / 2, windowWidth, windowHeight);
}

private static void DrawInputPopup() {
private static void DrawInputThenConnectPopup() {
DrawInputPopup("Connect to AP Server", ConnectButtonClicked);
}

private static void DrawInputThenSavePopup() {
DrawInputPopup("Save Connection Information", SaveInfoButtonClicked);
}

private static void DrawInputPopup(string commitText, Action onCommitClicked) {
var windowRect = GetPopupWindowRectangle();
var textFieldWidth = GUILayout.Width(windowRect.width * 0.6f);

Expand Down Expand Up @@ -319,8 +354,8 @@ private static void DrawInputPopup() {

GUILayout.Label("", labelStyle);

if (GUILayout.Button("Connect to AP Server", buttonStyle)) {
ConnectButtonClicked();
if (GUILayout.Button(commitText, buttonStyle)) {
onCommitClicked();
}
}, "Archipelago Connection Info", windowStyle);
}
Expand Down

0 comments on commit 3606cab

Please sign in to comment.