perobjectconfig and how it's used to have a shared configuration or multiple configurations

Discussions about Coding and Scripting
User avatar
_21
Experienced
Posts: 86
Joined: Mon Aug 30, 2021 10:51 am

perobjectconfig and how it's used to have a shared configuration or multiple configurations

Post by _21 »

I decided to post about this useful feature as I don't seem to be able to find much publicly available good information on this topic.
There was an old topic https://ut99.org/viewtopic.php?p=106845#p106845 but it contains some dead links which is concerning.
I saw some questions in the discord about this topic recently, I'm basically reposting my answer but also extending it.



What is perobjectconfig ?


It's a class decorator.
It tells the engine that the configurations should be stored separately for each instance of the object based on object name.

Code: Select all

class MapVoteClientConfig extends Object
    config(MVE_ClientConfig) perobjectconfig;
It's considered to be an undocumented unreal script feature. https://www.oldunreal.com/wiki/index.ph ... jectConfig
It allows having the configuration storage of an object based on object name instead of the default [PackageName.ClassName] inside UnrealTournament.ini
This unlocks a few key features that I've seen multiple mutators using.
uc_perobejct_config.png
Objects can be named when created with the extended new operator.

Code: Select all

new (class'MVE_ClientConfig', 'MapVoteClientConfig') class'MapVoteClientConfig';


How do I share configuration across multiple package names?


perobjectconfig allows you to directly specify the INI section name and because of this you can have classes from separate packages use the same section.

If you want different packages to share same settings then the perobjectconfig can be effectively used to store the config in the same place across multiple versions of a mutator.
For example the current package is MVE2h and the next release will be named MVE2i then perobjectconfig can be used so that the client settings stay in the same place even after new versions of mapvote:

Code: Select all

class MapVoteClientConfig extends Object
    config(MVE_ClientConfig) perobjectconfig;

var() config color BackgroundColor;
var() config color BoxesColor;
var() config int BoxesTextColor;
var() config int GameModTitleColor;
var() config int RuleTitleColor;
var() config int MapTitleColor;
var() config int KickVoteTitleColor;
var() config int PlayerTitleColor;
var() config int MapVoteTitleColor;
var() config string SelectedGameMode;
var() config string SelectedGameRule;
var() config string SelectedMap;
Then you need to instance it with the new operator giving it a name.

Code: Select all

static function MapVoteClientConfig GetInstance()
{
    // ensures config is read from same ini section regardless of package name
    return new (class'MVE_ClientConfig', 'MapVoteClientConfig') class'MapVoteClientConfig';
}
This will create ini file MVE_ClientConfig with section [MapVoteClientConfig] which looks like this:

Code: Select all

[MapVoteClientConfig]
BackgroundColor=(R=51,G=24,B=77,A=0)
BoxesColor=(R=255,G=221,B=188,A=0)
BoxesTextColor=10
GameModTitleColor=2
RuleTitleColor=3
MapTitleColor=4
KickVoteTitleColor=5
PlayerTitleColor=6
MapVoteTitleColor=7
MsgTimeOut=8.000000
bUseMsgTimeout=False
bLoadScreenShot=True
SelectedGameMode=Assault
SelectedGameRule=Instagib
SelectedMap=AS-Approach
Note: that a dummy class is used to create the INI file name in the example above.
It's also possible to use None as the outer parameter in new, in which case the filename will be taken from the config(ConfigFileName)

Code: Select all

// dummy object for referencing MVE_ClientConfig.ini
class MVE_ClientConfig extends Object;
When MVE2i is released it will read these same property values which were written by the older MVE2h package.



How do I store multiple configurations for a class?


Because section where the configuration is written to is specified by a name, all you need to do is to use multiple names.
With this you can have a hardcoded set of configurations.

Code: Select all

	assaultStats = new (class'MyDataFile', 'AssaultMapStatistics')class'MyStatisticsClass';
	ctfStats = new (class'MyDataFile', 'CTFMapStatistics')class'MyStatisticsClass';
	dmStats = new (class'MyDataFile', 'DMMapStatistics')class'MyStatisticsClass';
But this can be taken further if you could dynamically create a name.
It turns out that you can dynamically create new names by using SetPropertyText on a name variable.

Code: Select all

class MyDataFile extends Object;

var name ResultName;

function MyDataTable GetMyDataTable(string tableName) 
{
	self.SetPropertyText("ResultName", tableName);
	return new (class'MyDataFile', specificTableName)class'MyDataTable';
}
This can be used to store dynamic number of configurations for a class.
Which can be retrieved by a string key.



Working example code


Finally I'll share a short example code you can very easily get to run.
It is a basic commandlet that can be run from command line.
This code is short and if you're familiar with the rest of the UnrealScript concepts then it should be easy to understand.
You can select this, paste it into a file and play with it:

Code: Select all

class TestPerObjectConfig extends Commandlet perobjectconfig;

var config string Data;

var name SectionName;

event int Main( string Parms )
{
	WriteData("ProfileA", "12345");
	WriteData("ProfileB", "67890");
	Log(ReadData("ProfileA"));
	Log(ReadData("ProfileB"));
	return 0;
}

function WriteData(string section, string value)
{
	local TestPerObjectConfig obj;
	SetPropertyText("SectionName", section);
	obj = new (class 'TestPerObjectConfig', SectionName) class'TestPerObjectConfig';
	obj.Data = value;
	obj.SaveConfig();
}

function string ReadData(string section)
{
	local TestPerObjectConfig obj;
	SetPropertyText("SectionName", section);
	obj = new (class 'TestPerObjectConfig', SectionName) class'TestPerObjectConfig';
	return obj.Data;
}
I built it and ran it on using the 436 copy of the game.

Code: Select all

>ucc TestPerObjectConfig.TestPerObjectConfig

=======================================
ucc.exe: UnrealOS execution environment
Copyright 1999 Epic Games Inc
=======================================

Executing Class TestPerObjectConfig.TestPerObjectConfig
12345
67890
Running it also has the side effect of having the ini file ut99/System/TestPerObjectConfig.ini created which contains the following:

Code: Select all

[ProfileA]
Data=12345

[ProfileB]
Data=67890




Specifying the INI file name


There are 2 ways to do it. Either through using a class name through the new operator, or if left as None then the filename will be taken from the config(ConfigurationFile) class decorator.
Since classes cannot be created dynamically this the filenames will be hardcoded.

Code: Select all

class TestPerObjectConfig extends Commandlet perobjectconfig;

event int Main()
{
	obj = new (class'TestPerObjectConfig', SectionName) class'TestPerObjectConfig';
}
From the point of view of which file the data is saved to these two are equivalent.

Code: Select all

class TestPerObjectConfig extends Commandlet perobjectconfig config(TestPerObjectConfig);

event int Main()
{
	obj = new (None, SectionName) class'TestPerObjectConfig';
}
However, I recommend using the first method which does not have the outer class set to None.
If the outer class is set to None that will make the object appear in Transient package.
Which cannot is private and does not serialize and this would make it so that you cannot save game in SinglePlayer.




That's it from my side.

I hope this helps someone.

Feel free to share additional use cases using perobjectconfig or ask questions about perobjectconfig.
If there is any wrong or misleading information in this first post then I will edit it to have it corrected.

Special thanks goes to @Buggie and @Deaod who helped complete the info
You do not have the required permissions to view the files attached to this post.