Property modification works but does not work?

Hi guys… I encountered a super weird problem. I have been stuck for days… Please help :cry:

So I have two properties for my character, PowerLevel, and BaseSpeed:

//ATest1Character.h

UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category=Power)
float PowerLevel;

UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category=Power)
float BaseSpeed;

I assign them default values in the class constructor:

//ATest1Character.cpp

PowerLevel = 100.f;

BaseSpeed = 5.f;

I also log the memory address of the this pointer and the two properties (you will see why later):
//ATest1Character.cpp

UE_LOG(LogTemp, Warning, TEXT("this start:%p"), (void *)this);
UE_LOG(LogTemp, Warning, TEXT("powerlevel ptr start: %p"), (void *)&PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("powerlevel start: %f"), PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("basespeed ptr start: %p"), (void *)&BaseSpeed);
UE_LOG(LogTemp, Warning, TEXT("basespeed start: %f"), BaseSpeed);

Now, I want to modify PowerLevel and BaseSpeed from a sqlite table called player. So I get the respective PowerLevel and BaseSpeed from the table and assign them to these variables. However, they seem to reset back to those default values, even tho PowerLevel and BaseSpeed BOTH have the new values after the assignment!

Please see the following:

I use the function sqlite3_exec, which executes an sql statement and returns the value in a callback function.

Sqlite is written in c, so the callback function has the following signature:

(void *, int, char**, char**);

With the following signature, we cannot pass a normal class method, since normal class methods passes THIS as a implicit parameter. However, a static class method does not have a this pointer, so I tried to do a workaround by passing that as the callback:

//ATest1Character.h

    static int StaticGetStats(void*, int, char**, char**);

and my sql_exec function call:

//ATest1Character.cpp

sqlite3 *db;
char *zErrMsg = 0;
int rc;
char * sql;

rc = sqlite3_open("db/pstats.db", &db);
if (GEngine)
{		
	if (rc == SQLITE_OK){
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Connection Opened!  Yay!"));

		sql = "Select BasePowerLevel, BaseSpeed from player where name = \"player1\"";
		rc = sqlite3_exec(db, sql, ATest1Character::StaticGetStats, this, &zErrMsg);

As you can see, I pass the THIS pointer when calling sqlite3_exec. This is because sqlite3_exec passes the THIS pointer as a void* in the first argument of the callback function - In this case, the first argument in ATest1Character::StaticGetStats.

so in my StaticGetStats body definition, since I have a pointer to the instance that called it, I can cast the void* back to a ATest1Character*, and call the NORMAL NON STATIC GetStats class method of the instance:

//ATest1Character.cpp

int ATest1Character::StaticGetStats(void *me, int columns, char **columnvalues, char **columnnames){
UE_LOG(LogTemp, Warning, TEXT("Static me ptr is:%p"), (void *)me);

TArray<FString> UColumnValues;
TArray<FString> UColumnNames;

for (int i = 0; i < columns; i++){
	FString fcolumnvalue(columnvalues[i]);
	UE_LOG(LogTemp, Warning, TEXT("columnvalue is:%s"), *fcolumnvalue);
	UColumnValues.Add(fcolumnvalue);
	FString fcolumnnames(columnnames[i]);
	UColumnNames.Add(fcolumnnames);
}


ATest1Character* Myself = static_cast<ATest1Character*>(me);
if (Myself != NULL)
	if (sizeof(columns) == 4){
	int32 UColumns = columns;
	Myself->GetStats(UColumns, UColumnValues, UColumnNames);
	}
return 0;
  }

Finally, in my class method GetStats, I get the values I need from the table and assign them to my PowerLevel and BaseSpeed:

//ATest1Character.cpp

 void ATest1Character::GetStats(int32 UColumns, TArray<FString> UColumnValues, TArray<FString> UColumnNames){
if (GEngine){
	UE_LOG(LogTemp, Warning, TEXT("this after:%p"), (void *)this);
	for (int i = 0; i < UColumns; i++){
		UE_LOG(LogTemp, Warning, TEXT("UColumnValue is %s"), *UColumnValues[i]);
		GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, UColumnValues[i]);
		GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, TEXT(" = "));
		GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, UColumnNames[i]);
		GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, TEXT("\n"));
		if (UColumnNames[i] == "BasePowerLevel"){
			float pl = FCString::Atof(*UColumnValues[i]); //value should be 2000
			PowerLevel = pl;
			UE_LOG(LogTemp, Warning, TEXT("powerlevel ptr after: %p"), (void *)&PowerLevel);
			UE_LOG(LogTemp, Warning, TEXT("powerlevel after: %f"), PowerLevel);
		}
		if (UColumnNames[i] == "BaseSpeed"){
			float bs = FCString::Atof(*UColumnValues[i]); //value should be 10
			BaseSpeed = bs;
			UE_LOG(LogTemp, Warning, TEXT("basespeed ptr after: %p"), (void *)&BaseSpeed);
			UE_LOG(LogTemp, Warning, TEXT("basespeed after: %f"), BaseSpeed);
		}
	}
}
}

