<commit_before>using System;
using Microsoft.Xna.Framework.Input;
using Pathoschild.Stardew.Common;
using Pathoschild.Stardew.SkipIntro.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;

namespace Pathoschild.Stardew.SkipIntro
{
    /// <summary>The mod entry point.</summary>
    internal class ModEntry : Mod
    {
        /*********
        ** Properties
        *********/
        /// <summary>The mod configuration.</summary>
        private ModConfig Config;

        /// <summary>The current step in the mod logic.</summary>
        private Stage CurrentStage;


        /*********
        ** Public methods
        *********/
        /// <summary>The mod entry point, called after the mod is first loaded.</summary>
        /// <param name="helper">Provides methods for interacting with the mod directory, such as read/writing a config file or custom JSON files.</param>
        public override void Entry(IModHelper helper)
        {
            this.Config = helper.ReadConfig<ModConfig>();

            MenuEvents.MenuChanged += this.MenuEvents_MenuChanged;
        }


        /*********
        ** Private methods
        *********/
        /****
        ** Event handlers
        ****/
        /// <summary>The method called when the player returns to the title screen.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void MenuEvents_MenuChanged(object sender, EventArgsClickableMenuChanged e)
        {
            if (e.NewMenu is TitleMenu)
            {
                this.CurrentStage = Stage.SkipIntro;
                GameEvents.UpdateTick += this.GameEvents_UpdateTick;
            }
        }

        /// <summary>Receives an update tick.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void GameEvents_UpdateTick(object sender, EventArgs e)
        {
            try
            {
                // apply skip logic
                this.CurrentStage = Game1.activeClickableMenu is TitleMenu menu
                    ? this.Skip(menu, this.CurrentStage)
                    : Stage.None;

                // unhook when done
                if (this.CurrentStage == Stage.None)
                    GameEvents.UpdateTick -= this.GameEvents_UpdateTick;
            }
            catch (Exception ex)
            {
                this.Monitor.InterceptError(ex, "skipping the intro");
                GameEvents.UpdateTick -= this.GameEvents_UpdateTick;
            }
        }

