Download

Why loaded Mesh is released from memory even with active Handle?

Hi everyone! I am working on multiplayer game and trying to implement some features inside Epic Shooter Game. One of features is modular weapons. So, inside base weapon actor I place additional skeletal mesh components and set mesh at runtime based on Datatable data. Moreover, this replicates through the net. The problem is after loading meshes using **StreamableManager.LoadSynchronous **and calling **SetSkeletalMesh **Shooting process causes log to say that there is no mesh for component. But it still visible in game. In Addition, when you switch weapon new one appears without meshes. (as the first one if you press Q again). Honestly, have no idea whats the matter. BUT on local client all works perfectly.
The flow of methods:


void ExtractModulesData();
                     |
UFUNCTION(Server, Reliable, WithValidation)
void SetupModulesServer(const TArray<FName>& PathArray, const TArray<FName>& SocketNameArray);
                     |
UFUNCTION(Reliable, NetMulticast, WithValidation)
void SetupModulesLocally(const TArray<FName>& PathArray, const TArray<FName>& SocketNameArray);

**void ExtractModulesData(); - **Works well and it just get data from datatables.

**UFUNCTION(Server, Reliable, WithValidation)
void SetupModulesServer(const TArray& PathArray, const TArray& SocketNameArray); - **Send RPC to Server


void AShooterWeapon::SetupModulesServer_Implementation(const TArray<FName>& PathArray, const TArray<FName>& SocketNameArray)
{
SetupModulesLocally(PathArray, SocketNameArray);
 }

**UFUNCTION(Reliable, NetMulticast, WithValidation)
void SetupModulesLocally(const TArray& PathArray, const TArray& SocketNameArray); - **Performs mesh loading and setting to components on clients or Listen Server


void AShooterWeapon::SetupModulesLocally_Implementation(const TArray<FName>& PathArray, const TArray<FName>& SocketNameArray)
 {
if (GetNetMode()==NM_DedicatedServer) return;

UAssetManager& AssetManager = UAssetManager::Get();
FStreamableManager& StreamableManager = AssetManager.GetStreamableManager(); MuzzelComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh>(FSoftObjectPath(PathArray[0]), true, &MuzzelHandle)); MuzzelComp->AttachToComponent(Frame,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[0]);
 
ForeGripComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh>(FSoftObjectPath(PathArray[1]), true, &ForeGripHandle));
 ForeGripComp->AttachToComponent(Frame,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[1]);

 StockComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh>(FSoftObjectPath(PathArray[2]), true, &StockHandle));
StockComp->AttachToComponent(Frame,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[2]);

 HandleComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh>(FSoftObjectPath(PathArray[3]), true, &HandleHandle));
 HandleComp->AttachToComponent(Frame,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[3]);

 MagazineComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh>(FSoftObjectPath(PathArray[4]), true, &MagazineHandle));
MagazineComp->AttachToComponent(Frame,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[4]);

 if (!PathArray[5].IsNone()) ScopeComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh> (FSoftObjectPath(PathArray[5]), true, &ScopeHandle));
ScopeComp->AttachToComponent(Frame,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[5]);
 
if (ForeGripComp->SkeletalMesh) {
  if (!PathArray[6].IsNone()) GripComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh> (FSoftObjectPath(PathArray[6]), true, &GripHandle));
    GripComp->AttachToComponent(ForeGripComp,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[6]);
  }
  if (!PathArray[7].IsNone()) GunPointComp->SetSkeletalMesh(StreamableManager.LoadSynchronous<USkeletalMesh> (FSoftObjectPath(PathArray[7]),true, &GunPointHandle));   GunPointComp->AttachToComponent(MuzzelComp,FAttachmentTransformRules::KeepRelativeTransform,SocketNameArray[7]);
}

And Flow starts in BeginPlay of weapon. Yeah, it is bad as well, but it is more convinient to find mistake. (And Still I haven’t found it)
Sorry for Weird Code. Just want to be sure it works even with this code.
How it looks after pressing Q on Client 3

