Class ConfigBindings
Scheduled period/delay or a CmdCD cooldown bound to a
module config key (#531). One place for the shape rules, the key lookup and the value range, so
the load-time check, the scheduler and the reload step cannot disagree about what a binding is.
A bound value is always a whole number of seconds, read from the @ConfigEntry
field of the module's own registered config entity -- the same instance /ul reload
reloads in place, resolved once at load and kept, so there is one source of truth. A reload
that validateFields() refused never reaches the binding step; the one gap is a reload
whose file write failed with an IOException, which ConfigManager.reloadConfigs
logs and continues past without running the field's own validation annotations (#533), so only the binding's own range rule is guaranteed. The default lives only in that
field's initializer; an annotation literal next to a binding is refused rather than used as a
fallback, because after init() a declared key always has a value and a fallback would be
a second, hand-synchronised copy of the default.
Ranges: a bound @Scheduled period or delay must be 1 to MAX_TICK_SECONDS
seconds -- 0 does not mean "off" and does not mean "run once". A bound @CmdCD
cooldown may be 0 to Integer.MAX_VALUE seconds, where 0 means "no cooldown"
(maintainer ruling, 2026-09-23). Out of range is refused at load; on reload the running value is
kept. A module that uses any binding must declare api-version: 630
or higher.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static final classThrown byConfigBindings.Source.reloadSource()when the bound entity's last reload failed, so the@CmdCDreload step can keep the running value and say why.(package private) static final classA resolved binding: the module's config entity and the@ConfigEntryfield a key names. -
Field Summary
FieldsModifier and TypeFieldDescription(package private) static final StringThe rule text for a bound cooldown.The field types a binding may read.(package private) static final longLargest bound period or delay, in seconds: the tick count then fits in anint, so the reschedule arithmetic (inttick plus period, inlong) cannot overflow.(package private) static final longThe same ceiling in ticks: every interval of a bound task, bound or literal, is at most this.(package private) static final intThe lowestplugin.ymlapi-versiona module using a binding may declare.(package private) static final longBukkit's nominal tick rate.(package private) static final StringThe rule text for a bound period or delay. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescription(package private) static voidcheckApiVersionFloor(UltiToolsPlugin plugin, String firstBinding) Refuses a module that uses a binding while declaring anapi-versionbelowMIN_API_VERSION.private static voidcheckLiteralHalfWithinCeiling(String owner, Scheduled scheduled) The literal half of a partially bound task is held to the same tick ceiling as the bound half, so every interval of a bound task fits in Bukkit's int tick window and the reschedule arithmetic stays exact across its wraparound (Codex round 10 on #536).private static voidcheckNamesABinding(String owner, Scheduled scheduled) A key without a config class, a config class without a key, orasync = true.private static voidcheckNoLiteralBesideItsKey(String owner, Scheduled scheduled) A literal set alongside the key that replaces it: the default lives only in the config field.(package private) static voidcheckShape(String owner, CmdCD cmdCD) Refuses aCmdCDbinding whose elements contradict each other.(package private) static voidcheckShape(String owner, Scheduled scheduled) Refuses aScheduledbinding whose elements contradict each other: a key without a config class, a config class without any key,async = true(a bound task must be sync, so that its runs are observed on the main thread where a reload reschedules it -- #531 gate-1 round 2), or a literal set alongside the key that replaces it.private static FieldfindEntryField(Class<?> entityClass, String key) (package private) static ConfigurationExceptioninvalidValueAtLoad(UltiToolsPlugin plugin, String owner, ConfigBindings.Source source, Long value, String rule) The load-time refusal for an out-of-range bound value -- the same exception type and the same "file was not modified" contract as a@Rangeviolation.(package private) static boolean(package private) static boolean(package private) static booleanisValidCooldownSeconds(Long seconds) (package private) static booleanisValidTimerSeconds(Long seconds) (package private) static ConfigBindings.Sourceresolve(UltiToolsPlugin plugin, String owner, Class<? extends AbstractConfigEntity> configClass, String key) Resolveskeyon the single config entity ofconfigClassregistered forplugin.private static PluginModuleExceptionshapeError(String owner, String detail)
-
Field Details
-
TICKS_PER_SECOND
static final long TICKS_PER_SECONDBukkit's nominal tick rate. A bound seconds value is multiplied by this.- See Also:
-
MAX_TICK_SECONDS
static final long MAX_TICK_SECONDSLargest bound period or delay, in seconds: the tick count then fits in anint, so the reschedule arithmetic (inttick plus period, inlong) cannot overflow. That is about 3.4 years (#531 gate-1 IN-03).- See Also:
-
MAX_TICKS
static final long MAX_TICKSThe same ceiling in ticks: every interval of a bound task, bound or literal, is at most this.- See Also:
-
MIN_API_VERSION
static final int MIN_API_VERSIONThe lowestplugin.ymlapi-versiona module using a binding may declare. An older framework silently drops the binding elements, so this floor is what makes it refuse the module instead (#531 gate-1 WR-03).- See Also:
-
TIMER_RULE
The rule text for a bound period or delay.- See Also:
-
COOLDOWN_RULE
The rule text for a bound cooldown.- See Also:
-
INTEGRAL_FIELD_TYPES
The field types a binding may read.
-
-
Constructor Details
-
ConfigBindings
private ConfigBindings()
-
-
Method Details
-
isBound
- Parameters:
scheduled- the annotation- Returns:
trueif any of the three binding elements is set
-
isBound
- Parameters:
cmdCD- the annotation- Returns:
trueif either binding element is set
-
checkShape
Refuses aScheduledbinding whose elements contradict each other: a key without a config class, a config class without any key,async = true(a bound task must be sync, so that its runs are observed on the main thread where a reload reschedules it -- #531 gate-1 round 2), or a literal set alongside the key that replaces it.- Parameters:
owner-Class.method, for the messagescheduled- the bound annotation- Throws:
PluginModuleException- naming the owner and the contradicting elements
-
checkNamesABinding
A key without a config class, a config class without a key, orasync = true. -
checkNoLiteralBesideItsKey
A literal set alongside the key that replaces it: the default lives only in the config field. -
checkLiteralHalfWithinCeiling
The literal half of a partially bound task is held to the same tick ceiling as the bound half, so every interval of a bound task fits in Bukkit's int tick window and the reschedule arithmetic stays exact across its wraparound (Codex round 10 on #536). -
checkShape
Refuses aCmdCDbinding whose elements contradict each other.- Parameters:
owner- the executor class, orClass.method, for the messagecmdCD- the bound annotation- Throws:
PluginModuleException- naming the owner and the contradicting elements
-
resolve
static ConfigBindings.Source resolve(UltiToolsPlugin plugin, String owner, Class<? extends AbstractConfigEntity> configClass, String key) Resolveskeyon the single config entity ofconfigClassregistered forplugin.- Parameters:
plugin- the moduleowner-Class.methodor the executor class, for the messageconfigClass- the bound config classkey- the@ConfigEntrypath- Returns:
- the resolved source
- Throws:
PluginModuleException- when the class is not registered exactly once, the key names no@ConfigEntryfield, or the field is not integralConfigurationException- when the entity's load failed with anIOExceptionthatConfigManagercaught, so its values were never validated
-
findEntryField
- Returns:
- the
@ConfigEntryfield ofentityClass(declared or inherited) whose path -- or field name, when the path is empty -- equalskey, ornull
-
isValidTimerSeconds
- Parameters:
seconds- a bound value- Returns:
truefor a scheduling value in [1,MAX_TICK_SECONDS]
-
isValidCooldownSeconds
- Parameters:
seconds- a bound value- Returns:
truefor a cooldown value in [0,Integer.MAX_VALUE]; 0 means no cooldown
-
checkApiVersionFloor
Refuses a module that uses a binding while declaring anapi-versionbelowMIN_API_VERSION.- Parameters:
plugin- the modulefirstBinding- the first bound declaration found, for the message- Throws:
PluginModuleException- if the declared floor is too low
-
invalidValueAtLoad
static ConfigurationException invalidValueAtLoad(UltiToolsPlugin plugin, String owner, ConfigBindings.Source source, Long value, String rule) The load-time refusal for an out-of-range bound value -- the same exception type and the same "file was not modified" contract as a@Rangeviolation.- Parameters:
plugin- the module refusing to loadowner-Class.methodor the executor classsource- the bindingvalue- the refused valuerule- the rule it broke- Returns:
- the exception to throw
-
shapeError
-