Game freezes after calling external method and does not hit code

Hi all, I am relatively new to unity game dev and using the UnityWebRequest package in general. During my world creation, I am trying to hit our backend and get a description of the world based on user input. In my WorldController, I have code which starts a coroutine with the initialization function, which then calls my backend client code and fires off the web request:

void Start()
        {
            saveManager = GlobalController.GetSaveManagerExternal();
            engineBackend = GlobalController.GetEngineBackendExternal();

            StartCoroutine("Initialize", "PromptExamples.FantasyTestPrompt");
        }

        /// <summary>
        /// Initialize the world controller by either retrieving the world from backend or loading from disk
        /// </summary>
        /// <param name="prompt">If the prompt is specified, use it to get a world</param>
        /// <exception cref="EntityGenerationException"></exception>
        public void Initialize(string prompt = null)
        {
            if (!string.IsNullOrWhiteSpace(prompt))
            {
                engineBackend.GenerateWorldDescription(prompt, JsonExamples.ExampleWorld, (generatedResult) =>
                {
                    try
                    {
                        LoadWorld(generatedResult);
                    }
                    catch (Exception e)
                    {
                        throw new EntityGenerationException($"Failed to load generated world: {generatedResult} with exception {e.Message}", e);
                    }
                });
            }
            else
            {
                // Loading from disk
                string worldExampleJson = saveManager.GetWorldJson();
                LoadWorld(worldExampleJson);
            }
        }
        /// <summary>
        /// Ask backend to generate world description in JSON format from user input
        /// </summary>
        /// <param name="userInput">User input for what world they would like to play in. For example, "Steampunk post-apocalyptic world"</param>
        /// <param name="example">Example json format to give </param>
        /// <param name="callback">Callback to call after the response is obtained</param>
        /// <returns>Enumerator waiting on result</returns>
        public IEnumerator GenerateWorldDescription(string userInput, string example, Action<string> callback)
        {
            Debug.Log("HERE!!");

            WWWForm body = new WWWForm();
            string prompt = $"I am a game developer trying to generate a world for the players to play in based on their input. " +
                $"The player says they want this world: '{userInput}'. Please return the world data in JSON format, using the " +
                $"following as an example template: {example}";
            body.AddField("prompt", TrimPrompt(prompt));

            Uri generateWorldUri = new Uri(backendUrl, "/world/generate");
            UnityWebRequest req = UnityWebRequest.Post(generateWorldUri.ToString(), body);
            yield return req.SendWebRequest();

            if (req.result != UnityWebRequest.Result.Success)
            {
                throw new WebException($"Failed to communicate with backend to generate world. Received HTTP error: {req.error}");
            }

            callback(req.downloadHandler.text);
        }

The issue is, my GenerateWorldDescription code is never run. I have a breakpoint inside which is never hit, I even try to step directly in to the GenerateWorldDescription call and it doesn’t go in the code (just continues to the end of the function). I also have a debug log in there which isn’t output to the console. There are no errors in the console as well other than a shader warning about one of my bushes.

I’m completely stumped here on why this isn’t working, my only idea is that i’m using coroutines wrong somehow. Would appreciate any help

I don’t think you can call Coroutines directly like that on script 1 line 18. You have to StartCoroutine(someObject.SomeCoroutine(arguments));

Thanks for the response. I tried to move the StartCoroutine code to only call the async web request code, but I am getting the same issue - unity freezes for 30 seconds on game start and then throws the console error (failed to connect to backend, which is expected)

        /// <summary>
        /// Initialize the world controller by either retrieving the world from backend or loading from disk
        /// </summary>
        /// <param name="prompt">If the prompt is specified, use it to get a world</param>
        /// <exception cref="EntityGenerationException"></exception>
        void Initialize(string prompt = null)
        {
            if (!string.IsNullOrWhiteSpace(prompt))
            {
                StartCoroutine(engineBackend.GenerateWorldDescription(prompt, JsonExamples.ExampleWorld, (generatedResult) =>
                {
                    try
                    {
                        LoadWorld(generatedResult);
                    }
                    catch (Exception e)
                    {
                        // TODO: in the future, we can send a retry message to GPT asking to fix the generated world
                        throw new EntityGenerationException($"Failed to load generated world: {generatedResult} with exception {e.Message}", e);
                    }
                }));
            }
            else
            {
                // Loading from disk
                string worldExampleJson = saveManager.GetWorldJson();
                LoadWorld(worldExampleJson);
            }
        }

I’ve never done web stuff, but I imagine yield the send request like this:
yield return req.SendWebRequest(); is going to make Unity hang until the request is completed.

You probably want to get the UnityWebRequestAsyncOperation return, and in a loop, yield return null while isDone is false.

var operation = req.SendWebRequest();

while (operation.isDone == false)
{
    yield return null;
}

I’ll try this out, but my code seems to be exactly how the example does it in the docs https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.Get.html

8862807--1209432--upload_2023-3-8_21-10-49.png

I wonder what i’m doing differently

For anyone who stumbles on this thread, I had to put my async call in a using block

        public IEnumerator GenerateWorldDescription(string userInput, string example, Action<string> callback)
        {
            EnsureSafeUserPromptInput(userInput);

            WWWForm body = new WWWForm();
            string prompt = $"I am a game developer trying to generate a world for the players to play in based on their input. " +
                $"The player says they want this world: '{userInput}'. Please return the world data in JSON format, using the " +
                $"following as an example template: {example}";
            body.AddField("prompt", TrimPrompt(prompt));

            Uri generateWorldUri = new Uri(backendUrl, "/world/generate");
            using (UnityWebRequest req = UnityWebRequest.Post(generateWorldUri.ToString(), body))
            {
                yield return req.SendWebRequest();

                if (req.result != UnityWebRequest.Result.Success)
                {
                    throw new WebException($"Failed to communicate with backend to generate world. Received HTTP error: {req.error}");
                }

                callback(req.downloadHandler.text);
            }
        }