Result in Editor after play started:

The code has all got a bit corrupted so it’s hard to read but I had a couple of thoughts having just battled through getting asset management working in my game I’m working on.

Assuming the weapon meshes are not dynamically created but are uasset objects in your project file system, have you made sure the asset manager is aware of them and scanning those folders so it knows what assets to load ? If it is not it won’t keep them around in memory or load them (but the editor might load them into memory if you have that open and modify the asset so sometimes you think it’s working when it’s not)
You can do it in code as well but you can set it up in the DefaultGame.ini or use the editor GUI to do it, here’s a screen shot from mine to give an idea what I mean.

also in the code where you load it the first line says if it’s running on the server return so that’s only going to load on the client.



if (GetNetMode()==NM_DedicatedServer) return;

Assuming the mesh object is replicated I’ve set my code up so the server has authority and I’ll spawn anything on the server then let the game replication handle getting it to the client. If you just spawn it on the client the server is never going to know about it and wont have the correct channels open for rpc calls etc.

Tom Looman had a good write up on the asset manager here Asset Manager for Data Assets & Async Loading – Tom Looman and the EPic Action RPG code is worth digging through for setting up a custom asset manage but they do it Synchronously Tom has some async examples and if you dig through the source code there are some other ways to do it as well.

Once it’s all sorted an in memory it’s really easy to get a referance to it just one liner line below where UObject is whatever your object is and primary asset id is whatever the FPrimaryAssetId is of the object you have registered with the asset manager. Toms page and the Action RPG code have good examples of how to get it into memory in the first place it’s a bit different to how you are doing it but I think both ways work.


UObject* obj = Cast<UObject>(AssetManager.GetPrimaryAssetObject(primaryAssetID));

Also when you get it working I’d to a proper build of the dedicated server quite quickly and test it still behaves as you expect quite early on in your development cycle as running it dedicated from the command line with the -server -game -log flags still runs it in the editor context from what I can see, once you package it with no editor you get to find out if your assets really are all working as you think or not (having just got caught out with exactly that myself :slight_smile: )

not sure if that’s helpful or not hopefully gives some ideas of where to look!

Mark

If that is the case, then you are having replication problems. figure out how to replicate it to the clients and they will show on the clients. When you switch weapons you must reload all parts and the weapon, as in reassemble the whole gun to hand. < Many ways you can do that.

Yeah, I thought about that but i callsetting modules function on Listen Server as well as on Clients and SetHidden on sever too. So, techically it must be ok on server and not show in logs “Missing mesh for SkeletalMeshComponent” ( and etc). I’ll try to create more specific replication but still think meshes are releasing from memory and then it changes to nullptr in components and then replicating to the clients.

By the way, component properties doesn’t have **Replicating **specificator so should they replicate even so?

Are those bots or human players? I had an issue when you would die and watch a bot play when in his view. I could not see his gun parts. I had to actually add code to load the bots gun parts. Then i could see them when in his view or when watching them play. Otherwise i had bots running around with a base gun no parts attached to it. I will go find the code share it with you but it might not work for what you are writing compared to what i wrote.

Code i added but it will not make any sense as there is a ton more to it. Then we take the strings to classes then we use that to spawn the weapon or parts



