Announcement

Collapse
No announcement yet.

GetWorld()->GetAuthGameMode() access violation

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    GetWorld()->GetAuthGameMode() access violation

    Hi, im having a crash (access violation):

    when i try to access

    Cast<AMyGameMode>(GetWorld()->GetAuthGameMode())->InventoryManager->AddItemToInventory(this);

    the crash happen when i try to GetWorld()->GetAuthGameMode()

    Why is this happening? Why i dont have the reference to the game mode?
    I am calling the function that contain the lines above from a UMG Widget class.
    Is a singleplayer game (no server and clients)

    Could i provide more info?
    thanks !
    Last edited by Alexarg; 06-08-2015, 07:53 PM.

    #2
    So if you are actually the server (which should be the case in a single player game) then I would advice to break the line down and debug each statement.

    It could fail to cast the GameMode.
    GetWorld() could return NULL.
    Inventory Manager could also be NULL at the time.

    Comment


      #3
      Yes, sorry for my English i think I did not write clearly.
      The fail is when i try to get the Game mode.

      exactly here:
      GetWorld()->GetAuthGameMode();

      Comment


        #4
        Then check if GetWorld() returns a valid UWorld object.


        Code:
        UWorld* world = GetWorld();
        if(world)
        {
        world->GetAuthGameMode();
        }
        Because you can't be certain that it will return a valid value everytime.

        Comment


          #5
          Ok, i will check that, but, if it doesn't return a valid value, what can i do?, i need the gamemode to access my "InventoryManager".
          I mean:

          Code:
          UWorld* world = GetWorld();
          if(world)
          {
          world->GetAuthGameMode();
          }
          else{
           //Could i do something here??
          }
          thanks for your answers

          Comment


            #6
            If it does not return a valid value, you might call it too early after game initialization (for example in the constructor).

            The solution is just to push it into a later stage. Maybe PostInitializeComponents() or BeginPlay().

            Comment


              #7
              Ok, i think i know what could be the problem.
              I'm creating instances of "AItem" this way:

              Code:
              GetDefaultObject()
              Because some items are never spawned on the world. (I access a "loot" container that create a widget, and showing the items in that widget, so i create a reference of the item in that widget)

              hope you understand what i'm saying

              Could this be the problem?

              EDIT:
              I create a video (because my English sucks i think this way is best understood



              so when i hit "Take" the crash happen.

              And this is when i "interact" with the white box.

              Code:
              void AContainerLoot::Interact(){
              	//This is firing here because I dont want the container store the data if the player never interact with it
              	if (ItemsInContainer.Num() == 0){
              		//Cantidad de items en container
              		int32 itemsQuantity = FMath::RandRange(0, 3);
              		if (itemsQuantity > 0){
              			ASurvivalGameMode* ASGM = Cast<ASurvivalGameMode>(GetWorld()->GetAuthGameMode());
              			for (int32 i = 0; i < itemsQuantity; i++){
              				int32 randomIndex = FMath::RandRange(0, (ASGM->InventoryManager->GetItemDataBaseSize() - 1));
              				ItemsIndex.Add(randomIndex);
              				TSubclassOf<AItem> item = ASGM->InventoryManager->ItemDB->GetItemById(randomIndex);
              				if (ASGM->InventoryManager->ItemDB->GetItemById(randomIndex) != NULL){
              					ItemsInContainer.Add(item);
              				}
              
              			}
              		}
              	}
              
              	if (ItemsInstancesInLoot.Num() == 0){
              		for (int32 i = 0; i < ItemsInContainer.Num(); i++){
              			ItemsInstancesInLoot.Add(ItemsInContainer[i].GetDefaultObject());
              		}
              	}
              
              	//This is implemented on BP
              	CreateLootWidget();
              
              }
              
              TArray<class AItem *> AContainerLoot::GetItemsInLoot(){
              	return ItemsInstancesInLoot;
              }
              GetItemsInLoot() is called from the blueprint (subclass of the class containing the above Code) and then i pass the "ItemsInstancesInLoot" to the widget.
              Last edited by Alexarg; 06-08-2015, 08:47 PM.

              Comment


                #8
                Originally posted by Alexarg View Post
                Ok, i think i know what could be the problem.
                I'm creating instances of "AItem" this way:

                Code:
                GetDefaultObject()
                That might actually be the problem. And it is bad practice to just use GetDefaultObject(). The CDO (Class Default Object) is more like a template object and should not be used for game code directly.

                Actors should always be spawned via GetWorld()->SpawnActor() if you want to access them at runtime.

                You might already have something like the following, but you should consider encapsulating your ItemClasses for Inventories like this:

                Code:
                USTRUCT()
                struct FItemData
                {
                	GENERATED_USTRUCT_BODY()
                         
                        /** store the item class */
                	UPROPERTY(EditDefaultsOnly, Category = Item)
                	TSubclassOf<class AItem> ItemClass;
                
                        /////////////////////////////////////
                        // ADDITIONAL ITEM INFORMATION HERE, FOR EXAMPLE ID, DisplayName, UIIcon etc.
                
                	FItemData::FItemData()
                        {
                              ItemClass = AItem::StaticClass();
                	}
                
                };
                This way you can pass the items around between inventories without spawning them.


                But you have to make sure that as soon as you want to access data in an AItem-Object, you should Spawn it correctly instead of using ItemClass->GetDefaultObject().
                Last edited by DennyR; 06-08-2015, 10:10 PM.

                Comment


                  #9
                  Thanks for the info DennyR, really really helpful!
                  I understand that i have to spawn the actor in order to access data. The weird thing about that is this scenario:
                  I need to show the "item description" on my UMG widget, so now I need to spawn the item to access that description. So I spawn it always with a location and rotation or transform into the level, but this item should not exist on the level. , that confuses me a bit

                  Thanks again for your time DennyR, I think I have to study a little =)
                  Last edited by Alexarg; 06-08-2015, 11:50 PM.

                  Comment


                    #10
                    Originally posted by Alexarg View Post
                    Thanks for the info DennyR, really really helpful!
                    I understand that i have to spawn the actor in order to access data. The weird thing about that is this scenario:
                    I need to show the "item description" on my UMG widget, so now I need to spawn the item to access that description. So I spawn it always with a location and rotation or transform into the level, but this item should not exist on the level. , that confuses me a bit

                    Thanks again for your time DennyR, I think I have to study a little =)
                    What he described in his last post is the way you get around this. You separate the ItemData into it's own struct that will hold item properties that are required without an actor. While it's in your inventory you store it as an FItemData, then when you want to spawn the instance of your item into the world you use the ItemClass to spawn an instance and initialize it with the data stored in your FItemData instance (or just store a reference/copy of the FItemData in it). You're just separating your ItemData from your ItemActor. The ItemActor is the thing that actually appears in the world, and your ItemData is the lightweight version of your item that is what is accessed in inventory and populates the ItemActor with the data it needs.

                    Sorry if that's kind of verbal vomitty.

                    Comment


                      #11
                      Hi DennyR , sorry to bother you again. I am studying what can i do and I realise that i didn't understand why i shood encapsulating the ItemClasses.

                      I will explain what i have:

                      An ItemDataBase

                      Code:
                      USTRUCT()
                      struct FItemData
                      {
                      	GENERATED_USTRUCT_BODY()
                      
                      	UPROPERTY(EditDefaultsOnly, Category = Item)
                      	TSubclassOf<class AItem> Item;
                      
                      //Other info that I could need
                      
                      };
                      
                      /**
                       * 
                       */
                      UCLASS()
                      class My_API AItemDataBase : public AActor
                      {
                      	GENERATED_UCLASS_BODY()
                      
                      	UPROPERTY(EditDefaultsOnly, Category = ItemDatabase)
                      	TArray<FItemData> ItemClasses;
                      
                      	TSubclassOf<class AItem> GetItemByName(FString ItemName);
                      
                      	TSubclassOf<class AItem> GetItemById(int32 index);
                      };
                      And a child blueprint "BPItemDataBase" where i fill with data the TArray "ItemClasses".

                      On a "InventoryManager" constructor i just instantiate the Data Base

                      Code:
                      	static ConstructorHelpers::FObjectFinder<UBlueprint> ItemBDBlueprint(TEXT("Blueprint'/Game/Blueprints/Inventory/BPItemDataBase.BPItemDataBase'"));
                      	if (ItemBDBlueprint.Succeeded())
                      	{
                      		GameItemDataBase = (UClass*)ItemBDBlueprint.Object->GeneratedClass;
                      		ItemDB = GameItemDataBase->GetDefaultObject<AItemDataBase>();	
                      	}

                      And on my GameMode in the BeginPlay function i Spawn the InventoryManager:

                      Code:
                      void AMyGameMode::BeginPlay(){
                      	Super::BeginPlay();
                      	InventoryManager = Cast<AInventoryManager>(GetWorld()->SpawnActor(AInventoryManager::StaticClass()));
                      }
                      Then i have a ContainerLoot class, with can hold loot woth a TArray:

                      Code:
                      	UPROPERTY(EditDefaultsonly, BlueprintReadOnly, Category = ContainerItem)
                      	TArray<TSubclassOf<class AItem>> ItemsInContainer;
                      so here i can manually add items to a container or, if it doens't have any, random populate the "ItemsInContainer" array from the ItemDB like this:


                      Code:
                      if (ItemsInContainer.Num() == 0){
                      		
                      		int32 itemsQuantity = FMath::RandRange(0, 3);
                      		if (itemsQuantity > 0){
                      			ASurvivalGameMode* ASGM = Cast<ASurvivalGameMode>(GetWorld()->GetAuthGameMode());
                      			for (int32 i = 0; i < itemsQuantity; i++){
                      				int32 randomIndex = FMath::RandRange(0, (ASGM->InventoryManager->GetItemDataBaseSize() - 1));
                      				ItemsIndex.Add(randomIndex);
                      				TSubclassOf<AItem> item = ASGM->InventoryManager->ItemDB->GetItemById(randomIndex);
                      				if (ASGM->InventoryManager->ItemDB->GetItemById(randomIndex) != NULL){
                      					ItemsInContainer.Add(item);
                      				}
                      
                      			}
                      		}
                      	}
                      Now when i interact with this container actor, it should return instances of items wich are in "ItemsInContainer" in order to show the properties like "item descripcion" "item name" "item value" etc to a UMG widget.
                      SO here is the problem, if i spawn every item in every lootcontainer, i will end up woth a lot of spawned items all around the scene (wich i could hide or set not visible), but they still be there, but i cant find other way to do this.

                      Hope you / someone understand my problem
                      And im sorry for the wall of text
                      Last edited by Alexarg; 06-09-2015, 02:37 PM.

                      Comment


                        #12
                        Hey Alexarg, no problem at all. I understand your problem and I already gave an answer to it.
                        Just go back and read the my last and mrooney's post.


                        I think it is language barrier thing, so I will try to give it another go on your current state.



                        You have the lines of code:

                        Code:
                        /**
                         * 
                         */
                        UCLASS()
                        class My_API AItemDataBase : public AActor
                        {
                        	GENERATED_UCLASS_BODY()
                        
                        	UPROPERTY(EditDefaultsOnly, Category = ItemDatabase)
                        	TArray<FItemData> ItemClasses;
                        
                        	TSubclassOf<class AItem> GetItemByName(FString ItemName);
                        
                        	TSubclassOf<class AItem> GetItemById(int32 index);
                        };
                        
                        
                        UPROPERTY(EditDefaultsonly, BlueprintReadOnly, Category = ContainerItem)
                        TArray<TSubclassOf<class AItem>> ItemsInContainer;
                        just change it to :

                        Code:
                        /**
                         * 
                         */
                        UCLASS()
                        class My_API AItemDataBase : public AActor
                        {
                        	GENERATED_UCLASS_BODY()
                        
                        	UPROPERTY(EditDefaultsOnly, Category = ItemDatabase)
                        	TArray<FItemData> ItemClasses;
                        
                        	FItemData GetItemByName(FString ItemName);
                        
                        	FItemData GetItemById(int32 index);
                        };
                        
                        UPROPERTY(EditDefaultsonly, BlueprintReadOnly, Category = ContainerItem)
                        TArray<FItemData> ItemsInContainer;
                        ...so your containers work with the ItemData struct as well. In order to make use of the struct and to avoid spawning items for your inventories, add the information only needed in the inventory to the struct like this:

                        Code:
                        USTRUCT()
                        struct FItemData
                        {
                        	GENERATED_USTRUCT_BODY()
                        
                        	UPROPERTY(EditDefaultsOnly, Category = Item)
                        	TSubclassOf<class AItem> Item;
                        
                                //Other info that I could need
                                UPROPERTY(EditDefaultsOnly, Category = Item)
                                FText DisplayName;
                        
                                //Other info that I could need
                                UPROPERTY(EditDefaultsOnly, Category = Item)
                                FText ItemDescription;
                        
                                //Other info that I could need
                                UPROPERTY(EditDefaultsOnly, Category = Item)
                                float Weight;
                        
                                //Other info that I could need
                                UPROPERTY(EditDefaultsOnly, Category = Item)
                                UTexture* UIIcon;
                        
                                //... and whatever you need
                        };

                        Then in your AContainerLoot::Interact() function you can just pass the FItemData-Struct instead of the Actor or the ItemClass. In your UI you can then access the FItemData.DisplayName without spawning the Item.

                        This way you only have to spawn the Item if you want to see it in the game world.
                        Last edited by DennyR; 06-09-2015, 03:00 PM.

                        Comment


                          #13
                          ooooh I feel so stupid!
                          I understand and it's true, you had already answered above, just i did not understand, I think your last post was excellent, everything clear now
                          Thanks very much !!!

                          Comment


                            #14
                            Glad I could help

                            Comment


                              #15
                              Well, i have the same problem: I need to get my GameMode's method, so i call a reference from my character, but getting the reference crash the editor, i don't understand why.

                              Comment

                              Working...
                              X