Settings

How the settings system works


Introduction

MoreCommands has a highly configurable settings system. To fully understand it you have to read quite a wall of text as you can see. This is not mandatory to be able to use the mod or the be able to store settings. You only need to read this if you want to be able to understand the system, if you wan't to modify the behaviour of settings (e.g. on which servers they are available) or if you wan't to understand the "settings" commands.

Note that that this explanation of the settings system first explains all aspects of the settings system in pure text form - that means in a very theoretical form - and then shows an example. If you need an example for the things explained in each section, just scroll down to the bottom of the page, there is an example which shows you every aspect explained in the theoretical sections. You can use that to visualize the explanations which improves understanding significantly.

Setting Types

Client Settings

Client Settings are settings which are available only on the client. That means that there is no way of using them if MoreCommands isn't installed client side. Commands which are available only client side will use them to store settings (e.g. the "bind" command). This will make them accessible from the client. That means that you can manipulate them for every kind of server because they are stored locally on your PC.

Server Settings

Server Settings are settings which are available only on the server. That means that there is no way of using them if MoreCommands isn't installed server side. Commands which are available only server side will use them to store settings (e.g. the "waypoints" command). This will make them inaccessible from the client because they are stored on the server. However, there is one exception: If the server a player plays on is the integrated server (that means singleplayer, multiplayer is always a dedicated server) and additionally the player to which the settings belong to (of course on a server, every player has his own settings) is the player who hosts the server (e.g. in LAN mode, there are multiple players on the server although the server is still the integrated server/singleplayer), then server settings are stored along with client settings in the same file. Basically that means for you as the one who starts a singleplayer server, your server settings behave like client settings.

Common Settings

Common Settings are settings which are available on the client and on the server. That doesn't mean that they are stored as client and as server settings but rather as client OR as server settings. In detail that means if MoreCommands is installed client side, a setting that is a common setting will always be treated as a client setting. If this is not the case, the setting is still available and will be treated as a server setting (of course only if MoreCommands is installed on the server). An example for this type of setting is the "aliases" setting which is used by the "alias" command. This command is available client and server side. If MoreCommands is installed on both sides or only on the client, the client side version will always be the one that is used otherwise the server side version will be used. The reason why that is is to be able to manipulate settings. If they would always be saved server side and you are playing on a dedicated server you would have no access to these settings.

Global Settings

Client Settings, Server Settings and Common Settings are individual player settings. That means that they are tied to a player and each player can have different values for his own settings. The counterpart to these individual player settings are global settings. Global settings are settings which are equal for all players, they have a global scope. This has the effect that you can define e.g. global aliases which all players can use and not everyone has to create them for himself. Be aware that global settings are always weighted less than player settings. That means e.g. if a player creates an alias with the same name that a global alias has, the individual alias of the player will always be preferred. Sometimes this may not be that what you want but it's hard to decide which one is the right one to use and generally local/individual settings are always preferred over global settings. You can however disable invididual player aliases and variables (currently the only two settings which are available globally). See the Configuration section for more details about that.

Setting Constraints/Dependencies

Every setting can have various dependencies. This means that a setting can be constrained by various conditions which must be fulfilled in order to make a setting available in an environment. Environment means things like the server, world, dimension, etc. (Currently these three are the only three dependencies). This means that settings can be made available only in specific environments, e.g. it doesn't make sense to use the same waypoints in different worlds so by default waypoints are world-dependent, this means that every waypoint is only available in the world in which it was created. As already said, currently settings can depend on server, world and dimension. For every constraint, it is possible to allow multiple values to be accepted for a constraint or even to omit a constraint entirely.

It is important to note that these constraints may be available only client side. E.g. it doesn't make much sense to have the "server" constraint for server-side only settings because they are stored server side and thus they are always tied to that server which is why it wouldn't make sense to allow them to be constrained by a server dependency.

Another important aspect of these constraints is that each constraint has a priority level. This is important for the settings precedene as specified in the next item. The priority levels are currently the following: dimension > world > server. This means that the dimension constraint has the highest priority folowed by the world constraint and the server constraint which has the lowest priority.

Settings precedence

