Class ServerPropertiesManager
Why a panel edit cannot overwrite operator content (maintainer decisions of 2026-10-04, "operator-written
configuration is never overwritten" and "what code may write, by file type": a panel edit writes only the named
key; UltiKits/UltiTools-Reborn#607). The file is never re-emitted: an edit replaces the value text of the one
physical line that defines the key and nothing else - the header and operator comments, key order, the key text
and separator as written, line terminators and every other value keep their bytes. The file is decoded the way
the running server reads it (measured from the Paper 1.21.11 jar, Settings#loadFromFile: strict UTF-8,
falling back to ISO-8859-1 when the file is not valid UTF-8), the new value is escaped with the
Properties value grammar in a form that reader returns exactly, and the result is re-encoded in the
charset it was decoded with. Before publishing, every other line is checked byte for byte and a fresh parse must
return the new value and every other key unchanged; a key defined on more than one line, or over continuation
lines, is refused with a reason. Publication is atomic through AtomicConfigWriter, and only while the file
still holds the bytes the edit was made against. A refusal writes nothing and logs one WARNING naming the file,
the key and the reason, never a value.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static final classOne physical line that defines a key, located with thePropertiesgrammar.private static final classThe file's bytes and its text as the server decodes them: strict UTF-8, or ISO-8859-1 when the bytes are not valid UTF-8 (Paper 1.21.11Settings#loadFromFile, measured from the server jar).static final classOutcome of oneset_allbatch.private static enumWhat actually happened to one key.private static final classAn outcome, and for a refusal the reason (no value). -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final Stringprivate static final Loggerprivate final Fileprivate UltiPanelWebSocketClientprivate static final ObjectSerializes panel edits of the file within this server run. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionapplySetAll(com.google.gson.JsonObject values) Apply a batch of properties, report it over the socket, and hand the outcome back.private com.google.gson.JsonObjectprivate static List<byte[]> byteLines(byte[] bytes) Splits bytes into lines, each with its terminator (LF, CR or CRLF).private static StringOne line naming why a single-key write did not happen, ornullwhen itdid.private static booleanprivate static StringProperties#saveConvertfor a value: a leading space,\,= : # !and the four control escapes are escaped; any other control character, a lone surrogate, and - in a file the server reads as ISO-8859-1 - every character above~is written as\uXXXX, which the reader decodes in both charsets.The whitelisted keys the file defines, decoded the way the server reads them (see the class description).private voidvoidhandleServerProperties(com.google.gson.JsonObject data) Handle WebSocket message for server properties operations.private voidhandleSet(com.google.gson.JsonObject data) private voidhandleSetAll(com.google.gson.JsonObject data) private static booleanisWhitespace(char c) private static StringlineNumbers(List<ServerPropertiesManager.Definition> definitions) private static ServerPropertiesManager.WriteResultprivate voidsendResponse(com.google.gson.JsonObject response) booleansetProperty(String key, String value) voidprivate static int[]splitKey(CharSequence line) Where the key ends and the value starts in one logical line, exactly asProperties#load0decides: the key ends at the first unescaped=,:or whitespace; then whitespace, at most one=or:, and whitespace are skipped.private static com.google.gson.JsonArraytoJsonArray(List<String> keys) private static StringProperties#loadConvert:\uXXXX,\t \n \r \f, and\xfor any other x.private voidThe response above is what the panel reads; this is what a server operator reads.writeProperty(String key, String value) Why this exists next tosetProperty(String, String): the boolean is lossy.
-
Field Details
-
LOGGER
-
WRITE_LOCK
Serializes panel edits of the file within this server run. -
FILE_NAME
- See Also:
-
serverRoot
-
webSocketClient
-
SAFE_KEYS
-
-
Constructor Details
-
ServerPropertiesManager
-
-
Method Details
-
setWebSocketClient
-
getSafeProperties
The whitelisted keys the file defines, decoded the way the server reads them (see the class description).- Returns:
- key to value; empty when the file is absent, unreadable or unparseable
-
setProperty
-
writeProperty
Why this exists next tosetProperty(String, String): the boolean is lossy. Afalsecould mean "the key is not on the whitelist", "the running server server's server.properties does not have the key", "there is no server.properties to write to", or "the write itself failed" — four situations with four different fixes, collapsed into one value. The batch path has to tell the caller which one happened, so the real outcome is produced here andsetPropertykeeps its original contract by narrowing it.The write changes only the line defining
key(see the class description for why nothing else can change); a refusal carries its reason. -
refuse
-
lineNumbers
-
isWhitespace
private static boolean isWhitespace(char c) -
endsWithOddBackslashes
-
splitKey
Where the key ends and the value starts in one logical line, exactly asProperties#load0decides: the key ends at the first unescaped=,:or whitespace; then whitespace, at most one=or:, and whitespace are skipped.- Returns:
[keyLength, valueStart]
-
unescape
Properties#loadConvert:\uXXXX,\t \n \r \f, and\xfor any other x. -
escape
Properties#saveConvertfor a value: a leading space,\,= : # !and the four control escapes are escaped; any other control character, a lone surrogate, and - in a file the server reads as ISO-8859-1 - every character above~is written as\uXXXX, which the reader decodes in both charsets. In a UTF-8 file other characters are written as they are, as the server itself writes them. -
byteLines
Splits bytes into lines, each with its terminator (LF, CR or CRLF). -
describeOutcome
One line naming why a single-key write did not happen, ornullwhen itdid. Exposed throughhandleSet(JsonObject)'s response so a panel operator is told the reason, not justsuccess: false. -
handleServerProperties
public void handleServerProperties(com.google.gson.JsonObject data) Handle WebSocket message for server properties operations.- Parameters:
data- the message data
-
handleGet
private void handleGet() -
handleSet
private void handleSet(com.google.gson.JsonObject data) -
handleSetAll
private void handleSetAll(com.google.gson.JsonObject data) -
applySetAll
Apply a batch of properties, report it over the socket, and hand the outcome back.The return value is the point.
handleSetAlldoes not need it — it only ever builds a message — butupdate_configwithfileName: server_propertiesroutes through here too, and itsconfig_update_responseis supposed to distinguish "delivered" from "took effect". Before this returned anything, that path had no way to learn the answer and reported success unconditionally.- Parameters:
values- key → value, an explicit JSON null meaning "leave this key alone"- Returns:
- what happened to each key
-
buildSetAllResponse
-
toJsonArray
-
warnIfIncomplete
The response above is what the panel reads; this is what a server operator reads. Both are needed: someone changing a setting that silently does not apply otherwise finds nothing on either side. -
sendResponse
private void sendResponse(com.google.gson.JsonObject response)
-