Skip to content

Conversation

@nxtum
Copy link
Contributor

@nxtum nxtum commented Aug 14, 2025

Previously there was one Persist function that would save the configs for all plugins without the ability to undo.
Now, you can specify which plugin to persist, as well as remove any/all configs (restore to default)

enum { ID = RPC::ID_CONTROLLER_CONFIGURATION };

// @omit
Core::hresult Persist() { return Persist(Core::OptionalType<string>());}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mark it as DEPRECATED so people using it will get a warning!

@nxtum nxtum requested review from pwielders and sebaszm August 15, 2025 11:12
Copy link
Contributor

@pwielders pwielders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An overall remark. I guess in the past, because all plugins where in 1 file, we added an empty config ('{}') for plugins that had no override or where destroyed. However now the configs are single entries per callsign. Whenever you "add" a file it probably takes up a sector on flash storage per file (much more than two byes to signal {} in the one file approach). I guess it is worth the effort to not store an override file if there are no changes to to config. So suggest to add this optimization in the code.
No changes on the plugin, no override file in the thunder/plugins directpry o persitent storage space to safe flash sectors...

enum { ID = RPC::ID_CONTROLLER_CONFIGURATION };

// @omit
Core::hresult Persist() { return Persist(Core::OptionalType<string>());}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mark it as DEPRECATED so people using it will get a warning!

#endif

/* static */ const TCHAR* Server::PluginOverrideFile = _T("PluginHost/override.json");
/* static */ const TCHAR* Server::PluginOverrideDirectory = _T("PluginHost/");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to move the directory now to Thunder/plugins and store the configs there, it is in the same line as the fixed configs are. Maybe you could even swap this line than with the nex line and use the default of the plugins config from there so place this:

/* static */ const TCHAR* Server::PluginOverrideDirectory = _T("Thunder/") + Server::PluginConfigDirectory;

on the line "after" the Server::PluginConfigDirectory declaration

// Create individual plugin configuration files
string filePath;
filePath.reserve(persistentFolder.size() + name.size() + sizeof("Override.json") - 1);
filePath.append(persistentFolder).append(name).append("Override.json");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we put them all in a folder (e.g. previous remark Thunder/plugins. I guess we could than use the same sematics as we do in thunder an only append the name with json, so:

filePath.append(persistentFolder).append(name).append(".json");

than it is consistent with the configs we find in /etc/Thunder/plugins as the startin point. Consistency is key ;-)

@pwielders pwielders self-requested a review September 6, 2025 11:04
Copy link
Contributor

@pwielders pwielders left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Questions/Remark:

  1. Why is in the save, the way to create an override a lamda i.s.o. a normal private method?
  2. Comparing if the configs are different might be a bit more tricky. Cause officially:
{ "fieldA": 1, "fieldB" : 2 } === { "fieldB": 2, "fieldA": 1}

This is now detected as different configs.
And probbaly even worse:

{     "field":   1 } === {"field":1}

Is already detected as a different config :-)

got 2 suggestions to avoid this (or make it more clear):

  1. Use a flag on the service that if the "config" is set through a method, flag it as dirty and consider the config to be changed (even if it was not changed :-)
  2. enhance the Core::JSON::VariantContainer class with an bool operator ==(const VariantContainer& ) const and load each string into the VariantContainer class and compare those (field availability and there corresponding values)

I have a preference for the second option enhance the JSON class :-) might be usefull in more places and the VariantContainer is already used anway, somewhere in the PluginServer code but aI am also interested in @sebaszm opinion here...


/* static */ const TCHAR* Server::PluginOverrideFile = _T("PluginHost/override.json");
/* static */ const TCHAR* Server::PluginConfigDirectory = _T("plugins/");
/* static */ const TCHAR* Server::PluginOverrideDirectory = _T("Thunder/plugins/");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use EXPAND_AND_QUOTE(NAMESPACE) instead of using framework name directly.


// See if the persitent path for our-selves exist, if not we will create it :-)
Core::File persistentPath(_config.PersistentPath() + _T("PluginHost"));
Core::File persistentPath(_config.PersistentPath() + _T("Thunder/plugins"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the constant instead of repeating the path name.

Comment on lines 1780 to 1790
// Create individual plugin configuration files
string filePath;
filePath.reserve(persistentFolder.size() + name.size() + sizeof(".json") - 1);
filePath.append(persistentFolder).append(name).append(".json");

_pluginOverrideFiles.emplace(
std::piecewise_construct,
std::forward_as_tuple(name),
std::forward_as_tuple(std::move(filePath))
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need to remember the filenames for each callsign? if we only stored the base folder having a callsing we can every time find the right path. I don't think configs are being overiden so frequent to justify this flamboyance with memory.

Comment on lines 1821 to 1827
// Clear all currently set values, they might be from the previous run.
Clear();

Callsigns::const_iterator current(_callsigns.find(index->Callsign()));
// Read the file and parse it into this object.
IElement::FromFile(storage);
_serverconfig.SetPrefix(Prefix.Value());
_serverconfig.SetIdleTime(IdleTime.Value());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part shouldn't be repeated for each plugin config. This clears and looks for prefix and idletime in each of the plugins' config while this is Thunder property.

uint32_t result = Core::ERROR_NONE;

auto CreateOverride = [this, &result](ServiceMap::Iterator& indexService) {
const string currentCallsign = indexService->Callsign();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A copy!!

}

Core::File storage(indexFile->second);
if (storage.Create() == true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not write the override config at all if it's not changed! Let the absence of the override file mean there is no override.

Comment on lines +4859 to +4871
Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideDirectory);
return (infoBlob.Save(callsign));
}

return (infoBlob.Save());
uint32_t Restore(const Core::OptionalType<string>& callsign)
{
Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideDirectory);
return (infoBlob.Destroy(callsign));
}

uint32_t Load()
{
Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideFile);

Override infoBlob(_config, _services, Configuration().PersistentPath() + PluginOverrideDirectory);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this inefficient approach. Even if we are working with a single callsign we still create the Override object and prepare for handling all plugins (ie. saving callsiings, configs, filenames.... etc).

struct EXTERNAL IConfiguration : virtual public Core::IUnknown {
enum { ID = RPC::ID_CONTROLLER_CONFIGURATION };

// @omit @deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

omit and deprecated tags are not needed as this is not a virtual method and will not be considered for code generation at any level.

@sebaszm
Copy link
Contributor

sebaszm commented Sep 11, 2025

  1. Use a flag on the service that if the "config" is set through a method, flag it as dirty and consider the config to be changed (even if it was not changed :-)
  2. enhance the Core::JSON::VariantContainer class with an bool operator ==(const VariantContainer& ) const and load each string into the VariantContainer class and compare those (field availability and there corresponding values)

I have a preference for the second option enhance the JSON class :-) might be usefull in more places and the VariantContainer is already used anway, somewhere in the PluginServer code but aI am also interested in @sebaszm opinion here...

  1. does not solve the problem much if Store() is used with no callsign then all plugins will be marked dirty even if not changed.

Agree, to do this properly we would need to normalize the JSON first. JSON::Variant would be a perfect starting point to achieve this (it is though behind COMPLEMENTARY_CODE_SET flag). We however will encounter nuance like order of elements in array (is [a,b] same as [b,a]? depends), or explicitly setting a value that is same as a default. I think too that it would be a useful feature, but perhaps we should split this into another PR.

@sebaszm sebaszm self-requested a review October 1, 2025 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants