Cannot Set Variables Through An Interface Method Because They're Read Only?

I have an interface defined in C++ and I’m using so that objects defined in C++ can interact with objects defined in blueprint. I’m having issues writing setter methods in blueprint. For some reason the blueprint compiler says the variable being set is read-only in this context. That’s the case with every test variable I tried so far. Are interfaces limited in this regard or do I need to set up my interface differently?

UINTERFACE(Blueprintable)
class PASTRYPANZERPANIC_API UBuffableInterface : public UInterface
{
	GENERATED_BODY()
};

class IBuffableInterface{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintImplementableEvent, Category="BuffableInterface")
	TArray<class UBuffComponent*> GetActiveBuffs();

	UFUNCTION(BlueprintImplementableEvent, Category="BuffableInterface")
	void SetHealth(float health) const;

	UFUNCTION(BlueprintImplementableEvent, Category="BuffableInterface")
	float GetHealth() const;
};

What’s the difference between interfaces made in Blueprint and C++?

I made a test interface in blueprint and noticed all its methods can be implemented on the event graph while C++ interface methods don’t. I’m so confused.

So I realized I wasn’t using the correct generated body macros. I fixed that and I found a video that showed what the implementation for the default constructor should contain (which the guide I was reading did not explain at all). I put that in and discovered a waterfall of compile errors.

Can someone please point me to up-to-date source material on getting this set up correctly?

UINTERFACE(Blueprintable, MinimalAPI)
class PASTRYPANZERPANIC_API UBuffableInterface : public UInterface
{
	GENERATED_UINTERFACE_BODY()
};

class IBuffableInterface{
	GENERATED_IINTERFACE_BODY()

public:
};

UBuffableInterface::UBuffableInterface(
	const FObjectInitializer &ObjectInitializer
)
	:Super(ObjectInitializer)
{

}

Okay, I removed PASTRYPANZERPANIC_API from the class definition and everything compiled correctly. Coming back to this later to test method calling in editor.

Still doesn’t work. I just don’t understand why. This defies everything I know about interfaces in C# and Java.

And I still don’t understand why I’m not able to get event nodes for interface methods made in C++ like I would with a blueprint interface. Someone please help.

After playing around with other solutions for a few days I figured out this weirdness.

If a ufunction has no return value and is const, it doesn’t show up in the blueprint graph as an event, shows up as an interface function instead, and it causes the above problem where it can’t modify values.

UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category="MyInterface")
 void DoAThing() const;

If a ufunction is the same as the above but without const, then it does show as an event and not as a function.

UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category="MyInterface")
 void DoAThing();

If there is a return value, then ufunctions show up properly as an interface function.

UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category="MyInterface")
 bool DoAThing();

If you want an overridable function instead of an event, you have to make it return something. Even if the function is design to not really return anything, it should just return a throwaway value. I’m really not a fan of this is implemented.