In the Tick function of my character, I print to the log the current address and value of PowerLevel and BaseSpeed:

//ATest1Character.cpp

void ATest1Character::Tick(float DeltaSeconds){
Super::Tick(DeltaSeconds);
UE_LOG(LogTemp, Warning, TEXT("Powerlevel Tick: %f"), PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("PowerLevel ptr Tick: %p"), &PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("BaseSpeed Tick: %f"), BaseSpeed);
UE_LOG(LogTemp, Warning, TEXT("BaseSpeed ptr Tick: %p"), &BaseSpeed);
CharacterMovement->MaxWalkSpeed = SpeedFactor * PowerLevel + BaseSpeed;
}

When I run the game, in my character’s constructor, my character’s PowerLevel and BaseSpeed are supposed to be updated by accessing the table. However, it doesn’t happen.

I instantly thought that perhaps I was creating a copy of the character and setting the variables in the copy. So i logged the memory address of the THIS pointer and the PowerLevel and BaseSpeed properties before the sqlite call and after it was made. But the log shows that they are the SAME memory addresses:

//Log

LogTemp:Warning: this start:000000D9177B6400
LogTemp:Warning: powerlevel ptr start: 000000D9177B6918
LogTemp:Warning: powerlevel start: 100.000000
LogTemp:Warning: basespeed ptr start: 000000D9177B6920
LogTemp:Warning: basespeed start: 5.000000
LogTemp:Warning: Static me ptr is:000000D9177B6400
LogTemp:Warning: columnvalue is:2000.0
LogTemp:Warning: columnvalue is:10.0
LogTemp:Warning: this after:000000D9177B6400
LogTemp:Warning: UColumnValue is 2000.0
LogTemp:Warning: powerlevel ptr after: 000000D9177B6918
LogTemp:Warning: powerlevel after: 2000.000000
LogTemp:Warning: UColumnValue is 10.0
LogTemp:Warning: basespeed ptr after: 000000D9177B6920
LogTemp:Warning: basespeed after: 10.000000
PIE: Info Play in editor start time for /Game/Maps/UEDPIE_0_Example_Map -0.712
LogTemp:Warning: Powerlevel Tick: 100.000000
LogTemp:Warning: PowerLevel ptr Tick: 000000D9177B6918
LogTemp:Warning: BaseSpeed Tick: 5.000000
LogTemp:Warning: BaseSpeed ptr Tick: 000000D9177B6920
LogTemp:Warning: Powerlevel Tick: 100.000000
LogTemp:Warning: PowerLevel ptr Tick: 000000D9177B6918
LogTemp:Warning: BaseSpeed Tick: 5.000000
LogTemp:Warning: BaseSpeed ptr Tick: 000000D9177B6920
LogTemp:Warning: Powerlevel Tick: 100.000000
LogTemp:Warning: PowerLevel ptr Tick: 000000D9177B6918
LogTemp:Warning: BaseSpeed Tick: 5.000000
LogTemp:Warning: BaseSpeed ptr Tick: 000000D9177B6920

Now, if I try to assign values to PowerLevel and BaseSpeed in a normal class method, the assignment works perfectly. Does anyone have any idea whats going on here? I have been stuck on this issue for days, and I simply cannot understand how this problem can exist. Is there something going on behind the scenes in unreal engine that’s resetting the properties?

I’ve been through your code and I can’t see any obvious problems. I notice that the two properties you are trying to set are both BlueprintReadWrite. Do you have a blueprint which is changing these values at the start of play? ie. After the constructor, but before Tick().

Nope, I tried taking out all my blueprint nodes for my character to test if that was the case…

For now, I’ve been able to get around this variable assignment issue by wrapping my sql_exec call in a separate class method, and calling that method on my character’s BeginPlay event (instead of in the constructor of my character).

If you’re interested in tracking it down further then you could add a “Data Break Point” in Visual Studio which will fire whenever the value stored at that memory address is changed. If you haven’t done this before it’s a bit convoluted. You need to set a normal break point (in your case I would suggest the constructor), start your application and when it breaks, click on Debug->Windows->Break Points. This will bring up a break point window from which you can select New->Data Break Point. Here you specify the memory address you want to monitor, then resume execution. As soon as that memory address changes the debugger will break and you might be able to get some helpful information from the stack trace.