Let's say we have a setting named "Test" which would have the value "Value1" for the server "TestServer" and the world "TestWorld" and the value "Value2" for the world "TestWorld" and dimension "Nether". The difference is that the first value will be available only for the server "TestServer" and for the world "TestWorld" but for all dimensions. The second value is available only for the world "TestWorld" and for the dimension "Nether" but for all servers. Now the question is: Which value is used when we are on the server "TestServer", in world "TestWorld" and in dimension "Nether" so the requirements for both values are met. Here comes the ordering of settings by precedence into play: Each setting has an associated priority which is used to sort all setting in a way that the setting with the highest priority is preferred over the settings with lower priorities. The priority of a setting is calculated using the following steps:
  1. Determine the priority level of the constraints of a setting. This priority level is the sum of the individual priority levels of each single constraint. The priority level for each constraint is chosen in a way such that there can never be the same sum for two settings with different constraints so each combination of constraints is unique (In detail each constraint has a priority level of 2n where n is a number greater or equal to zero. The higher this number the higher the priority level will be. E.g. it is n = 0 for the server constraint, n = 1 for the world constraint and n = 2 for the world constraints. Thus the priority levels are 20=1, 21=2, 22=4.
    A sum of these numbers will alway be unique).
    If the priority level of the constraints are different for two settings, this means they have different constraints and therefore different priorities so the comparison is done and we know which setting has a higher priority. If they have the same constraints, continue with step 2.
  2. In the case that two settings have the same constraints, it is possible to specify a custom priority level for a setting which will then be used to determine the priority. If both settings have specified such a priority level, then the setting with the higher priority level has a higher priority. If only one of those two settings has specified such a priority level, then this setting is always considered to have a higher priority no matter what the exact value is. If none of these two settings has specified such a priority level it is continued with step 3.
  3. The last step is to compare how restrictive a setting is. That means how many values are given for a specific constraint. If a setting has a constraint which accepts more values than another setting, this setting has a lower priority because it is less restrictive and therefore less concise in describing its allowed usage environment. So the last step to compare two setting for their priority level works the following way: For each constraint, starting with the one having the highest priority level going to the one with the lowest priority level, it is determined which setting has less accepted values for this constraint. If the amount of accepted values is unequal, the setting with less accepted values has a higher priority. If they are equal it is proceeded to the next constraint. If it turns out that both settings have exactly the same amount of accepted values for each constraint, then these two settings are finally considered equal. You should avoid these situations since then it is not guaranteed in which order such settings are sorted because the have the same priority and it is not possible to say which setting should be preferred. However such cases should be rare and if they occur you can still assign a custom priority level to these settings as mentioned in step 2.

Merged Settings

Some settings define a key-value mapping for their values. This means that such a settings consists of several keys mapped to values. E.g. this is the case for aliases which maps aliases to actual commands, for bindings which maps keyboard keys to comands, for variables which maps variables to variable values, etc.
These type of settings allow a process called merging. To explain what this is, consider the following example: We have the setting "bindings" which represents keyboard bindings and we want to have different bindings depending on the world. E.g. we want the binding "K=/say hi" to be allowed in every world. Also we want to have the binding "F=/fly" to be allowed only in world "TestWorld". Now intuitively it would make sense that in world "TestWorld" both bindings can be used and in every other world only the binding to key "K". However if we would use the settings system as it is decribed to this point, this will not work. Instead, in world "TestWorld", only the binding to key "F" would be available. The reason is simply that the entire list of all mappings is considered as the value of a setting. Since the setting with the "F" binding has a higher priority, this setting is preferred over the setting with the "K" binding. This is not what people would expect and therefore this is not how the settings system behaves. Instead settings consisting of such mappings are merged so that all mappings of settings with lower priorities are available too. If a mapping with the same key exists for multiple settings, only the mapping of the setting with the highest priority is used. In this example if we had "/fly" mapped to "K" as well instead of "F" and if we would be in "TestWorld", the "K=/say hi" mapping would be used because it has a higher priority.
This kind of merging settings has several effects for these kind of settings: When removing a mapping (or removing all mappings) there can still be mappings to exactly the same key but from settings which have a lower priority than the priority of the current mapping. So if a mapping is removed and there is a mapping of lower priority available, the mapping to be removed won't be removed but replaced with the mapping of lower priority. You can notice that behavior e.g. when you use the "/unalias" command. If there is such a mapping with lower priority, the alias you want to be removed is not really removed but replaced with the one that has a lower priority (if there is one). Another effect is there has to be a default setting which is used when new mappings are created. In this case, we have to know which priority this mapping should have. By default a setting just using the server constraint is used. See the next section about the settings command to see how you can change that behavior. Also you should know that creating a new mapping will remove all mappings for the same key which have a higher priority because if this wouldn't be done the new mapping couldn't be used because it has a lower priority than an existing one. E.g. if you had a mapping to key "K" which depends on server, world and dimension and you want to add a new mapping to "K" which depends only on the server, the old mapping will be removed.
If you don't want to merge settings at all, this is also possible to do using the "allowMerge" property in the settings file (See that section for more details).