        /****
        ** Methods
        ****/
        /// <summary>Skip the intro if the game is ready.</summary>
        /// <param name="menu">The title menu whose intro to skip.</param>
        /// <param name="currentStage">The current step in the mod logic.</param>
        /// <returns>Returns the next step in the skip logic.</returns>
        private Stage Skip(TitleMenu menu, Stage currentStage)
        {
            // wait until the game is ready
            if (Game1.currentGameTime == null)
                return currentStage;

            // main skip logic
            if (currentStage == Stage.SkipIntro)
            {
                // skip to title screen
                menu.receiveKeyPress(Keys.Escape);
                menu.update(Game1.currentGameTime);

                // skip to other screen
                switch (this.Config.SkipTo)
                {
                    case Screen.Title:
                        // skip button transition
                        while (this.Helper.Reflection.GetField<int>(menu, "buttonsToShow").GetValue() < TitleMenu.numberOfButtons)
                            menu.update(Game1.currentGameTime);
                        return Stage.None;

                    case Screen.Load:
                        // skip to load screen
                        menu.performButtonAction("Load");
                        while (TitleMenu.subMenu == null)
                            menu.update(Game1.currentGameTime);
                        return Stage.None;

                    case Screen.JoinCoop:
                    case Screen.HostCoop:
                        // skip to co-op screen
                        menu.performButtonAction("Co-op");
                        while (TitleMenu.subMenu == null)
                            menu.update(Game1.currentGameTime);

                        return this.Config.SkipTo == Screen.JoinCoop
                            ? Stage.None
                            : Stage.WaitingForConnection;
                }
            }

            // skip to host tab after connection is established
            if (currentStage == Stage.WaitingForConnection)
            {
                // not applicable
                if (this.Config.SkipTo != Screen.HostCoop || !(TitleMenu.subMenu is CoopMenu submenu))
                    return Stage.None;

                // not connected yet
                if (submenu.hostTab == null)
                    return currentStage;

                // select host tab
                submenu.receiveLeftClick(submenu.hostTab.bounds.X, submenu.hostTab.bounds.Y, playSound: false);
            }

            // ???
            return Stage.None;
        }
    }
}
<commit_msg>fix SkipIntro game freeze after getting disconnected from multiplayer (#268)<commit_after>using System;
using Microsoft.Xna.Framework.Input;
using Pathoschild.Stardew.Common;
using Pathoschild.Stardew.SkipIntro.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Menus;

namespace Pathoschild.Stardew.SkipIntro
{
    /// <summary>The mod entry point.</summary>
    internal class ModEntry : Mod
    {
        /*********
        ** Properties
        *********/
        /// <summary>The mod configuration.</summary>
        private ModConfig Config;

        /// <summary>The current step in the mod logic.</summary>
        private Stage CurrentStage;


        /*********
        ** Public methods
        *********/
        /// <summary>The mod entry point, called after the mod is first loaded.</summary>
        /// <param name="helper">Provides methods for interacting with the mod directory, such as read/writing a config file or custom JSON files.</param>
        public override void Entry(IModHelper helper)
        {
            this.Config = helper.ReadConfig<ModConfig>();

            MenuEvents.MenuChanged += this.MenuEvents_MenuChanged;
        }


        /*********
        ** Private methods
        *********/
        /****
        ** Event handlers
        ****/
        /// <summary>The method called when the player returns to the title screen.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void MenuEvents_MenuChanged(object sender, EventArgsClickableMenuChanged e)
        {
            if (e.NewMenu is TitleMenu)
            {
                this.CurrentStage = Stage.SkipIntro;
                GameEvents.UpdateTick += this.GameEvents_UpdateTick;
            }
        }

        /// <summary>Receives an update tick.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments.</param>
        private void GameEvents_UpdateTick(object sender, EventArgs e)
        {
            try
            {
                // apply skip logic
                this.CurrentStage = Game1.activeClickableMenu is TitleMenu menu
                    ? this.Skip(menu, this.CurrentStage)
                    : Stage.None;

                // unhook when done
                if (this.CurrentStage == Stage.None)
                    GameEvents.UpdateTick -= this.GameEvents_UpdateTick;
            }
            catch (Exception ex)
            {
                this.Monitor.InterceptError(ex, "skipping the intro");
                GameEvents.UpdateTick -= this.GameEvents_UpdateTick;
            }
        }

        /****
        ** Methods
        ****/
        /// <summary>Skip the intro if the game is ready.</summary>
        /// <param name="menu">The title menu whose intro to skip.</param>
        /// <param name="currentStage">The current step in the mod logic.</param>
        /// <returns>Returns the next step in the skip logic.</returns>
        private Stage Skip(TitleMenu menu, Stage currentStage)
        {
            // wait until the game is ready
            if (Game1.currentGameTime == null)
                return currentStage;

            // do nothing if a confirmation box is on-screen (e.g. multiplayer disconnect error)
            if (TitleMenu.subMenu is ConfirmationDialog)
                return Stage.None;

            // main skip logic
            if (currentStage == Stage.SkipIntro)
            {
                // skip to title screen
                menu.receiveKeyPress(Keys.Escape);
                menu.update(Game1.currentGameTime);

                // skip to other screen
                switch (this.Config.SkipTo)
                {
                    case Screen.Title:
                        // skip button transition
                        while (this.Helper.Reflection.GetField<int>(menu, "buttonsToShow").GetValue() < TitleMenu.numberOfButtons)
                            menu.update(Game1.currentGameTime);
                        return Stage.None;

                    case Screen.Load:
                        // skip to load screen
                        menu.performButtonAction("Load");
                        while (TitleMenu.subMenu == null)
                            menu.update(Game1.currentGameTime);
                        return Stage.None;

                    case Screen.JoinCoop:
                    case Screen.HostCoop:
                        // skip to co-op screen
                        menu.performButtonAction("Co-op");
                        while (TitleMenu.subMenu == null)
                            menu.update(Game1.currentGameTime);

                        return this.Config.SkipTo == Screen.JoinCoop
                            ? Stage.None
                            : Stage.WaitingForConnection;
                }
            }

            // skip to host tab after connection is established
            if (currentStage == Stage.WaitingForConnection)
            {
                // not applicable
                if (this.Config.SkipTo != Screen.HostCoop || !(TitleMenu.subMenu is CoopMenu submenu))
                    return Stage.None;

                // not connected yet
                if (submenu.hostTab == null)
                    return currentStage;

                // select host tab
                submenu.receiveLeftClick(submenu.hostTab.bounds.X, submenu.hostTab.bounds.Y, playSound: false);
            }

            // ???
            return Stage.None;
        }
    }
}

{'fraction_non_alphanumeric': 0.10093576814754247, 'fraction_numerical': 0.001362769146906514, 'mean_word_length': 1.4565945101539834, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4293309', 'n_tokens_mistral': 2695, 'n_tokens_neox': 2558, 'n_words': 663}