Paper plugins use YAML configuration files to store settings. The JavaPlugin class provides built-in methods for working with a default config.yml file.
Default Configuration
The default configuration file is config.yml located in your plugin’s data folder.
Configuration Methods
From JavaPlugin.java, the configuration methods:
public FileConfiguration getConfig()
public void reloadConfig()
public void saveConfig()
public void saveDefaultConfig()
public void saveResource(String resourcePath, boolean replace)
Creating a Default Configuration
Create config.yml in resources
Create a config.yml file in src/main/resources/:# Plugin settings
enable-feature: true
max-players: 100
welcome-message: "Welcome to the server!"
# Database settings
database:
host: localhost
port: 3306
name: minecraft
username: root
password: ""
# List of blocked items
blocked-items:
- TNT
- BEDROCK
- COMMAND_BLOCK
Load configuration in plugin
Load the configuration in your plugin’s onEnable() method:@Override
public void onEnable() {
// Copy default config if it doesn't exist
saveDefaultConfig();
// Access configuration values
boolean featureEnabled = getConfig().getBoolean("enable-feature");
int maxPlayers = getConfig().getInt("max-players");
String welcomeMessage = getConfig().getString("welcome-message");
getLogger().info("Feature enabled: " + featureEnabled);
}
saveDefaultConfig() only creates the file if it doesn’t exist. It won’t overwrite existing configurations.
Reading Configuration Values
Basic Values
FileConfiguration config = getConfig();
// String
String message = config.getString("welcome-message");
String messageWithDefault = config.getString("missing-key", "Default value");
// Boolean
boolean enabled = config.getBoolean("enable-feature");
// Integer
int maxPlayers = config.getInt("max-players");
// Double
double multiplier = config.getDouble("damage-multiplier");
// Long
long timestamp = config.getLong("last-backup");
Lists
// String list
List<String> blockedItems = config.getStringList("blocked-items");
// Integer list
List<Integer> allowedLevels = config.getIntegerList("allowed-levels");
// Generic list
List<?> items = config.getList("items");
Nested Values (Configuration Sections)
// Access nested values with dot notation
String host = config.getString("database.host");
int port = config.getInt("database.port");
// Or get the entire section
ConfigurationSection dbSection = config.getConfigurationSection("database");
if (dbSection != null) {
String host = dbSection.getString("host");
int port = dbSection.getInt("port");
String dbName = dbSection.getString("name");
}
Check if Key Exists
if (config.contains("welcome-message")) {
String message = config.getString("welcome-message");
}
// Check with type
if (config.isString("welcome-message")) {
// Value exists and is a string
}
if (config.isInt("max-players")) {
// Value exists and is an integer
}
Writing Configuration Values
Setting Values
FileConfiguration config = getConfig();
// Set basic values
config.set("enable-feature", true);
config.set("max-players", 100);
config.set("welcome-message", "Hello!");
// Set nested values
config.set("database.host", "localhost");
config.set("database.port", 3306);
// Set lists
config.set("blocked-items", List.of("TNT", "BEDROCK"));
// Save to file
saveConfig();
Creating Sections
FileConfiguration config = getConfig();
// Create a new section
ConfigurationSection section = config.createSection("new-section");
section.set("key1", "value1");
section.set("key2", 42);
saveConfig();
Reloading Configuration
Reload the configuration from disk:
@Override
public boolean onCommand(CommandSender sender, Command command,
String label, String[] args) {
if (command.getName().equalsIgnoreCase("reload")) {
reloadConfig(); // Reloads config.yml from disk
sender.sendMessage("Configuration reloaded!");
return true;
}
return false;
}
From JavaPlugin.java:171-180:
public void reloadConfig() {
newConfig = YamlConfiguration.loadConfiguration(configFile);
final InputStream defConfigStream = getResource("config.yml");
if (defConfigStream == null) {
return;
}
newConfig.setDefaults(YamlConfiguration.loadConfiguration(
new InputStreamReader(defConfigStream, StandardCharsets.UTF_8)));
}
Custom Configuration Files
Create additional configuration files beyond the default config.yml:
public class MyPlugin extends JavaPlugin {
private FileConfiguration messagesConfig;
private File messagesFile;
@Override
public void onEnable() {
// Load default config
saveDefaultConfig();
// Load custom config
loadMessagesConfig();
}
private void loadMessagesConfig() {
messagesFile = new File(getDataFolder(), "messages.yml");
// Create if doesn't exist
if (!messagesFile.exists()) {
saveResource("messages.yml", false);
}
messagesConfig = YamlConfiguration.loadConfiguration(messagesFile);
}
public FileConfiguration getMessagesConfig() {
if (messagesConfig == null) {
loadMessagesConfig();
}
return messagesConfig;
}
public void saveMessagesConfig() {
if (messagesConfig == null || messagesFile == null) {
return;
}
try {
messagesConfig.save(messagesFile);
} catch (IOException e) {
getLogger().severe("Could not save messages.yml: " + e.getMessage());
}
}
public void reloadMessagesConfig() {
messagesConfig = YamlConfiguration.loadConfiguration(messagesFile);
}
}
Using Custom Configurations
// Read from custom config
String joinMessage = getMessagesConfig().getString("join-message");
// Write to custom config
getMessagesConfig().set("quit-message", "Goodbye!");
saveMessagesConfig();
// Reload custom config
reloadMessagesConfig();
Saving Resources
Extract files from your plugin JAR to the data folder:
// Save resource from JAR to plugin folder
saveResource("config.yml", false); // Don't replace if exists
saveResource("messages.yml", true); // Replace if exists
saveResource("data/items.yml", false); // Create in subdirectory
From JavaPlugin.java:199-234:
public void saveResource(String resourcePath, boolean replace) {
if (resourcePath == null || resourcePath.equals("")) {
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
}
resourcePath = resourcePath.replace('\\', '/');
InputStream in = getResource(resourcePath);
if (in == null) {
throw new IllegalArgumentException(
"The embedded resource '" + resourcePath + "' cannot be found in " + file);
}
File outFile = new File(dataFolder, resourcePath);
// ... copies the file ...
}
Configuration Patterns
Wrapper Class Pattern
Create a class to manage configuration:
public class Config {
private final FileConfiguration config;
public Config(JavaPlugin plugin) {
plugin.saveDefaultConfig();
this.config = plugin.getConfig();
}
public boolean isFeatureEnabled() {
return config.getBoolean("enable-feature", true);
}
public int getMaxPlayers() {
return config.getInt("max-players", 100);
}
public String getWelcomeMessage() {
return config.getString("welcome-message", "Welcome!");
}
public DatabaseConfig getDatabase() {
ConfigurationSection section = config.getConfigurationSection("database");
if (section == null) {
throw new IllegalStateException("Database configuration missing");
}
return new DatabaseConfig(
section.getString("host"),
section.getInt("port"),
section.getString("name"),
section.getString("username"),
section.getString("password")
);
}
}
record DatabaseConfig(String host, int port, String name,
String username, String password) {}
Usage:
public class MyPlugin extends JavaPlugin {
private Config config;
@Override
public void onEnable() {
this.config = new Config(this);
if (config.isFeatureEnabled()) {
getLogger().info(config.getWelcomeMessage());
}
}
}
Data Folder
The plugin’s data folder is automatically created:
File dataFolder = getDataFolder();
// Returns: plugins/PluginName/
if (!dataFolder.exists()) {
dataFolder.mkdirs(); // Create if doesn't exist
}
// Create subdirectories
File playerDataFolder = new File(dataFolder, "playerdata");
if (!playerDataFolder.exists()) {
playerDataFolder.mkdirs();
}
From JavaPlugin.java:84-86:
public final File getDataFolder() {
return dataFolder;
}
Best Practices
-
Provide defaults:
String value = config.getString("key", "default-value");
-
Validate configuration:
int maxPlayers = config.getInt("max-players", 100);
if (maxPlayers < 1 || maxPlayers > 1000) {
getLogger().warning("Invalid max-players, using default");
maxPlayers = 100;
}
-
Use
saveDefaultConfig() carefully:
// Only creates if doesn't exist - won't overwrite
saveDefaultConfig();
-
Cache configuration values:
// Don't do this repeatedly:
if (getConfig().getBoolean("feature")) { ... }
// Do this instead:
boolean featureEnabled = getConfig().getBoolean("feature");
if (featureEnabled) { ... }
-
Handle missing sections:
ConfigurationSection section = config.getConfigurationSection("database");
if (section == null) {
getLogger().severe("Database configuration missing!");
getServer().getPluginManager().disablePlugin(this);
return;
}
Consider using a configuration wrapper class to provide type-safe access to configuration values and centralize default values.