The "settings" command

The "settings" command allows to set the constraints for a setting in game. There are three variants of this command: "settings_client", "settings_server" and "settings_global". The general syntax is:

settings_(client/server/global) <set <SETTING> {CONSTRAINTS}|reset <SETTING>>

It should be obvious that "settings_client" is for client settings, "settings_server" is for server settings and "settings_global" is for global settings. Apart from this difference, all of these three commands work exactly the same way. If you want to set, e.g. the world constraint to make a setting available only on a specific world, you have to use "/settings_(client/server/global) set SETTING_NAME world". After setting the constraints of a setting every time you set the value for this setting (or e.g. create a mapping for settings with mappings as values), it will enforce a dependency on the current value of that constraint. For example everytime you set the value from now on, it will depend on the current world. Another example: If you use "settings_client set aliases world dimension", every alias you create will be dependent on the current world and the current dimension.
Be aware that the constraints you have set via the settings command will be reset everytime a change of the current environmental value of a constraint occurs. E.g. if you move from the Overworld to the Nether, any constraints which have been set via these commands will be lost. This means if you use one of these commands to enforce a dimension dependency and you change the dimension, this dependency will be lost and you have to reuse the command. These dependencies will be lost for ALL constraints, this means even if you didn't use the dimension constraint but the world constraint and you change the dimension this dependency will be lost and you have to reuse the command. Be aware of that or otherwise you will be surprised why the command doesn't work.

Storage of settings

There are two ways of how settings are stored:

The json format

The json format will always be used for client settings and for global settings. Server settings are only saved using the json format when the player who owns the settings plays in singleplayer mode (when using LAN, only if he is the host of the game). In multiplayer server settings are saved on the server using the NBT format which is decribed below. Client settings are saved in a file called "client_settings.json" and global settings are saved in a file called "global_settings.cfg". Server settings, if they are saved as json file, are contained in the client settings file. Before I explain how a settings file looks like, we need to have a look at the basic elements of the json format (if you are familiar with it, you can skip that). There a four basic elements in the json format, which are:

JSON Objects

A json object is an element which maps other elements to a key. An object begins and ends with curly braces. The elements are assigned to their keys with a colon. The keys must be strings, that means that they must be enclosed by quotation marks. Multiple properties (key<->value pairs) must be separated with commas. For example:
{ 						<----- Beginning of an object element
	"Key1" : "Hi",				<----- A simple property
	"Key2" : [0,1,2,{"Bla":":D"}],		<----- A property with a list element as value
	"Key3" : {"Key" : "Another object"}	<----- A property with another object element as value
}						<----- End of an object element
	

JSON Arrays

A json array is an element which lists other elements. A list begins and ends with square brackets, the individual elements must be separated with commas. For example:
["Some Element", 5, 8, ["A list in a list", 36.82], {"Key": "Value"}]
	

JSON Primitives

Json primitives are simple elements such as strings and numbers. There are four primitive types:
  • Strings: A string is delimited by quotation marks, e.g. "Hi, I'm a string"
  • Numbers: Well, there's not much to say about numbers...
  • Boolean values: This is a value which is either "true" or "false" (without quotation marks)
  • The null element: There's one element which is identified with "null" (no quotation marks). This element represents no value, it is used if a value is not yet known. This element type is not used for settings.

The Structure of a settings file

The structure of a settings file looks like this:
{
"ExampleSetting" : [
	{
		"server" : ["127.0.0.1"],
		"world" : ["TestWorld"],
		"dimension" : ["Nether"],
		"allowMerge" : false,
		"value" : {
			"F" : "fly",
			"N" : "noclip"
		}
	},
	{
		"value" : {"K" : "kill"}
	}
	],
"AnotherSetting" : "blabla",
"YetAnotherSetting" : {
		"world" : "MoreCommands"
		"value" : "Hello World"
	},
"YetAnotherSetting2" : [{
		"world" : ["MoreCommands", "Test"],
		"priority": 2,
		"value" : "Hi1"
	},
	{
		"world" : ["Test", "World"],
		"priority": 5,
		"value" : "Hi2"
	}
	]
}
	