//Get and set all proper attachments to each gun before you spawn
//save them off for switching purposes  ThePRI->GunParts.= array of classes from these >"|M1X6|BM16|CM16|GPT00|M4S6";
// primarySetup = "|M1X6|BM16|CM16|GPT00|M4S6";//basic M16 /gun/barrel/clip/trigger/stock
//primarySetup2 = "|4XR7|JS91|YFHM|GPT00|8WXI|SK00";//basic AK47 /gun/barrel/clip/trigger/stock
UE_LOG(LogPHWeapon, Log, TEXT("APHWeapon::SetAllAttachments GunPartList.Num() = %d"), GunPartList.Num());
if (GunPartList.Num() == 0)
{
    for (int32 i = 0; i < ThePRI->GunParts.Num(); i++)
    {
       if (ThePRI->GunParts* != nullptr)
       {
          FActorSpawnParameters SpawnGunPartInfo;
          SpawnGunPartInfo.Instigator = GetInstigator();
          SpawnGunPartInfo.ObjectFlags |= RF_Transient; // We never want to save Gun Parts into a map
          GunPartList* = GetWorld()->SpawnActor<APHGunPart>(ThePRI->GunParts*.Get(), SpawnGunPartInfo);
           UE_LOG(LogPHWeapon, Log, TEXT("APHWeapon::SetAllAttachments() *GunPartList* = %s"), *GunPartList*->GetName());

         //GunPartList* = new ThePRI.GunParts*;THIS for UOBJECT spawns
        //GunPartList* = spawn(ThePRI->GunParts*);//WAS USING THIS for AActors spawns
       }
    }
    UE_LOG(LogPHWeapon, Log, TEXT(""));
}

//server controlled
if (HasAuthority())
{
    Client_SetPartsList(GunPartList);
    InstallWeaponParts(GunPartList);
}


//to ensure that you see gunparts on bots guns in spectating mode
if (ThePRI->IsABot())
    Assemble1PGun(GunPartList);

not sure if this will really help out as there is bunch more code after this to get gun assembled and settings set so it is ready to go. I had all my weapons set to load up paintball guns. So i had **** load of stuff added into my guns. Example: Under grenade launchers, flashlights, triggers, weapon skills, adding ammo, setting 7 triggers up(full auto and more), If you use co2 that setup, battery setup, You name it we added it.

Thanks for the link it definitely was worth reading. However, i didn’t understand usage of this loading, because he never assigned any data to properties in** UMonsterData** struct. Overriding in blueprints? Then he should override **GetPrimaryAssetId **again.

I have checked the code again and come to decision that is replication in the second problem. Skeletal meshes which is loaded using streamable manager and setted inside components are destroying or releasing.


I am see then while output log claim they don’t exists. Suppose it is a bigger issue.

Clients don’t have this problem until weapon is switched. And then it goes…

Ahh yes, with that log you can see what is going on. Sounds like what you were saying is happening.

Just remember the sever should do all the making and attaching of the components, not the the client. Then have the server replicate it over to the client, so they get a copy from replication… Then all should work fine.

When you switch weapons. The one that was in your hand gets destroyed and the weapon you are switching to needs to be reassembled. That is why when i spawned all the parts i saved them into an array GunPartList . The main gun does not get destroyed its in memory in manager to switch over to from an array it was saved in. So the array GunPartList is saved so when you switch you do not need to respawn all the parts just reattach them. Saves time and resources for faster switching. You will always need to reassemble the weapon going into your hand.

But what about using **SetHidden **or SetVisibility? If i set meshes using **FObjectFinder **in constructor they don’t dissapear on the server and client as well. So switching visibility cause problem.
I’ll keep in mind your advice, it’s useful just wondering why it happens.

Yeah you need a function to handle the visibility on the weapon and each part.



void AWeaponAttachment::ChangeVisibility(bool bIsVisible)
{
    if (Mesh)
    {
       Mesh->SetHidden(!bIsVisible);
    }

    if (OverlayMesh)
    {
      OverlayMesh->SetHidden(!bIsVisible);
    }

    if (GunPartList.Num() > 0)
    {
        for (int32 i = 0; i < GunPartList.Num(); i++)
        {
             if ((GunPartList*) && (GunPartList*->TP_Mesh))
             {
                   GunPartList*->TP_Mesh->SetHidden(!bIsVisible);
             }
        }
    }
}


Thanks for the example, it works well. By the way, messages in log are shown because i not assign mesh in components on the server, though on client do. It works well but it seems a little bit weird.

What i do is have the character make my inventory manager then have that make my gun send it to the client then after the gun is made and sent from the inventory manager and i know what it is. I then head to a function in the weapon file and add the all parts and the setting on them then when it finishes i comes back to inventory manager to finish giving ammo and anything else the weapon needs, then finish out the weapon so it can be used.