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 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.
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;
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';
}
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
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;
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';
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';
}
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;
}
Code: Select all
>ucc TestPerObjectConfig.TestPerObjectConfig
=======================================
ucc.exe: UnrealOS execution environment
Copyright 1999 Epic Games Inc
=======================================
Executing Class TestPerObjectConfig.TestPerObjectConfig
12345
67890
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';
}
Code: Select all
class TestPerObjectConfig extends Commandlet perobjectconfig config(TestPerObjectConfig);
event int Main()
{
obj = new (None, SectionName) class'TestPerObjectConfig';
}
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