As you can see, all settings are contained in an object element of which the keys are the setting names. A setting can either be a primitive type or an object or an array.

If it is a primitive element, it is assumed that this primitive element is the value of the setting. As there are no dependencies given, a setting which is mapped directly to a primitive value is not constrained by any depencency.

If it is an object element, this object element will be treated as part of an array element of which it is the first and only element so that's just "syntactic sugar".

If it is an array element, every primitive element or array element this array contains will be treated as direct value of the setting just as if you use a primitive element directly. Note that if you do it this way, there are still no dependencies given, so every of these elements has the same priority which will cause them to overwrite themselves and only one value will be used which will be the last one in the array. So the usage of a array element is either to contain exactly ONE primitive or array element which will then be used as the value of the setting with no dependencies or to contain one or multiple object elements which are used to specifiy different dependencies.

The object elements are structured in the following way: The object must have at least the key "value" which is the actual value of the setting. There are the following optional keys to add:

  • The "allowMerge" key:
    This key is a boolean value. If it is set to true, you can omit this key, as not specifying it always means "true". This setting has only an effect if the value of a setting is an object element (which means it is a list of mappings). This key has the purpose to allow/disallow merging of settings with mappings as their values as desribed in section Merged Settings. See that section for details about what this key does. In this example it would disallow to merge the setting containing the "F" and "N" mapping to be merged with the setting containing the "K" mapping.
  • The "priority" key:
    This key must be a positive numeric value and specifies the custom priority level described in section Settings Precedence (See that for more details) and is therefore used to assign a custom priority level for settings which have equal constraints. In this example the two value of "YetAnotherSetting2" both contain only the server constraint but with exactly the equal amount of values for that constraint. Therefore the would be considered equal in terms of priority. Now which value would be used in world "Test"?. Nobody knows because of the fact they're equal there's no guarenteed order so it may be the first value but also the second value. To prevent this, the "priority" key is specified which says the value 2 has a higher priority and will therefore be used.
  • The constraint keys:
    Every other key added to the json object will be treated as constraint (of course only if that constraint exists). Currently there are three constraints: "server", "world" and "dimension". The name of each constraint must either be mapped to a single string value which will then be the only accepted value of that constraint or to a json array which consists of multiple strings which then is treated as a list of accepted values. Note that for the "dimension" constraint, you can use numeric dimension id's as well but DON'T DO THAT. The reason is simple: Dimension id's are not consistent. The can vary depending on the installed mods which register additional dimensions, except for the built-in dimensions, "Overworld" (= 0), "Nether" (= -1) and "The End" (= 1). Apart from these three dimensions, don't use numeric ids. Prefer dimension names.

The NBT format

The NBT format is minecraft's own format to store data. It is used for server settings of players which are not the host of a singleplayer game. Settings stored in nbt files are saved in a directory named "settings_server". The actual nbt files have always the name "PLAYER_UUID.dat" where "PLAYER_UUID" is the UUID of a player (an id for the player, you can google for "UUID" if you don't know what it is). The NBT format is actually quite similar to the json format which is why the structure of the settings is exactly the same as it is for the json format. There are some different element types than json has (especially more numeric types) but the overall structure is the same. There is however one big difference between json and NBT: The NBT format is a binary format and not a text format, so you can't simply open it with a text editor and change settings. Another aspect that is different is that all constraints which are available only client side won't be stored in NBT because NBT is only used to store server settings and therefore they are always on the same server so it would be superfluos to store these constraints. If you still want to modify settings saved as nbt files, you can use a nbt editor, there are several of these, just google for "nbt editor" or something like that.

Final Word

If you didn't fall asleep while reading this you should now have the full knowledge to use the settings system how you want and not only how it is configured by default which can be quite useful sometimes. If you didn't understand anything or you didn't understand the system completely just don't do anything with it, the default configuration works exactly how it is needed almost the whole time. This is really only needed for an advanced usage of the settings system or to create settings directly without using any commands (useful e.g. for macros).