Whats the best way to inject WorldContext into a CDO?

i’m currently passing it in to a function via meta(WorldContext) and it ‘works’ but the log complains about no worldcontext.

its ok if i hook it manually in that function but if i then call another function and pass it through it complains again. even using an actor as worldcontext within the function causes it to complain.

i assume its because GetWorld() returns nullptr on the CDO

You say alright! But you can override the function on your custom actor.

its in a UObject, but GetLevel() would require a valid outer anyway which we cant assume CDOs have?

The CDO dont depends on World,it lives on global array

If you shared the code I could provide a better answer, but in general the solution is that you don’t. If you’re using the CDO for anything and you want to do something that requires a WorldContext, you need to have a way to provide that separately.

This means that if you call a function on the CDO, the function needs its own WorldContext parameter. If the CDO is being used in some other function which wants to call functions needing a WorldContext, then it either needs to come up with one or have its own WorldContext param.

the code is simple,

UObject class with (ShowWorldContextPin) since we override GetWorld it might hide

GetWorld as above, returning nullptr if CDO

UFUNCTION(meta=WorldContext = ““)

result above,

issue was it was working but logging a warning in editor, of course when i went back to take the screenshot the warning was gone so could have been a debug mode thing?

worldcontext was valid and i tested with actors too

i thought the issue may be that it has worldcontext was valid but GetWorld was returning null so it was confusing the editor

question remains the same, is this the best way to do it?

From a design perspective, the CDO itself should not serve as an entity linked to UWorld. I believe the issue you should address is not enabling the CDO to obtain world parameters, but rather avoiding the use of CDO altogether and instead manually creating instances with an appropriate Outer.

I guess I don’t understand the broader context. If you’re using the CDO to call functions, I wouldn’t expect that class to have GetWorld overridden. And if you overrode GetWorld you shouldn’t need the UCLASS meta.

The blueprint logic looks correct for calling Execute and TestFunc on the CDO, though it comes down a lot to the details of specific implementations.

I would recommend not overriding GetWorld in your class. That uclass meta should let you call functions that require a world context so that you can pass in your parameter instead. If there’s some edge case though, you’d have to write your own wrapper around Engine functions or use the “CallableWithoutWorldContext” UFUNCTION meta on your own functions.

While I think this is generally good advice, there are times where using the CDO as a state-less lambda-like-thing is very useful for configuring data. Especially when blueprint is the only tool to do logic as data instead of code.

Even instances can have this problem when they are part of data assets. The design constraints for running functionality on a CDO and instanced objects of data assets are almost identical.

Also, as much as I would agree, Epic’s own gameplay framework GAS works this way with stateless objects running logic on a CDO. And people have a tendency to mimic that sort of thing (though their framework does have a handle on this world context stuff).

I override GetWorld because the object can be a CDO or Instantiated. I have 2 use cases,

  1. the command pattern where im basically just wrapping simple const commands in a uclass, most seem too basic bother instanitating although i may and just pool them
  2. i have a similiar ability system to GAS which is instantiated and latent but some functions i may want to call on the CDO, ie GetAbilityCost or CanAbilityBeExecuted

agreed i moved away from this because it was too clunky, the only option here would be to have all the functions on the PDA and have to DA call functionsbyname but i didnt like that

In that case, it’d be easier (sort of) to just assume no world context from an override and that you’re always calling it on the CDO. Or limit the functions allowable to be called on the CDO to things that shouldn’t need a world context (which makes more sense to me).

I’m not really sure what you mean. I felt like it’s worked out pretty well in my experience. More so with my hobby project, but it’s just taking what we shipped Midnight Suns with and I’ve expanded on.

happy to hear from your experience?

what i meant was since DA cant override functions i’d need a PDA for each function and a DA to call it? or a God PDA with all functions but then i’d need to call by Name set on the DA. but then i also may/may not need to instantiate objects if i do need to hold data, say for instance an undo command function.

Just to clarify, by DA you mean Data Asset and by PDA you mean Primary Data Asset?

I’m also still unclear what you’re referring to as “call by name”? Just a regular old function call? or calling functions using reflection and ProcessEvent?

correct on both

so a DataAsset cant override functions so i’d either need a Primary per function or a god Primary where i process an event defined in the DataAsset.( or a combo of both)

again correct me if im wrong on anything, im trying to figure this out in my head. so i figured one uobject class per function or maybe a small switch for similar functions

The asset can’t, but the data asset type can.

Okay, I think I see what you’re suggesting that’s different from what I am. I’m not suggesting that the data-asset is the command object. I’m suggesting that the data asset would hold the command object or objects.

Here’s the pattern from Midnight Suns I was talking about:

The primary data asset is something like UAbilityDefinition. It would have properties like:

UPROPERTY(EditDefaultOnly, Instanced)
TArray< const UCommand* > Actions;

UPROPERTY(EditDefaultOnly, Instanced)
const UCommand* OnSomethingAction;

That command is a specific instance of the command pattern, so you can configure specific data members of it for the ability that it’s a part of. But as part of data asset, these aren’t bound to any world. So any function you call on them, you’d have to pass in a WorldContext to do any world things.

This is also meant as a pattern, not a type. So you wouldn’t have one UCommand type. But multiples, each with an API specific to the context they were being used. For example at home, I have one, UAbilityEffect, that are things that an ability can do and so it has an API around doing things with a source & target & such. But I also have a UTargetingStyle that has it’s own API for starting, stopping, previewing, gathering, etc. There isn’t a generic type that all these “command pattern” objects use and is trying to support API’s for all derived types.

I’m also share the command objects when appropriate. For example, my status effects have their own primary data type, but reuse the UAbilityEffect object for actions that should be taken on apply or removal or tick. Because the types of things that the status effects should be able to do at those times are the same as when activating an ability. (and why in my actual code base why that object has a more generic name than UAbilityEffect)

I think of these as bit of a hybrid between the strategy and command patterns. There’s a little bit of both in how it works. But, one upside is that you’re always dealing with instances and never CDO’s. Though they still can’t be used as world contexts.

The topic’s drifted a bit from the original question. Feel free to hit me up in DM’s if you would like to discuss more details how to put this sort of thing into practice.

1 Like

exactly what i’m trying to do :slight_smile:

agreed but ill mark you as solution for your help, i think the takeaway for anyone who read this later is probably dont use CDOs but if you do pass in world context via an object or the META

regarding the command pattern i think im just over engineering, it felt like a waste to instantiate an object for a simple function, but i intend to using pooling anyway so its really no issue.

just to be clear you use instanced uobjects? what kind of payload do you use?