Finally got around to this. It’s absolutely possible and even surprisingly easy using C++.
For the record, I’ve switched to 4.9.1 in the meantime, which could make a difference. (I don’t know.)
First, I defined a struct in one of my classes’ header files:
USTRUCT(BlueprintType)
struct FTestStruct : public FTableRowBase
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
int32 testInt;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
FString testString;
};
Then I created a csv file with the necessary syntax, which I saved as Content/Data/testData.csv, and imported the csv file into the editor to create the data table, which I saved in the same folder.
Next, I added a reference to an object of type UDataTable table to MyProjectGameMode.h:
UCLASS()
class MYPROJECT_API AMyProjectGameMode : public AGameMode
{
GENERATED_BODY()
AMyProjectGameMode(); // C'tor
// Called when the game starts
virtual void BeginPlay() override;
UDataTable* MyDataTable;
};
The actual work gets done in MyProjectGameMode.cpp. In the constructor, I used FObjectFinder to grab the DataTable located at the known relative path. In BeginPlay, I then used the reference to the data table filled in the constructor and the fixed path to the csv file to actually fill the data table.
Credit goes to How to load the CSV DataTable dynamically? - Community & Industry Discussion - Epic Developer Community Forums from where I copied most of the code.
AMyProjectGameMode::AMyProjectGameMode()
{
ConstructorHelpers::FObjectFinder<UDataTable> MyDataTableObj(TEXT("UDataTable'/Game/Data/testData.testData'"));
if (MyDataTableObj.Succeeded())
{
MyDataTable = MyDataTableObj.Object;
UE_LOG(LogTemp, Warning, TEXT("Data Table found"));
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Data Table not found"));
}
}
// Called when the game starts
void AMyProjectGameMode::BeginPlay()
{
Super::BeginPlay();
FString csvFile = FPaths::GameContentDir() + "Data\\TestData.csv";
UE_LOG(LogTemp, Warning, TEXT("Attempting to read csv from path '%s'"), *csvFile);
if (FPaths::FileExists(csvFile))
{
FString FileContent;
//Read the csv file
FFileHelper::LoadFileToString(FileContent, *csvFile);
if (MyDataTable)
{
TArray<FString> problems = MyDataTable->CreateTableFromCSVString(FileContent);
if (problems.Num() > 0)
{
for (int32 ProbIdx = 0; ProbIdx < problems.Num(); ProbIdx++)
{
//Log the errors
}
UE_LOG(LogTemp, Warning, TEXT("Data Table update failed"));
}
else
{
//Updated Successfully
UE_LOG(LogTemp, Warning, TEXT("Data Table updated successfully"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("Data Table not found"));
}
}
}
(Note that there’s probably a much better place for this than the GameMode, but it seemed like a good starting place when trying this out. It also looks like you can’t output log messages in the GameMode’s BeginPlay method.)
I expected this to be much more difficult and was pleasantly surprised with the results. I was amazed to learn that with the editor open, the data table gets updated on the fly and saved whenever the csv file changes. (It’s highly likely that at least this last bit of magic only works if the csv file is also located in the Content folder.)
Unfortunately, I was unable to get packaging to work, so I can’t say whether the autoimport would also work for packaged game (probably) and if so, at what point during the initialization.
On my TODO list:
- extend the code to handle updating multiple data tables
- replace the fixed paths with something more flexible