How to load the CSV DataTable dynamically?

Hi,
How to load the CSV DataTable dynamically? I want to download csv from my server and update game data… Is it possible?

I don’t know how to download a file from the server. But once you download the csv file, you can update dynamically using something similar below

FString csvFile = FPaths::GameContentDir() + "Downloads\\DownloadedFile.csv";
		if (FPaths::FileExists(csvFile ))
		{
			FString FileContent;
			// the csv file
			FFileHelper::LoadFileToString(FileContent, *csvFile );
			TArray<FString> problems = YourDataTable->CreateTableFromCSVString(FileContent);

			if (problems.Num() > 0)
			{
				for (int32 ProbIdx = 0; ProbIdx < problems.Num(); ProbIdx++)
				{		
                                            //Log the errors
				}
			}
			else
			{
				//Updated Successfully
			}
		}
1 Like

Hi,
do you know if it is possible to do this same using only BP?
So I’ve several csv files in my game folder. I just want to load one of them depending on game state, and I don’t want to use c++ at all.

Thank you for your request. There is already a feature request in our system, UE-20507, to be considered by the development staff.

Hey ,

Are there been any progress on that? I’d like to import .csv files on runtime aswel.

Thanks

Hey -

The feature request that mentioned is still currently open for considersation however I can’t say when/if it will be incorporated into the engine. I have updated the request post to indicate that this is still an issue for the community.

Cheers

CreateTableFromCSVString is working only in editor. So seems like there is no any solution for this (built-in solution of course).

So CreateTableFromCSVString doesn’t update itself on a live compiled game? How about using a plugin like VaRest and implement the data on the fly?

Not 100% sure but it may be worth checking 's victory plugin. I seem to recall some better file IO support in there.

We need some update on this. This is the best way to tweak values in the game such as rate of fire, bullet spread etc. I would like my QA to play the game-> modify values in excel → do a keypress in game to update rate of fire and see how it feels.

So the problem is that the code that reads this in the data table class is all in bed with the rest of the editor. You can’t just comment out the Editor Only ifdefs there (I tried it), it calls functions on the asset base classes that are only in the editor, and support functions that are only in editor, etc. It quickly spirals out of control. In order to support this in a standalone game, they’d either have to rewrite a version of the import code that doesn’t use any of the editor support classes or code, or they’d have to move a lot of stuff out of editor into the base engine. Sigh. I hope this is a big boat, because lots of people are in it.

I’d also like to see this. It’s been a few years, any updates?

In the end, I wrote my own. Here’s some code that may be helpful, extracted from some early Horde Mode prototyping I did for Earthfall. Naturally all the Earthfall specific stuff is useless, but it shows how to create a datatable in code, and parse a CSV into records and add them:

/*
    	This function reads in the csv file provided from Content/HordeMode/filename and parses it into
    	a datatable.  The first column is the rowkey.  Tables aren't indexed by row index in Unreal, but by a key, so they may be in any order.
    */
    bool ADirector::LoadHordeScript(FString filename)
    {
    	int rowsAdded = 0;
    	if (filename.IsEmpty())
    		return false;
    	// Create a new datatable based on the wave struct
    	UClass* DataTableClass = UDataTable::StaticClass();
    	ScriptedLines = NewObject<UDataTable>(this, DataTableClass, FName(TEXT("HordeModeDataTable")));	// Blow away any previous table
    	ScriptedLines->RowStruct = FScriptLineDescriptorRow::StaticStruct();
    
    	loopCounters.Reset();		// Clear anything we might be in the middle of
    	ScriptCallStack.Reset();
    
    	FString csvFile = FPaths::ProjectContentDir() + "HordeMode/" + filename;
    	if (FPaths::FileExists(csvFile))
    	{
    		TArray<FString> lines;
                    // Load the entire file into an array of strings
    		FFileHelper::LoadANSITextFileToStrings(*csvFile, NULL, lines);
    		const UEnum* SpawnTypeEnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("ESpawnType"), true);
    		const UEnum* LineTypeEnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EScriptLineType"), true);
    		FScriptLineDescriptorRow row;
    		if (!SpawnTypeEnumPtr || !LineTypeEnumPtr)
    		{
    			UE_LOG(Earthfall_Lore, Error, TEXT("Can't find enum for ESpawnType or EScriptLineType"));
    			return false;
    		}
    
    
                    // Parse each string into a record
    		for (int i = 1; i < lines.Num(); i++)
    		{
    			FString aString = lines[i];
    
    			TArray<FString> stringArray = {};

                            // break string into tokens broken up by commas
    			aString.ParseIntoArray(stringArray, TEXT(","), false);
    			if (stringArray.Num() == 0)
    				continue;	// Skip empty lines
    			FString LineLable = stringArray[0];
    			// CSV files will  entry if it has a ',' character in it. So if your ; comment has a , you have to look for ";   
    			if ((LineLable.Len() == 0)  || LineLable.StartsWith("\";") || LineLable.StartsWith(";"))
    				continue;	// Skip comments or lines with no label
                            // Validate number of expected tokens
    			if (stringArray.Num() < 13)
    			{
    				UE_LOG(Earthfall_Lore, Warning, TEXT("LoadHordeScript skipping non-commented line %d with only %d columns"), i, stringArray.Num());
    				continue;
    			}
                            // Parse all the tokens into a struct
    			row.LineType = (EScriptLineType)LineTypeEnumPtr->GetValueByNameString(*stringArray[1]);
    			row.SpawnType = (ESpawnType)SpawnTypeEnumPtr->GetValueByNameString(*stringArray[2]);
    			row.Min = FCString::Atoi(*stringArray[3]);
    			row.Max = FCString::Atoi(*stringArray[4]);
    			row.PlayAudio = (FCString::Stricmp(*stringArray[5], TEXT("TRUE")) == 0);
    			row.HealthMult = FCString::Atof(*stringArray[6]);
    			row.DamageMult = FCString::Atof(*stringArray[7]);
    			row.SpeedMult = FCString::Atof(*stringArray[8]);
    			row.DelayAfterLine = FCString::Atof(*stringArray[9]);
    			row.ShowCountdown = (FCString::Stricmp(*stringArray[10], TEXT("TRUE")) == 0);
    			row.NumericData = FCString::Atof(*stringArray[11]);
    			row.StringData = FName(*stringArray[12]);
                            // Add a new row to the datatable
    			ScriptedLines->AddRow(FName(*stringArray[0]), row);
    			rowsAdded++;
    // 			if (i == 1)
    // 			{
    // 				ScriptedLine = row;	// Starting wave
    // 			}
    		}
    	}
    	else
    	{
    		UE_LOG(Earthfall_AI, Error, TEXT("LoadHordeScript File %s not found!"), *csvFile);
    		return false;
    	}
    	ScriptedLine.DelayAfterLine = 0;
    	return (rowsAdded > 0);
    }
1 Like

This code apply on .h file or .cpp file?

Well that was in my .cpp file. You’ll need to extract the relevant bits for your use. It’s just an example of how I made a new table, set up the RowStruct that defines a row, in the csv and parsed each line into an instance of that struct and added them to the datatable. Your code will of course have to use your own structure based on your data, but the framework should be the same.