Merge branch 'master' of https://github.com/AuthMe-Team/AuthMeReloaded into antibot-improvement

Conflicts:
	src/main/java/fr/xephi/authme/service/AntiBotService.java
This commit is contained in:
ljacqu 2016-10-30 08:56:16 +01:00
commit 7a43703d52
73 changed files with 2075 additions and 1163 deletions

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sat Oct 01 23:33:39 CEST 2016. See commands/commands.tpl.md -->
<!-- File auto-generated on Sun Oct 23 18:25:12 CEST 2016. See docs/commands/commands.tpl.md -->
## AuthMe Commands
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`
@ -45,6 +45,8 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
- **/authme version**: Show detailed information about the installed AuthMeReloaded version, the developers, contributors, and license.
- **/authme converter** &lt;job>: Converter command for AuthMeReloaded.
<br />Requires `authme.admin.converter`
- **/authme messages**: Adds missing messages to the current messages file.
<br />Requires `authme.admin.updatemessages`
- **/authme help** [query]: View detailed help for /authme commands.
- **/login** &lt;password>: Command to log in using AuthMeReloaded.
<br />Requires `authme.player.login`
@ -61,7 +63,8 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
- **/changepassword** &lt;oldPassword> &lt;newPassword>: Command to change your password using AuthMeReloaded.
<br />Requires `authme.player.changepassword`
- **/changepassword help** [query]: View detailed help for /changepassword commands.
- **/email**: The AuthMeReloaded Email command base.
- **/email**: The AuthMeReloaded email command base.
- **/email show**: Show your current email address.
- **/email add** &lt;email> &lt;verifyEmail>: Add a new email address to your account.
<br />Requires `authme.player.email.add`
- **/email change** &lt;oldEmail> &lt;newEmail>: Change an email address of your account.
@ -73,7 +76,6 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
<br />Requires `authme.player.captcha`
- **/captcha help** [query]: View detailed help for /captcha commands.
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Oct 01 23:33:39 CEST 2016
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 18:25:12 CEST 2016

View File

@ -1,109 +1,159 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sun Oct 23 21:08:57 CEST 2016. See docs/config/config.tpl.md -->
## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
with which you can configure various settings. This following is the initial contents of
the generated config.yml file.
```yml
DataSource:
# What type of database do you want to use?
# Valid values: sqlite, mysql
backend: sqlite
backend: 'SQLITE'
# Enable database caching, should improve database performance
caching: true
# Database location
mySQLHost: 127.0.0.1
# Database Port
# Database host address
mySQLHost: '127.0.0.1'
# Database port
mySQLPort: '3306'
# Username about Database Connection Infos
mySQLUsername: authme
mySQLUsername: 'authme'
# Password about Database Connection Infos
mySQLPassword: '12345'
# Database Name, use with converters or as SQLITE database name
mySQLDatabase: authme
mySQLDatabase: 'authme'
# Table of the database
mySQLTablename: authme
mySQLTablename: 'authme'
# Column of IDs to sort data
mySQLColumnId: id
mySQLColumnId: 'id'
# Column for storing or checking players nickname
mySQLColumnName: username
mySQLColumnName: 'username'
# Column for storing or checking players RealName
mySQLRealName: 'realname'
# Column for storing players passwords
mySQLColumnPassword: password
mySQLColumnPassword: 'password'
# Column for storing players emails
mySQLColumnEmail: email
# Column for Saving if a player is logged in or not
mySQLColumnLogged: isLogged
# Column for storing players IPs
mySQLColumnIp: ip
mySQLColumnEmail: 'email'
# Column for storing if a player is logged in or not
mySQLColumnLogged: 'isLogged'
# Column for storing players ips
mySQLColumnIp: 'ip'
# Column for storing players lastlogins
mySQLColumnLastLogin: lastlogin
# Column for SaveQuitLocation - X
mySQLlastlocX: x
# Column for SaveQuitLocation - Y
mySQLlastlocY: y
# Column for SaveQuitLocation - Z
mySQLlastlocZ: z
# Column for SaveQuitLocation - World name
mySQLlastlocWorld: world
# Column for RealName
mySQLRealName: realname
mySQLColumnLastLogin: 'lastlogin'
# Column for storing player LastLocation - X
mySQLlastlocX: 'x'
# Column for storing player LastLocation - Y
mySQLlastlocY: 'y'
# Column for storing player LastLocation - Z
mySQLlastlocZ: 'z'
# Column for storing player LastLocation - World Name
mySQLlastlocWorld: 'world'
# Overrides the size of the DB Connection Pool, -1 = Auto
poolSize: -1
ExternalBoardOptions:
# Column for storing players passwords salts
mySQLColumnSalt: ''
# Column for storing players groups
mySQLColumnGroup: ''
# -1 means disabled. If you want that only activated players
# can log into your server, you can set here the group number
# of unactivated users, needed for some forum/CMS support
nonActivedUserGroup: -1
# Other MySQL columns where we need to put the username (case-sensitive)
mySQLOtherUsernameColumns: []
# How much log2 rounds needed in BCrypt (do not change if you do not know what it does)
bCryptLog2Round: 10
# phpBB table prefix defined during the phpBB installation process
phpbbTablePrefix: 'phpbb_'
# phpBB activated group ID; 2 is the default registered group defined by phpBB
phpbbActivatedGroupId: 2
# Wordpress prefix defined during WordPress installation
wordpressTablePrefix: 'wp_'
Converter:
Rakamak:
# Rakamak file name
fileName: 'users.rak'
# Rakamak use IP?
useIP: false
# Rakamak IP file name
ipFileName: 'UsersIp.rak'
CrazyLogin:
# CrazyLogin database file name
fileName: 'accounts.db'
settings:
sessions:
# Do you want to enable the session feature?
# If enabled, when a player authenticates successfully,
# his IP and his nickname is saved.
# The next time the player joins the server, if his IP
# is the same of the last time, and the timeout time
# hasn't expired, he will not need to authenticate.
# is the same as last time and the timeout hasn't
# expired, he will not need to authenticate.
enabled: false
# After how many minutes a session should expire?
# Consider that session will end only after the timeout time, and
# if the player's ip has changed but the timeout hasn't expired,
# player will be kicked out of sever due to invalidSession!
# After how many minutes should a session expire?
# Remember that sessions will end only after the timeout, and
# if the player's IP has changed but the timeout hasn't expired,
# the player will be kicked from the server due to invalid session
timeout: 10
# Should the session expire if the player try to login with an
# another IP Address?
# Should the session expire if the player tries to log in with
# another IP address?
sessionExpireOnIpChange: true
# Message language, available languages:
# https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md
messagesLanguage: 'en'
restrictions:
# Can not authenticated players chat and see the chat log?
# Care that this feature blocks also all the commands not
# Keeps collisions disabled for logged players
# Works only with MC 1.9
keepCollisionsDisabled: false
# Can not authenticated players chat?
# Keep in mind that this feature also blocks all commands not
# listed in the list below.
allowChat: false
# Can not authenticated players see the chat log?
# Hide the chat log from players who are not authenticated?
hideChat: false
# Commands allowed when a player is not authenticated
allowCommands:
- /login
- /register
- /l
- /reg
- /email
- /captcha
# Max number of allowed registrations per IP (default: 1)
# Allowed commands for unauthenticated players
allowCommands:
- '/login'
- '/register'
- '/l'
- '/reg'
- '/email'
- '/captcha'
# Max number of allowed registrations per IP
# The value 0 means an unlimited number of registrations!
maxRegPerIp: 1
# Max allowed username length
# Minimum allowed username length
minNicknameLength: 4
# Maximum allowed username length
maxNicknameLength: 16
# When this setting is enabled, online players can't be kicked out
# due to "Logged in from another Location"
# This setting will prevent potetial security exploits.
# This setting will prevent potential security exploits.
ForceSingleSession: true
ForceSpawnLocOnJoin:
# If enabled, every player will be teleported to the world spawnpoint
# after successful authentication.
# The quit location of the player will be overwritten.
# If enabled, every player that spawn in one of the world listed in
# "ForceSpawnLocOnJoin.worlds" will be teleported to the spawnpoint after successful
# authentication. The quit location of the player will be overwritten.
# This is different from "teleportUnAuthedToSpawn" that teleport player
# back to his quit location after the authentication.
# to the spawnpoint on join.
enabled: false
# WorldNames where we need to force the spawn location
# Case-sensitive!
worlds:
worlds:
- 'world'
- 'world_nether'
- 'world_the_end'
# This option will save the quit location of the players.
SaveQuitLocation: false
# To activate the restricted user feature you need
# to enable this option and configure the
# AllowedRestrctedUser field.
# to enable this option and configure the AllowedRestrictedUser field.
AllowRestrictedUser: false
# The restricted user feature will kick players listed below
# if they don't match of the defined ip address.
# if they don't match the defined IP address.
# Example:
# AllowedRestrictedUser:
# - playername;127.0.0.1
# AllowedRestrictedUser:
# - playername;127.0.0.1
AllowedRestrictedUser: []
# Should unregistered players be kicked immediately?
kickNonRegistered: false
@ -113,23 +163,21 @@ settings:
# After the authentication they will be teleported back to
# their normal position.
teleportUnAuthedToSpawn: false
# Minimum allowed nick length
minNicknameLength: 4
# Can unregistered players walk around?
allowMovement: false
# Should not authenticated players have speed = 0?
# This will reset the fly/walk speed to default value after the login.
removeSpeed: true
# After how many time players who fail to login or register
# should be kicked? Set to 0 to disable.
# After how many seconds should players who fail to login or register
# be kicked? Set to 0 to disable.
timeout: 30
# Regex sintax of allowed characters in the player name.
# Regex syntax of allowed characters in the player name.
allowedNicknameCharacters: '[a-zA-Z0-9_]*'
# How far can unregistered players walk? Set to 0
# for unlimited radius
# How far can unregistered players walk?
# Set to 0 for unlimited radius
allowedMovementRadius: 100
# Enable double check of password when you register
# when it's true, registration require that kind of command:
# when it's true, registration requires that kind of command:
# /register <password> <confirmPassword>
enablePasswordConfirmation: true
# Should we protect the player inventory before logging in? Requires ProtocolLib.
@ -141,64 +189,67 @@ settings:
displayOtherAccounts: true
# Ban ip when the ip is not the ip registered in database
banUnsafedIP: false
# Spawn Priority, Values : authme, essentials, multiverse, default
spawnPriority: authme,essentials,multiverse,default
# Spawn priority; values: authme, essentials, multiverse, default
spawnPriority: 'authme,essentials,multiverse,default'
# Maximum Login authorized by IP
maxLoginPerIp: 0
# Maximum Join authorized by IP
maxJoinPerIp: 0
# AuthMe will NEVER teleport players !
# AuthMe will NEVER teleport players if set to true!
noTeleport: false
# Regex syntax for allowed Chars in passwords.
# Regex syntax for allowed chars in passwords
allowedPasswordCharacters: '[\x21-\x7E]*'
# Keeps collisions disabled for logged players
# Works only with MC 1.9
keepCollisionsDisabled: false
# Log level: INFO, FINE, DEBUG. Use INFO for general messages,
# FINE for some additional detailed ones (like password failed),
# and DEBUG for debugging
logLevel: 'FINE'
# By default we schedule async tasks when talking to the database. If you want
# typical communication with the database to happen synchronously, set this to false
useAsyncTasks: true
GameMode:
# ForceSurvivalMode to player when join ?
# Force survival gamemode when player joins?
ForceSurvivalMode: false
unrestrictions:
# Below you can list all account names that
# AuthMe will ignore for registration or login, configure it
# at your own risk!! Remember that if you are going to add
# nickname with [], you have to delimit name with ' '.
# this option add compatibility with BuildCraft and some
# other mods.
# It is case-sensitive!
UnrestrictedName: []
security:
# Minimum length of password
minPasswordLength: 5
# Maximum length of password
passwordMaxLength: 30
# this is very important options,
# every time player join the server,
# if they are registered, AuthMe will switch him
# to unLoggedInGroup, this
# should prevent all major exploit.
# So you can set up on your Permission Plugin
# this special group with 0 permissions, or permissions to chat,
# or permission to
# send private message or all other perms that you want,
# the better way is to set up
# this group with few permissions,
# so if player try to exploit some account,
# they can
# do anything except what you set in perm Group.
# After a correct logged-in player will be
# moved to his correct permissions group!
# Pay attention group name is case sensitive,
# so Admin is different from admin,
# otherwise your group will be wiped,
# and player join in default group []!
# This is a very important option: every time a player joins the server,
# if they are registered, AuthMe will switch him to unLoggedInGroup.
# This should prevent all major exploits.
# You can set up your permission plugin with this special group to have no permissions,
# or only permission to chat (or permission to send private messages etc.).
# The better way is to set up this group with few permissions, so if a player
# tries to exploit an account they can do only what you've defined for the group.
# After, a logged in player will be moved to his correct permissions group!
# Please note that the group name is case-sensitive, so 'admin' is different from 'Admin'
# Otherwise your group will be wiped and the player will join in the default group []!
# Example unLoggedinGroup: NotLogged
unLoggedinGroup: unLoggedinGroup
# possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB,
# MYBB, IPB3, IPB4, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,
# DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM(for developpers only)
passwordHash: SHA256
# salt length for the SALTED2MD5 MD5(MD5(password)+salt)
unLoggedinGroup: 'unLoggedinGroup'
# Possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB,
# MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512,
# DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only)
passwordHash: 'SHA256'
# Salt length for the SALTED2MD5 MD5(MD5(password)+salt)
doubleMD5SaltLength: 8
# If password checking return false, do we need to check with all
# other password algorithm to check an old password?
# AuthMe will update the password to the new passwordHash!
# AuthMe will update the password to the new password hash
supportOldPasswordHash: false
# Cancel unsafe passwords for being used, put them on lowercase!
#unsafePasswords:
#- '123456'
#- 'password'
unsafePasswords:
# Prevent unsafe passwords from being used; put them in lowercase!
# unsafePasswords:
# - '123456'
# - 'password'
unsafePasswords:
- '123456'
- 'password'
- 'qwerty'
@ -206,7 +257,7 @@ settings:
- '54321'
- '123456789'
registration:
# enable registration on the server?
# Enable registration on the server?
enabled: true
# Send every X seconds a message to a player to
# remind him that he has to login/register
@ -217,40 +268,33 @@ settings:
# Do we replace password registration by an email registration method?
enableEmailRegistrationSystem: false
# Enable double check of email when you register
# when it's true, registration require that kind of command:
# when it's true, registration requires that kind of command:
# /register <email> <confirmEmail>
doubleEmailCheck: false
# Do we force kicking player after a successful registration?
# Do we force kick a player after a successful registration?
# Do not use with login feature below
forceKickAfterRegister: false
# Does AuthMe need to enforce a /login after a successful registration?
forceLoginAfterRegister: false
unrestrictions:
# below you can list all account names that
# AuthMe will ignore for registration or login, configure it
# at your own risk!! Remember that if you are going to add
# nickname with [], you have to delimit name with ' '.
# this option add compatibility with BuildCraft and some
# other mods.
# It is CaseSensitive!
UnrestrictedName: []
# Message language, available : en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt
messagesLanguage: en
# Force these commands after /login, without any '/', use %p for replace with player name
# Force these commands after /login, without any '/', use %p to replace with player name
forceCommands: []
# Force these commands after /login as a server console, without any '/', use %p for replace with player name
# Force these commands after /login as service console, without any '/'.
# Use %p to replace with player name
forceCommandsAsConsole: []
# Force these commands after /register, without any '/', use %p for replace with player name
# Force these commands after /register, without any '/', use %p to replace with player name
forceRegisterCommands: []
# Force these commands after /register as a server console, without any '/', use %p for replace with player name
# Force these commands after /register as a server console, without any '/'.
# Use %p to replace with player name
forceRegisterCommandsAsConsole: []
# Do we need to display the welcome message (welcome.txt) after a login?
# Enable to display the welcome message (welcome.txt) after a login
# You can use colors in this welcome.txt + some replaced strings:
# {PLAYER}: player name, {ONLINE}: display number of online players, {MAXPLAYERS}: display server slots,
# {IP}: player ip, {LOGINS}: number of players logged, {WORLD}: player current world, {SERVER}: server name
# {PLAYER}: player name, {ONLINE}: display number of online players,
# {MAXPLAYERS}: display server slots, {IP}: player ip, {LOGINS}: number of players logged,
# {WORLD}: player current world, {SERVER}: server name
# {VERSION}: get current bukkit version, {COUNTRY}: player country
useWelcomeMessage: true
# Do we need to broadcast the welcome message to all server or only to the player? set true for server or false for player
# Broadcast the welcome message to the server or only to the player?
# set true for server or false for player
broadcastWelcomeMessage: false
# Should we delay the join message and display it once the player has logged in?
delayJoinMessage: false
@ -260,64 +304,115 @@ settings:
removeJoinMessage: false
# Should we remove leave messages altogether?
removeLeaveMessage: false
# Do we need to add potion effect Blinding before login/register?
# Do we need to add potion effect Blinding before login/reigster?
applyBlindEffect: false
# Do we need to prevent people to login with another case?
# If Xephi is registered, then Xephi can login, but not XEPHI/xephi/XePhI
preventOtherCase: false
# Log level: INFO, FINE, DEBUG. Use INFO for general messages,
# FINE for some additional detailed ones (like password failed),
# and DEBUG for debug messages
logLevel: 'FINE'
# By default we schedule async tasks when talking to the database
# If you want typical communication with the database to happen synchronously, set this to false
useAsyncTasks: true
ExternalBoardOptions:
# MySQL column for the salt, needed for some forum/cms support
mySQLColumnSalt: ''
# MySQL column for the group, needed for some forum/cms support
mySQLColumnGroup: ''
# -1 mean disabled. If u want that only
# activated player can login in your server
# u can put in this options the group number
# of unactivated user, needed for some forum/cms support
nonActivedUserGroup: -1
# Other MySQL columns where we need to put the Username (case sensitive)
mySQLOtherUsernameColumns: []
# How much Log to Round needed in BCrypt(do not change it if you do not know what's your doing)
bCryptLog2Round: 10
# phpBB prefix defined during phpbb installation process
phpbbTablePrefix: 'phpbb_'
# phpBB activated group id, 2 is default registered group defined by phpbb
phpbbActivatedGroupId: 2
# WordPress prefix defined during WordPress installation process
wordpressTablePrefix: 'wp_'
permission:
# Take care with this options, if you dont want
# to use Vault and Group Switching of
# AuthMe for unloggedIn players put true
# below, default is false.
# Take care with this option; if you want
# to use group switching of AuthMe
# for unloggedIn players, set this setting to true.
# Default is false.
EnablePermissionCheck: false
BackupSystem:
# Enable or Disable Automatic Backup
ActivateBackup: false
# set Backup at every start of Server
OnServerStart: false
# set Backup at every stop of Server
OnServerStop: true
# Windows only mysql installation Path
MysqlWindowsPath: 'C:\Program Files\MySQL\MySQL Server 5.1\'
Email:
# Email SMTP server host
mailSMTP: 'smtp.gmail.com'
# Email SMTP server port
mailPort: 465
# Email account which sends the mails
mailAccount: ''
# Email account password
mailPassword: ''
# Custom sender name, replacing the mailAccount name in the email
mailSenderName: ''
# Recovery password length
RecoveryPasswordLength: 8
# Mail Subject
mailSubject: 'Your new AuthMe password'
# Like maxRegPerIP but with email
maxRegPerEmail: 1
# Recall players to add an email?
recallPlayers: false
# Delay in minute for the recall scheduler
delayRecall: 5
# Blacklist these domains for emails
emailBlacklisted:
- '10minutemail.com'
# Whitelist ONLY these domains for emails
emailWhitelisted: []
# Send the new password drawn in an image?
generateImage: false
# The OAuth2 token
emailOauth2Token: ''
Hooks:
# Do we need to hook with multiverse for spawn checking?
multiverse: true
# Do we need to hook with BungeeCord?
bungeecord: false
# Send player to this BungeeCord server after register/login
sendPlayerTo: ''
# Do we need to disable Essentials SocialSpy on join?
disableSocialSpy: true
# Do we need to force /motd Essentials command on join?
useEssentialsMotd: false
GroupOptions:
# Unregistered permission group
UnregisteredPlayerGroup: ''
# Registered permission group
RegisteredPlayerGroup: ''
Protection:
# Enable some servers protection (country based login, antibot)
enableProtection: false
# Apply the protection also to registered usernames
enableProtectionRegistered: true
# Countries allowed to join the server and register. For country codes, see
# http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/
# PLEASE USE QUOTES!
countries:
- 'US'
- 'GB'
# Countries not allowed to join the server and register
# PLEASE USE QUOTES!
countriesBlacklist:
- 'A1'
# Do we need to enable automatic antibot system?
enableAntiBot: true
# Max number of players allowed to login in 5 secs
# before the AntiBot system is enabled automatically
antiBotSensibility: 10
# Duration in minutes of the antibot automatic system
antiBotDuration: 10
# Delay in seconds before the antibot activation
antiBotDelay: 60
Purge:
# If enabled, AuthMe automatically purges old, unused accounts
useAutoPurge: false
# Number of days after which an account should be purged
daysBeforeRemovePlayer: 60
# Do we need to remove the player.dat file during purge process?
removePlayerDat: false
# Do we need to remove the Essentials/userdata/player.yml file during purge process?
removeEssentialsFile: false
# World where are players.dat stores
defaultWorld: 'world'
# Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge?
removeLimitedCreativesInventories: false
# Do we need to remove the AntiXRayData/PlayerData/player file during purge process?
removeAntiXRayFile: false
# Do we need to remove permissions?
removePermissions: false
Security:
SQLProblem:
# Stop the server if we can't contact the sql database
# Take care with this, if you set that to false,
# AuthMe automatically disable and the server is not protected!
# Take care with this, if you set this to false,
# AuthMe will automatically disable and the server won't be protected!
stopServer: true
ReloadCommand:
# /reload support
useReloadCommandSupport: true
console:
# Replace passwords in the console when player type a command like /login
# Remove passwords from console?
removePassword: true
# Copy AuthMe log output in a separate file as well?
logConsole: true
@ -344,99 +439,20 @@ Security:
length: 8
# How many hours is a recovery code valid for?
validForHours: 4
Converter:
Rakamak:
# Rakamak file name
fileName: users.rak
# Rakamak use ip ?
useIP: false
# IP file name for rakamak
ipFileName: UsersIp.rak
CrazyLogin:
# CrazyLogin database file
fileName: accounts.db
Email:
# Email SMTP server host
mailSMTP: smtp.gmail.com
# Email SMTP server port
mailPort: 465
# Email account that send the mail
mailAccount: ''
# Email account password
mailPassword: ''
# Custom SenderName, that replace the mailAccount name in the email
mailSenderName: ''
# Random password length
RecoveryPasswordLength: 8
# Email subject of password get
mailSubject: 'Your new AuthMe password'
# Like maxRegPerIp but with email
maxRegPerEmail: 1
# Recall players to add an email?
recallPlayers: false
# Delay in minute for the recall scheduler
delayRecall: 5
# Blacklist these domains for emails
emailBlacklisted:
- 10minutemail.com
# WhiteList only these domains for emails
emailWhitelisted: []
# Do we need to send new password draw in an image?
generateImage: false
# The email OAuth 2 token (leave empty if not used)
emailOauth2Token: ''
Hooks:
# Do we need to hook with multiverse for spawn checking?
multiverse: true
# Do we need to hook with BungeeCord ?
bungeecord: false
# Send player to this BungeeCord server after register/login
sendPlayerTo: ''
# Do we need to disable Essentials SocialSpy on join?
disableSocialSpy: true
# Do we need to force /motd Essentials command on join?
useEssentialsMotd: false
Purge:
# If enabled, AuthMe automatically purges old, unused accounts
useAutoPurge: false
# Number of Days an account become Unused
daysBeforeRemovePlayer: 60
# Do we need to remove the player.dat file during purge process?
removePlayerDat: false
# Do we need to remove the Essentials/userdata/player.yml file during purge process?
removeEssentialsFile: false
# World where are players.dat stores
defaultWorld: 'world'
# Do we need to remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge process ?
removeLimitedCreativesInventories: false
# Do we need to remove the AntiXRayData/PlayerData/player file during purge process?
removeAntiXRayFile: false
# Do we need to remove permissions?
removePermissions: false
Protection:
# Enable some servers protection ( country based login, antibot )
enableProtection: false
# Apply the protection also to registered usernames
enableProtectionRegistered: true
# Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes
# PLEASE USE QUOTES!
countries:
- 'US'
- 'GB'
# Countries blacklisted automatically (without any needed to enable protection)
# PLEASE USE QUOTES!
countriesBlacklist:
- 'A1'
# Do we need to enable automatic antibot system?
enableAntiBot: true
# Max number of player allowed to login in 5 secs before enable AntiBot system automatically
antiBotSensibility: 10
# Duration in minutes of the antibot automatic system
antiBotDuration: 10
# Delay in seconds before the antibot activation
antiBotDelay: 60
GroupOptions:
# Registered permission group
RegisteredPlayerGroup: ''
# Unregistered permission group
UnregisteredPlayerGroup: ''
BackupSystem:
# Enable or disable automatic backup
ActivateBackup: false
# Set backup at every start of server
OnServerStart: false
# Set backup at every stop of server
OnServerStop: true
# Windows only mysql installation Path
MysqlWindowsPath: 'C:\Program Files\MySQL\MySQL Server 5.1\'
```
To change settings on a running server, save your changes to config.yml and use
`/authme reload`.
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 21:08:57 CEST 2016

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sun Oct 02 10:47:16 CEST 2016. See permissions/permission_nodes.tpl.md -->
<!-- File auto-generated on Sun Oct 23 15:38:58 CEST 2016. See permissions/permission_nodes.tpl.md -->
## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds.
@ -26,25 +26,27 @@ The following are the permission nodes that are currently supported by the lates
- **authme.admin.spawn** Administrator command to teleport to the AuthMe spawn.
- **authme.admin.switchantibot** Administrator command to toggle the AntiBot protection status.
- **authme.admin.unregister** Administrator command to unregister an existing user.
- **authme.admin.updatemessages** Permission to use the update messages command.
- **authme.allowmultipleaccounts** Permission to be able to register multiple accounts.
- **authme.bypassantibot** Permission node to bypass AntiBot protection.
- **authme.bypassforcesurvival** Permission for users to bypass force-survival mode.
- **authme.bypasspurge** Permission to bypass the purging process
- **authme.bypasspurge** Permission to bypass the purging process.
- **authme.player.*** Permission to use all player (non-admin) commands.
- **authme.player.canbeforced** Permission for users a login can be forced to.
- **authme.player.captcha** Command permission to use captcha.
- **authme.player.changepassword** Command permission to change the password.
- **authme.player.email** Grants all email permissions.
- **authme.player.email.add** Command permission to add an email address.
- **authme.player.email.change** Command permission to change the email address.
- **authme.player.email.recover** Command permission to recover an account using it's email address.
- **authme.player.email.recover** Command permission to recover an account using its email address.
- **authme.player.login** Command permission to login.
- **authme.player.logout** Command permission to logout.
- **authme.player.register** Command permission to register.
- **authme.player.seeownaccounts** Permission to use to see own other accounts.
- **authme.player.unregister** Command permission to unregister.
- **authme.vip** Permission node to identify VIP users.
- **authme.vip** When the server is full and someone with this permission joins the server, someone will be kicked.
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 02 10:47:16 CEST 2016
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 15:38:58 CEST 2016

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Sun Oct 09 09:42:48 CEST 2016. See translations/translations.tpl.md -->
<!-- File auto-generated on Sun Oct 23 18:25:14 CEST 2016. See docs/translations/translations.tpl.md -->
# AuthMe Translations
The following translations are available in AuthMe. Set `messagesLanguage` to the language code
@ -8,32 +8,32 @@ in your config.yml to use the language, or use another language code to start a
Code | Language | Translated | &nbsp;
---- | -------- | ---------: | ------
[en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 73% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=73&h=5&txtpad=1" alt="bar" />
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 91% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=91&h=5&txtpad=1" alt="bar" />
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 97% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=97&h=5&txtpad=1" alt="bar" />
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 66% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=66&h=5&txtpad=1" alt="bar" />
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 70% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=70&h=5&txtpad=1" alt="bar" />
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 97% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=97&h=5&txtpad=1" alt="bar" />
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 74% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=74&h=5&txtpad=1" alt="bar" />
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 74% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=74&h=5&txtpad=1" alt="bar" />
[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 71% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=71&h=5&txtpad=1" alt="bar" />
[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 97% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=97&h=5&txtpad=1" alt="bar" />
[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 88% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=88&h=5&txtpad=1" alt="bar" />
[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 95% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=95&h=5&txtpad=1" alt="bar" />
[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 97% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=97&h=5&txtpad=1" alt="bar" />
[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 64% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb7700&w=64&h=5&txtpad=1" alt="bar" />
[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 68% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=68&h=5&txtpad=1" alt="bar" />
[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 95% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=95&h=5&txtpad=1" alt="bar" />
[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 72% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=72&h=5&txtpad=1" alt="bar" />
[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 97% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=97&h=5&txtpad=1" alt="bar" />
[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 72% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=72&h=5&txtpad=1" alt="bar" />
[it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ff66&w=100&h=5&txtpad=1" alt="bar" />
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 76% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=76&h=5&txtpad=1" alt="bar" />
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Latvian | 57% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=57&h=5&txtpad=1" alt="bar" />
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 80% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=80&h=5&txtpad=1" alt="bar" />
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 95% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=95&h=5&txtpad=1" alt="bar" />
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 91% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=91&h=5&txtpad=1" alt="bar" />
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 97% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=97&h=5&txtpad=1" alt="bar" />
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 50% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=50&h=5&txtpad=1" alt="bar" />
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 85% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=85&h=5&txtpad=1" alt="bar" />
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 97% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=66ee55&w=97&h=5&txtpad=1" alt="bar" />
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 85% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=85&h=5&txtpad=1" alt="bar" />
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 85% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=85&h=5&txtpad=1" alt="bar" />
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 85% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=85&h=5&txtpad=1" alt="bar" />
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 85% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=99bb22&w=85&h=5&txtpad=1" alt="bar" />
[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 74% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb8800&w=74&h=5&txtpad=1" alt="bar" />
[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Latvian | 55% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb6600&w=55&h=5&txtpad=1" alt="bar" />
[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 78% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=bb9900&w=78&h=5&txtpad=1" alt="bar" />
[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 92% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=92&h=5&txtpad=1" alt="bar" />
[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 88% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=88cc33&w=88&h=5&txtpad=1" alt="bar" />
[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 95% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=95&h=5&txtpad=1" alt="bar" />
[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 49% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aa5500&w=49&h=5&txtpad=1" alt="bar" />
[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 83% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=83&h=5&txtpad=1" alt="bar" />
[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 95% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=77dd44&w=95&h=5&txtpad=1" alt="bar" />
[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 83% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=83&h=5&txtpad=1" alt="bar" />
[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 83% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=83&h=5&txtpad=1" alt="bar" />
[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 83% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=83&h=5&txtpad=1" alt="bar" />
[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 83% | <img src="https://placeholdit.imgix.net/~text?txtsize=5&bg=aaaa11&w=83&h=5&txtpad=1" alt="bar" />
---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 09 09:42:48 CEST 2016
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 18:25:14 CEST 2016

View File

@ -199,6 +199,9 @@ public class AuthMe extends JavaPlugin {
ConsoleLogger.setLogger(getLogger());
ConsoleLogger.setLogFile(new File(getDataFolder(), LOG_FILENAME));
// Create plugin folder
getDataFolder().mkdir();
// Load settings and set up the console and console filter
settings = Initializer.createSettings(this);
bukkitService = new BukkitService(this, settings);

View File

@ -56,16 +56,10 @@ public class CommandDescription {
private PermissionNode permission;
/**
* Private constructor. Use {@link CommandDescription#builder()} to create instances of this class.
* Private constructor.
* <p>
* Note for developers: Instances should be created with {@link CommandDescription#createInstance} to be properly
* Note for developers: Instances should be created with {@link CommandBuilder#register()} to be properly
* registered in the command tree.
*/
private CommandDescription() {
}
/**
* Create an instance.
*
* @param labels command labels
* @param description description of the command
@ -74,30 +68,17 @@ public class CommandDescription {
* @param parent parent command
* @param arguments command arguments
* @param permission permission node required to execute this command
*
* @return the created instance
* @see CommandDescription#builder()
*/
private static CommandDescription createInstance(List<String> labels, String description,
String detailedDescription, Class<? extends ExecutableCommand> executableCommand, CommandDescription parent,
List<CommandArgumentDescription> arguments, PermissionNode permission) {
CommandDescription instance = new CommandDescription();
instance.labels = labels;
instance.description = description;
instance.detailedDescription = detailedDescription;
instance.executableCommand = executableCommand;
instance.parent = parent;
instance.arguments = arguments;
instance.permission = permission;
if (parent != null) {
parent.addChild(instance);
}
return instance;
}
private void addChild(CommandDescription command) {
children.add(command);
private CommandDescription(List<String> labels, String description, String detailedDescription,
Class<? extends ExecutableCommand> executableCommand, CommandDescription parent,
List<CommandArgumentDescription> arguments, PermissionNode permission) {
this.labels = labels;
this.description = description;
this.detailedDescription = detailedDescription;
this.executableCommand = executableCommand;
this.parent = parent;
this.arguments = arguments;
this.permission = permission;
}
/**
@ -224,8 +205,21 @@ public class CommandDescription {
private PermissionNode permission;
/**
* Build a CommandDescription from the builder or throw an exception if a mandatory
* field has not been set.
* Build a CommandDescription and register it onto the parent if available.
*
* @return The generated CommandDescription object
*/
public CommandDescription register() {
CommandDescription command = build();
if (command.parent != null) {
command.parent.children.add(command);
}
return command;
}
/**
* Build a CommandDescription (without registering it on the parent).
*
* @return The generated CommandDescription object
*/
@ -236,8 +230,8 @@ public class CommandDescription {
checkArgument(executableCommand != null, "Executable command must be set");
// parents and permissions may be null; arguments may be empty
return createInstance(labels, description, detailedDescription, executableCommand,
parent, arguments, permission);
return new CommandDescription(labels, description, detailedDescription, executableCommand,
parent, arguments, permission);
}
public CommandBuilder labels(List<String> labels) {

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.command;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableList;
import fr.xephi.authme.command.executable.HelpCommand;
import fr.xephi.authme.command.executable.authme.AccountsCommand;
import fr.xephi.authme.command.executable.authme.AuthMeCommand;
@ -11,6 +11,7 @@ import fr.xephi.authme.command.executable.authme.ForceLoginCommand;
import fr.xephi.authme.command.executable.authme.GetEmailCommand;
import fr.xephi.authme.command.executable.authme.GetIpCommand;
import fr.xephi.authme.command.executable.authme.LastLoginCommand;
import fr.xephi.authme.command.executable.authme.MessagesCommand;
import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand;
import fr.xephi.authme.command.executable.authme.PurgeCommand;
import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand;
@ -40,14 +41,13 @@ import fr.xephi.authme.permission.PlayerPermission;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Initializes all available AuthMe commands.
*/
public class CommandInitializer {
private Set<CommandDescription> commands;
private List<CommandDescription> commands;
public CommandInitializer() {
buildCommands();
@ -58,7 +58,7 @@ public class CommandInitializer {
*
* @return the command descriptions
*/
public Set<CommandDescription> getCommands() {
public List<CommandDescription> getCommands() {
return commands;
}
@ -66,10 +66,10 @@ public class CommandInitializer {
// Register the base AuthMe Reloaded command
final CommandDescription AUTHME_BASE = CommandDescription.builder()
.labels("authme")
.description("Main command")
.description("AuthMe op commands")
.detailedDescription("The main AuthMeReloaded command. The root for all admin commands.")
.executableCommand(AuthMeCommand.class)
.build();
.register();
// Register the register command
CommandDescription.builder()
@ -81,7 +81,7 @@ public class CommandInitializer {
.withArgument("password", "Password", false)
.permission(AdminPermission.REGISTER)
.executableCommand(RegisterAdminCommand.class)
.build();
.register();
// Register the unregister command
CommandDescription.builder()
@ -92,7 +92,7 @@ public class CommandInitializer {
.withArgument("player", "Player name", false)
.permission(AdminPermission.UNREGISTER)
.executableCommand(UnregisterAdminCommand.class)
.build();
.register();
// Register the forcelogin command
CommandDescription.builder()
@ -103,7 +103,7 @@ public class CommandInitializer {
.withArgument("player", "Online player name", true)
.permission(AdminPermission.FORCE_LOGIN)
.executableCommand(ForceLoginCommand.class)
.build();
.register();
// Register the changepassword command
CommandDescription.builder()
@ -115,7 +115,7 @@ public class CommandInitializer {
.withArgument("pwd", "New password", false)
.permission(AdminPermission.CHANGE_PASSWORD)
.executableCommand(ChangePasswordAdminCommand.class)
.build();
.register();
// Register the last login command
CommandDescription.builder()
@ -126,7 +126,7 @@ public class CommandInitializer {
.withArgument("player", "Player name", true)
.permission(AdminPermission.LAST_LOGIN)
.executableCommand(LastLoginCommand.class)
.build();
.register();
// Register the accounts command
CommandDescription.builder()
@ -137,7 +137,7 @@ public class CommandInitializer {
.withArgument("player", "Player name or IP", true)
.permission(AdminPermission.ACCOUNTS)
.executableCommand(AccountsCommand.class)
.build();
.register();
// Register the getemail command
CommandDescription.builder()
@ -148,7 +148,7 @@ public class CommandInitializer {
.withArgument("player", "Player name", true)
.permission(AdminPermission.GET_EMAIL)
.executableCommand(GetEmailCommand.class)
.build();
.register();
// Register the setemail command
CommandDescription.builder()
@ -160,7 +160,7 @@ public class CommandInitializer {
.withArgument("email", "Player email", false)
.permission(AdminPermission.CHANGE_EMAIL)
.executableCommand(SetEmailCommand.class)
.build();
.register();
// Register the getip command
CommandDescription.builder()
@ -171,7 +171,7 @@ public class CommandInitializer {
.withArgument("player", "Player name", false)
.permission(AdminPermission.GET_IP)
.executableCommand(GetIpCommand.class)
.build();
.register();
// Register the spawn command
CommandDescription.builder()
@ -181,7 +181,7 @@ public class CommandInitializer {
.detailedDescription("Teleport to the spawn.")
.permission(AdminPermission.SPAWN)
.executableCommand(SpawnCommand.class)
.build();
.register();
// Register the setspawn command
CommandDescription.builder()
@ -191,7 +191,7 @@ public class CommandInitializer {
.detailedDescription("Change the player's spawn to your current position.")
.permission(AdminPermission.SET_SPAWN)
.executableCommand(SetSpawnCommand.class)
.build();
.register();
// Register the firstspawn command
CommandDescription.builder()
@ -201,7 +201,7 @@ public class CommandInitializer {
.detailedDescription("Teleport to the first spawn.")
.permission(AdminPermission.FIRST_SPAWN)
.executableCommand(FirstSpawnCommand.class)
.build();
.register();
// Register the setfirstspawn command
CommandDescription.builder()
@ -211,7 +211,7 @@ public class CommandInitializer {
.detailedDescription("Change the first player's spawn to your current position.")
.permission(AdminPermission.SET_FIRST_SPAWN)
.executableCommand(SetFirstSpawnCommand.class)
.build();
.register();
// Register the purge command
CommandDescription.builder()
@ -223,7 +223,7 @@ public class CommandInitializer {
.withArgument("all", "Add 'all' at the end to also purge players with lastlogin = 0", true)
.permission(AdminPermission.PURGE)
.executableCommand(PurgeCommand.class)
.build();
.register();
// Register the purgelastposition command
CommandDescription.builder()
@ -235,7 +235,7 @@ public class CommandInitializer {
.withArgument("player/*", "Player name or * for all players", false)
.permission(AdminPermission.PURGE_LAST_POSITION)
.executableCommand(PurgeLastPositionCommand.class)
.build();
.register();
// Register the purgebannedplayers command
CommandDescription.builder()
@ -245,7 +245,7 @@ public class CommandInitializer {
.detailedDescription("Purge all AuthMeReloaded data for banned players.")
.permission(AdminPermission.PURGE_BANNED_PLAYERS)
.executableCommand(PurgeBannedPlayersCommand.class)
.build();
.register();
// Register the switchantibot command
CommandDescription.builder()
@ -256,7 +256,7 @@ public class CommandInitializer {
.withArgument("mode", "ON / OFF", true)
.permission(AdminPermission.SWITCH_ANTIBOT)
.executableCommand(SwitchAntiBotCommand.class)
.build();
.register();
// Register the reload command
CommandDescription.builder()
@ -266,7 +266,7 @@ public class CommandInitializer {
.detailedDescription("Reload the AuthMeReloaded plugin.")
.permission(AdminPermission.RELOAD)
.executableCommand(ReloadCommand.class)
.build();
.register();
// Register the version command
CommandDescription.builder()
@ -276,7 +276,7 @@ public class CommandInitializer {
.detailedDescription("Show detailed information about the installed AuthMeReloaded version, the "
+ "developers, contributors, and license.")
.executableCommand(VersionCommand.class)
.build();
.register();
CommandDescription.builder()
.parent(AUTHME_BASE)
@ -287,7 +287,16 @@ public class CommandInitializer {
"royalauth / vauth / sqliteToSql / mysqlToSqlite", false)
.permission(AdminPermission.CONVERTER)
.executableCommand(ConverterCommand.class)
.build();
.register();
CommandDescription.builder()
.parent(AUTHME_BASE)
.labels("messages", "msg")
.description("Add missing messages")
.detailedDescription("Adds missing messages to the current messages file.")
.permission(AdminPermission.UPDATE_MESSAGES)
.executableCommand(MessagesCommand.class)
.register();
// Register the base login command
final CommandDescription LOGIN_BASE = CommandDescription.builder()
@ -298,7 +307,7 @@ public class CommandInitializer {
.withArgument("password", "Login password", false)
.permission(PlayerPermission.LOGIN)
.executableCommand(LoginCommand.class)
.build();
.register();
// Register the base logout command
CommandDescription LOGOUT_BASE = CommandDescription.builder()
@ -308,51 +317,51 @@ public class CommandInitializer {
.detailedDescription("Command to logout using AuthMeReloaded.")
.permission(PlayerPermission.LOGOUT)
.executableCommand(LogoutCommand.class)
.build();
.register();
// Register the base register command
final CommandDescription REGISTER_BASE = CommandDescription.builder()
.parent(null)
.labels("register", "reg")
.description("Registration command")
.description("Register an account")
.detailedDescription("Command to register using AuthMeReloaded.")
.withArgument("password", "Password", true)
.withArgument("verifyPassword", "Verify password", true)
.permission(PlayerPermission.REGISTER)
.executableCommand(RegisterCommand.class)
.build();
.register();
// Register the base unregister command
CommandDescription UNREGISTER_BASE = CommandDescription.builder()
.parent(null)
.labels("unregister", "unreg")
.description("Unregistration Command")
.description("Unregister an account")
.detailedDescription("Command to unregister using AuthMeReloaded.")
.withArgument("password", "Password", false)
.permission(PlayerPermission.UNREGISTER)
.executableCommand(UnregisterCommand.class)
.build();
.register();
// Register the base changepassword command
final CommandDescription CHANGE_PASSWORD_BASE = CommandDescription.builder()
.parent(null)
.labels("changepassword", "changepass", "cp")
.description("Change password Command")
.description("Change password of an account")
.detailedDescription("Command to change your password using AuthMeReloaded.")
.withArgument("oldPassword", "Old Password", false)
.withArgument("newPassword", "New Password.", false)
.withArgument("oldPassword", "Old password", false)
.withArgument("newPassword", "New password", false)
.permission(PlayerPermission.CHANGE_PASSWORD)
.executableCommand(ChangePasswordCommand.class)
.build();
.register();
// Register the base Email command
CommandDescription EMAIL_BASE = CommandDescription.builder()
.parent(null)
.labels("email")
.description("Email command")
.detailedDescription("The AuthMeReloaded Email command base.")
.description("Add email or recover password")
.detailedDescription("The AuthMeReloaded email command base.")
.executableCommand(EmailBaseCommand.class)
.build();
.register();
// Register the show command
CommandDescription.builder()
@ -361,7 +370,7 @@ public class CommandInitializer {
.description("Show Email")
.detailedDescription("Show your current email address.")
.executableCommand(ShowEmailCommand.class)
.build();
.register();
// Register the add command
CommandDescription.builder()
@ -373,7 +382,7 @@ public class CommandInitializer {
.withArgument("verifyEmail", "Email address verification", false)
.permission(PlayerPermission.ADD_EMAIL)
.executableCommand(AddEmailCommand.class)
.build();
.register();
// Register the change command
CommandDescription.builder()
@ -385,20 +394,20 @@ public class CommandInitializer {
.withArgument("newEmail", "New email address", false)
.permission(PlayerPermission.CHANGE_EMAIL)
.executableCommand(ChangeEmailCommand.class)
.build();
.register();
// Register the recover command
CommandDescription.builder()
.parent(EMAIL_BASE)
.labels("recover", "recovery", "recoveremail", "recovermail")
.description("Recover password using Email")
.description("Recover password using email")
.detailedDescription("Recover your account using an Email address by sending a mail containing " +
"a new password.")
.withArgument("email", "Email address", false)
.withArgument("code", "Recovery code", true)
.permission(PlayerPermission.RECOVER_EMAIL)
.executableCommand(RecoverEmailCommand.class)
.build();
.register();
// Register the base captcha command
CommandDescription CAPTCHA_BASE = CommandDescription.builder()
@ -409,9 +418,9 @@ public class CommandInitializer {
.withArgument("captcha", "The Captcha", false)
.permission(PlayerPermission.CAPTCHA)
.executableCommand(CaptchaCommand.class)
.build();
.register();
Set<CommandDescription> baseCommands = ImmutableSet.of(
List<CommandDescription> baseCommands = ImmutableList.of(
AUTHME_BASE,
LOGIN_BASE,
LOGOUT_BASE,
@ -441,7 +450,7 @@ public class CommandInitializer {
.detailedDescription("View detailed help for /" + base.getLabels().get(0) + " commands.")
.withArgument("query", "The command or query to view help for.", true)
.executableCommand(HelpCommand.class)
.build();
.register();
}
}
}

View File

@ -8,6 +8,7 @@ import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -26,7 +27,7 @@ public class CommandMapper {
*/
private static final Class<? extends ExecutableCommand> HELP_COMMAND_CLASS = HelpCommand.class;
private final Set<CommandDescription> baseCommands;
private final Collection<CommandDescription> baseCommands;
private final PermissionsManager permissionsManager;
@Inject

View File

@ -1,9 +1,11 @@
package fr.xephi.authme.command;
import com.google.common.collect.Lists;
import org.bukkit.ChatColor;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public final class CommandUtils {
@ -34,6 +36,14 @@ public final class CommandUtils {
return sb.toString();
}
/**
* Constructs a hierarchical list of commands for the given command. The commands are in order:
* the parents of the given command precede the provided command. For example, given the command
* for {@code /authme register}, a list with {@code [{authme}, {authme register}]} is returned.
*
* @param command the command to build a parent list for
* @return the parent list
*/
public static List<CommandDescription> constructParentList(CommandDescription command) {
List<CommandDescription> commands = new ArrayList<>();
CommandDescription currentCommand = command;
@ -43,4 +53,35 @@ public final class CommandUtils {
}
return Lists.reverse(commands);
}
public static String buildSyntax(CommandDescription command) {
String arguments = command.getArguments().stream()
.map(arg -> formatArgument(arg))
.collect(Collectors.joining(" "));
return (constructCommandPath(command) + " " + arguments).trim();
}
public static String buildSyntax(CommandDescription command, List<String> correctLabels) {
String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW;
for (int i = 1; i < correctLabels.size(); ++i) {
commandSyntax += " " + correctLabels.get(i);
}
for (CommandArgumentDescription argument : command.getArguments()) {
commandSyntax += " " + formatArgument(argument);
}
return commandSyntax;
}
/**
* Format a command argument with the proper type of brackets.
*
* @param argument the argument to format
* @return the formatted argument
*/
public static String formatArgument(CommandArgumentDescription argument) {
if (argument.isOptional()) {
return "[" + argument.getName() + "]";
}
return "<" + argument.getName() + ">";
}
}

View File

@ -14,6 +14,11 @@ import java.util.List;
import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND;
import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL;
import static fr.xephi.authme.command.help.HelpProvider.ALL_OPTIONS;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_COMMAND;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_DESCRIPTION;
public class HelpCommand implements ExecutableCommand {
@ -46,9 +51,9 @@ public class HelpCommand implements ExecutableCommand {
int mappedCommandLevel = result.getCommandDescription().getLabelCount();
if (mappedCommandLevel == 1) {
helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN);
helpProvider.outputHelp(sender, result, SHOW_COMMAND | SHOW_DESCRIPTION | SHOW_CHILDREN | SHOW_ALTERNATIVES);
} else {
helpProvider.outputHelp(sender, result, HelpProvider.ALL_OPTIONS);
helpProvider.outputHelp(sender, result, ALL_OPTIONS);
}
}

View File

@ -0,0 +1,54 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.service.MessageUpdater;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.io.File;
import java.util.List;
/**
* Messages command, updates the user's messages file with any missing files
* from the provided file in the JAR.
*/
public class MessagesCommand implements ExecutableCommand {
private static final String DEFAULT_LANGUAGE = "en";
@Inject
private Settings settings;
@Inject
@DataFolder
private File dataFolder;
@Inject
private Messages messages;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
final String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE);
try {
boolean isFileUpdated = new MessageUpdater(
new File(dataFolder, getMessagePath(language)),
getMessagePath(language),
getMessagePath(DEFAULT_LANGUAGE))
.executeCopy(sender);
if (isFileUpdated) {
messages.reload();
}
} catch (Exception e) {
sender.sendMessage("Could not update messages: " + e.getMessage());
ConsoleLogger.logException("Could not update messages:", e);
}
}
private static String getMessagePath(String code) {
return "messages/messages_" + code + ".yml";
}
}

View File

@ -1,36 +0,0 @@
package fr.xephi.authme.command.help;
import fr.xephi.authme.command.CommandArgumentDescription;
import fr.xephi.authme.command.CommandDescription;
import org.bukkit.ChatColor;
import java.util.List;
/**
* Helper class for displaying the syntax of a command properly to a user.
*/
final class CommandSyntaxHelper {
private CommandSyntaxHelper() {
}
public static String getSyntax(CommandDescription command, List<String> correctLabels) {
String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW;
for (int i = 1; i < correctLabels.size(); ++i) {
commandSyntax += " " + correctLabels.get(i);
}
for (CommandArgumentDescription argument : command.getArguments()) {
commandSyntax += " " + formatArgument(argument);
}
return commandSyntax;
}
/** Format a command argument with the proper type of brackets. */
private static String formatArgument(CommandArgumentDescription argument) {
if (argument.isOptional()) {
return "[" + argument.getName() + "]";
}
return "<" + argument.getName() + ">";
}
}

View File

@ -67,6 +67,10 @@ public class HelpMessagesService implements Reloadable {
return localCommand;
}
public String getDescription(CommandDescription command) {
return getText(getCommandPath(command) + DESCRIPTION_SUFFIX, command::getDescription);
}
public String getMessage(HelpMessage message) {
return messageFileHandler.getMessage(message.getKey());
}

View File

@ -15,11 +15,12 @@ import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import static fr.xephi.authme.command.help.HelpSection.DETAILED_DESCRIPTION;
import static fr.xephi.authme.command.help.HelpSection.SHORT_DESCRIPTION;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
/**
@ -79,7 +80,7 @@ public class HelpProvider implements Reloadable {
if (hasFlag(SHOW_COMMAND, options)) {
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.COMMAND) + ": "
+ CommandSyntaxHelper.getSyntax(command, correctLabels));
+ CommandUtils.buildSyntax(command, correctLabels));
}
if (hasFlag(SHOW_DESCRIPTION, options)) {
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(SHORT_DESCRIPTION) + ": "
@ -171,19 +172,29 @@ public class HelpProvider implements Reloadable {
}
private void printAlternatives(CommandDescription command, List<String> correctLabels, List<String> lines) {
if (command.getLabels().size() <= 1 || correctLabels.size() <= 1) {
if (command.getLabels().size() <= 1) {
return;
}
lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.ALTERNATIVES) + ":");
// Get the label used
final String parentLabel = correctLabels.get(0);
final String childLabel = correctLabels.get(1);
// Label with which the command was called -> don't show it as an alternative
final String usedLabel;
// Takes alternative label and constructs list of labels, e.g. "reg" -> [authme, reg]
final Function<String, List<String>> commandLabelsFn;
if (correctLabels.size() == 1) {
usedLabel = correctLabels.get(0);
commandLabelsFn = label -> singletonList(label);
} else {
usedLabel = correctLabels.get(1);
commandLabelsFn = label -> Arrays.asList(correctLabels.get(0), label);
}
// Create a list of alternatives
for (String entry : command.getLabels()) {
if (!entry.equalsIgnoreCase(childLabel)) {
lines.add(" " + CommandSyntaxHelper.getSyntax(command, asList(parentLabel, entry)));
for (String label : command.getLabels()) {
if (!label.equalsIgnoreCase(usedLabel)) {
lines.add(" " + CommandUtils.buildSyntax(command, commandLabelsFn.apply(label)));
}
}
}
@ -238,7 +249,7 @@ public class HelpProvider implements Reloadable {
String parentCommandPath = String.join(" ", parentLabels);
for (CommandDescription child : command.getChildren()) {
lines.add(" /" + parentCommandPath + " " + child.getLabels().get(0)
+ ChatColor.GRAY + ChatColor.ITALIC + ": " + child.getDescription());
+ ChatColor.GRAY + ChatColor.ITALIC + ": " + helpMessagesService.getDescription(child));
}
}

View File

@ -40,6 +40,7 @@ public class MySQL implements DataSource {
private String password;
private String database;
private String tableName;
private int poolSize;
private List<String> columnOthers;
private Columns col;
private HashAlgorithm hashAlgorithm;
@ -98,6 +99,10 @@ public class MySQL implements DataSource {
this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX);
this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID);
this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX);
this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE);
if(poolSize == -1) {
poolSize = RuntimeUtils.getCoreCount();
}
}
private void setConnectionArguments() throws RuntimeException {
@ -105,7 +110,7 @@ public class MySQL implements DataSource {
ds.setPoolName("AuthMeMYSQLPool");
// Pool size
ds.setMaximumPoolSize(RuntimeUtils.getCoreCount());
ds.setMaximumPoolSize(poolSize);
// Database URL
ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database);

View File

@ -62,13 +62,13 @@ public class Initializer {
*/
public static Settings createSettings(AuthMe authMe) throws Exception {
File configFile = new File(authMe.getDataFolder(), "config.yml");
if (FileUtils.copyFileFromResource(configFile, "config.yml")) {
PropertyResource resource = new YamlFileResource(configFile);
SettingsMigrationService migrationService = new SettingsMigrationService(authMe.getDataFolder());
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
return new Settings(authMe.getDataFolder(), resource, migrationService, configurationData);
if(!configFile.exists()) {
configFile.createNewFile();
}
throw new Exception("Could not copy config.yml from JAR to plugin folder");
PropertyResource resource = new YamlFileResource(configFile);
SettingsMigrationService migrationService = new SettingsMigrationService(authMe.getDataFolder());
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
return new Settings(authMe.getDataFolder(), resource, migrationService, configurationData);
}
/**

View File

@ -8,126 +8,125 @@ public enum AdminPermission implements PermissionNode {
/**
* Administrator command to register a new user.
*/
REGISTER("authme.admin.register", DefaultPermission.OP_ONLY),
REGISTER("authme.admin.register"),
/**
* Administrator command to unregister an existing user.
*/
UNREGISTER("authme.admin.unregister", DefaultPermission.OP_ONLY),
UNREGISTER("authme.admin.unregister"),
/**
* Administrator command to force-login an existing user.
*/
FORCE_LOGIN("authme.admin.forcelogin", DefaultPermission.OP_ONLY),
FORCE_LOGIN("authme.admin.forcelogin"),
/**
* Administrator command to change the password of a user.
*/
CHANGE_PASSWORD("authme.admin.changepassword", DefaultPermission.OP_ONLY),
CHANGE_PASSWORD("authme.admin.changepassword"),
/**
* Administrator command to see the last login date and time of a user.
*/
LAST_LOGIN("authme.admin.lastlogin", DefaultPermission.OP_ONLY),
LAST_LOGIN("authme.admin.lastlogin"),
/**
* Administrator command to see all accounts associated with a user.
*/
ACCOUNTS("authme.admin.accounts", DefaultPermission.OP_ONLY),
ACCOUNTS("authme.admin.accounts"),
/**
* Administrator command to get the email address of a user, if set.
*/
GET_EMAIL("authme.admin.getemail", DefaultPermission.OP_ONLY),
GET_EMAIL("authme.admin.getemail"),
/**
* Administrator command to set or change the email address of a user.
*/
CHANGE_EMAIL("authme.admin.changemail", DefaultPermission.OP_ONLY),
CHANGE_EMAIL("authme.admin.changemail"),
/**
* Administrator command to get the last known IP of a user.
*/
GET_IP("authme.admin.getip", DefaultPermission.OP_ONLY),
GET_IP("authme.admin.getip"),
/**
* Administrator command to teleport to the AuthMe spawn.
*/
SPAWN("authme.admin.spawn", DefaultPermission.OP_ONLY),
SPAWN("authme.admin.spawn"),
/**
* Administrator command to set the AuthMe spawn.
*/
SET_SPAWN("authme.admin.setspawn", DefaultPermission.OP_ONLY),
SET_SPAWN("authme.admin.setspawn"),
/**
* Administrator command to teleport to the first AuthMe spawn.
*/
FIRST_SPAWN("authme.admin.firstspawn", DefaultPermission.OP_ONLY),
FIRST_SPAWN("authme.admin.firstspawn"),
/**
* Administrator command to set the first AuthMe spawn.
*/
SET_FIRST_SPAWN("authme.admin.setfirstspawn", DefaultPermission.OP_ONLY),
SET_FIRST_SPAWN("authme.admin.setfirstspawn"),
/**
* Administrator command to purge old user data.
*/
PURGE("authme.admin.purge", DefaultPermission.OP_ONLY),
PURGE("authme.admin.purge"),
/**
* Administrator command to purge the last position of a user.
*/
PURGE_LAST_POSITION("authme.admin.purgelastpos", DefaultPermission.OP_ONLY),
PURGE_LAST_POSITION("authme.admin.purgelastpos"),
/**
* Administrator command to purge all data associated with banned players.
*/
PURGE_BANNED_PLAYERS("authme.admin.purgebannedplayers", DefaultPermission.OP_ONLY),
PURGE_BANNED_PLAYERS("authme.admin.purgebannedplayers"),
/**
* Administrator command to toggle the AntiBot protection status.
*/
SWITCH_ANTIBOT("authme.admin.switchantibot", DefaultPermission.OP_ONLY),
SWITCH_ANTIBOT("authme.admin.switchantibot"),
/**
* Administrator command to convert old or other data to AuthMe data.
*/
CONVERTER("authme.admin.converter", DefaultPermission.OP_ONLY),
CONVERTER("authme.admin.converter"),
/**
* Administrator command to reload the plugin configuration.
*/
RELOAD("authme.admin.reload", DefaultPermission.OP_ONLY),
RELOAD("authme.admin.reload"),
/**
* Permission to see Antibot messages.
*/
ANTIBOT_MESSAGES("authme.admin.antibotmessages", DefaultPermission.OP_ONLY),
ANTIBOT_MESSAGES("authme.admin.antibotmessages"),
/**
* Permission to use the update messages command.
*/
UPDATE_MESSAGES("authme.admin.updatemessages"),
/**
* Permission to see the other accounts of the players that log in.
*/
SEE_OTHER_ACCOUNTS("authme.admin.seeotheraccounts", DefaultPermission.OP_ONLY);
SEE_OTHER_ACCOUNTS("authme.admin.seeotheraccounts");
/**
* The permission node.
*/
private String node;
/**
* The default permission level
*/
private DefaultPermission defaultPermission;
/**
* Constructor.
*
* @param node Permission node.
*/
AdminPermission(String node, DefaultPermission defaultPermission) {
AdminPermission(String node) {
this.node = node;
this.defaultPermission = defaultPermission;
}
@Override
@ -137,6 +136,6 @@ public enum AdminPermission implements PermissionNode {
@Override
public DefaultPermission getDefaultPermission() {
return defaultPermission;
return DefaultPermission.OP_ONLY;
}
}

View File

@ -8,76 +8,70 @@ public enum PlayerPermission implements PermissionNode {
/**
* Command permission to login.
*/
LOGIN("authme.player.login", DefaultPermission.ALLOWED),
LOGIN("authme.player.login"),
/**
* Command permission to logout.
*/
LOGOUT("authme.player.logout", DefaultPermission.ALLOWED),
LOGOUT("authme.player.logout"),
/**
* Command permission to register.
*/
REGISTER("authme.player.register", DefaultPermission.ALLOWED),
REGISTER("authme.player.register"),
/**
* Command permission to unregister.
*/
UNREGISTER("authme.player.unregister", DefaultPermission.ALLOWED),
UNREGISTER("authme.player.unregister"),
/**
* Command permission to change the password.
*/
CHANGE_PASSWORD("authme.player.changepassword", DefaultPermission.ALLOWED),
CHANGE_PASSWORD("authme.player.changepassword"),
/**
* Command permission to add an email address.
*/
ADD_EMAIL("authme.player.email.add", DefaultPermission.ALLOWED),
ADD_EMAIL("authme.player.email.add"),
/**
* Command permission to change the email address.
*/
CHANGE_EMAIL("authme.player.email.change", DefaultPermission.ALLOWED),
CHANGE_EMAIL("authme.player.email.change"),
/**
* Command permission to recover an account using it's email address.
* Command permission to recover an account using its email address.
*/
RECOVER_EMAIL("authme.player.email.recover", DefaultPermission.ALLOWED),
RECOVER_EMAIL("authme.player.email.recover"),
/**
* Command permission to use captcha.
*/
CAPTCHA("authme.player.captcha", DefaultPermission.ALLOWED),
CAPTCHA("authme.player.captcha"),
/**
* Permission for users a login can be forced to.
*/
CAN_LOGIN_BE_FORCED("authme.player.canbeforced", DefaultPermission.ALLOWED),
CAN_LOGIN_BE_FORCED("authme.player.canbeforced"),
/**
* Permission to use to see own other accounts.
*/
SEE_OWN_ACCOUNTS("authme.player.seeownaccounts", DefaultPermission.ALLOWED);
SEE_OWN_ACCOUNTS("authme.player.seeownaccounts");
/**
* The permission node.
*/
private String node;
/**
* The default permission level
*/
private DefaultPermission defaultPermission;
/**
* Constructor.
*
* @param node Permission node.
*/
PlayerPermission(String node, DefaultPermission defaultPermission) {
PlayerPermission(String node) {
this.node = node;
this.defaultPermission = defaultPermission;
}
@Override
@ -87,7 +81,7 @@ public enum PlayerPermission implements PermissionNode {
@Override
public DefaultPermission getDefaultPermission() {
return defaultPermission;
return DefaultPermission.ALLOWED;
}
}

View File

@ -17,7 +17,7 @@ public enum PlayerStatePermission implements PermissionNode {
BYPASS_FORCE_SURVIVAL("authme.bypassforcesurvival", DefaultPermission.OP_ONLY),
/**
* Permission node to identify VIP users.
* When the server is full and someone with this permission joins the server, someone will be kicked.
*/
IS_VIP("authme.vip", DefaultPermission.OP_ONLY),
@ -27,7 +27,7 @@ public enum PlayerStatePermission implements PermissionNode {
ALLOW_MULTIPLE_ACCOUNTS("authme.allowmultipleaccounts", DefaultPermission.OP_ONLY),
/**
* Permission to bypass the purging process
* Permission to bypass the purging process.
*/
BYPASS_PURGE("authme.bypasspurge", DefaultPermission.NOT_ALLOWED);
@ -44,7 +44,8 @@ public enum PlayerStatePermission implements PermissionNode {
/**
* Constructor.
*
* @param node Permission node.
* @param node Permission node
* @param defaultPermission The default permission
*/
PlayerStatePermission(String node, DefaultPermission defaultPermission) {
this.node = node;

View File

@ -0,0 +1,139 @@
package fr.xephi.authme.service;
import com.github.authme.configme.SettingsManager;
import com.github.authme.configme.knownproperties.ConfigurationData;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.properties.StringProperty;
import com.github.authme.configme.resource.YamlFileResource;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* Updates a user's messages file with messages from the JAR files.
*/
public class MessageUpdater {
private final FileConfiguration userConfiguration;
private final FileConfiguration localJarConfiguration;
private final FileConfiguration defaultJarConfiguration;
private final List<Property<String>> properties;
private final SettingsManager settingsManager;
private boolean hasMissingMessages = false;
/**
* Constructor.
*
* @param userFile messages file in the data folder
* @param localJarFile path to messages file in JAR in local language
* @param defaultJarFile path to messages file in JAR for default language
* @throws Exception if userFile does not exist or no JAR messages file can be loaded
*/
public MessageUpdater(File userFile, String localJarFile, String defaultJarFile) throws Exception {
if (!userFile.exists()) {
throw new Exception("Local messages file does not exist");
}
userConfiguration = YamlConfiguration.loadConfiguration(userFile);
localJarConfiguration = loadJarFileOrSendError(localJarFile);
defaultJarConfiguration = localJarFile.equals(defaultJarFile) ? null : loadJarFileOrSendError(defaultJarFile);
if (localJarConfiguration == null && defaultJarConfiguration == null) {
throw new Exception("Could not load any JAR messages file to copy from");
}
properties = buildPropertyEntriesForMessageKeys();
settingsManager = new SettingsManager(
new YamlFileResource(userFile), (r, p) -> true, new ConfigurationData((List) properties));
}
/**
* Copies missing messages to the messages file.
*
* @param sender sender starting the copy process
* @return true if the messages file was updated, false otherwise
* @throws Exception if an error occurs during saving
*/
public boolean executeCopy(CommandSender sender) throws Exception {
copyMissingMessages();
if (!hasMissingMessages) {
sender.sendMessage("No new messages to add");
return false;
}
// Save user configuration file
try {
settingsManager.save();
sender.sendMessage("Message file updated with new messages");
return true;
} catch (Exception e) {
throw new Exception("Could not save to messages file: " + StringUtils.formatException(e));
}
}
@SuppressWarnings("unchecked")
private void copyMissingMessages() {
for (Property<String> property : properties) {
String message = userConfiguration.getString(property.getPath());
if (message == null) {
hasMissingMessages = true;
message = getMessageFromJar(property.getPath());
}
settingsManager.setProperty(property, message);
}
}
private String getMessageFromJar(String key) {
String message = (localJarConfiguration == null ? null : localJarConfiguration.getString(key));
if (message != null) {
return message;
}
return (defaultJarConfiguration == null) ? null : defaultJarConfiguration.getString(key);
}
private static FileConfiguration loadJarFileOrSendError(String jarPath) {
try (InputStream stream = FileUtils.getResourceFromJar(jarPath)) {
if (stream == null) {
ConsoleLogger.info("Could not load '" + jarPath + "' from JAR");
return null;
}
InputStreamReader isr = new InputStreamReader(stream);
FileConfiguration configuration = YamlConfiguration.loadConfiguration(isr);
close(isr);
return configuration;
} catch (IOException e) {
ConsoleLogger.logException("Exception while handling JAR path '" + jarPath + "'", e);
}
return null;
}
private static List<Property<String>> buildPropertyEntriesForMessageKeys() {
return Arrays.stream(MessageKey.values())
.map(key -> new StringProperty(key.getKey(), ""))
.collect(Collectors.toList());
}
private static void close(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
ConsoleLogger.info("Cannot close '" + closeable + "': " + StringUtils.formatException(e));
}
}
}
}

View File

@ -98,6 +98,10 @@ public class DatabaseSettings implements SettingsHolder {
public static final Property<String> MYSQL_COL_GROUP =
newProperty("ExternalBoardOptions.mySQLColumnGroup", "");
@Comment("Overrides the size of the DB Connection Pool, -1 = Auto")
public static final Property<Integer> MYSQL_POOL_SIZE =
newProperty("DataSource.poolSize", -1);
private DatabaseSettings() {
}

View File

@ -36,7 +36,10 @@ public class PluginSettings implements SettingsHolder {
public static final Property<Boolean> SESSIONS_EXPIRE_ON_IP_CHANGE =
newProperty("settings.sessions.sessionExpireOnIpChange", true);
@Comment("Message language, available: en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt")
@Comment({
"Message language, available languages:",
"https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md"
})
public static final Property<String> MESSAGES_LANGUAGE =
newProperty("settings.messagesLanguage", "en");
@ -65,8 +68,8 @@ public class PluginSettings implements SettingsHolder {
newProperty(LogLevel.class, "settings.logLevel", LogLevel.FINE);
@Comment({
"By default we schedule async tasks when talking to the database",
"If you want typical communication with the database to happen synchronously, set this to false"
"By default we schedule async tasks when talking to the database. If you want",
"typical communication with the database to happen synchronously, set this to false"
})
public static final Property<Boolean> USE_ASYNC_TASKS =
newProperty("settings.useAsyncTasks", true);

View File

@ -20,13 +20,16 @@ public class ProtectionSettings implements SettingsHolder {
public static final Property<Boolean> ENABLE_PROTECTION_REGISTERED =
newProperty("Protection.enableProtectionRegistered", true);
@Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes",
"PLEASE USE QUOTES!"})
@Comment({
"Countries allowed to join the server and register. For country codes, see",
"http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/",
"PLEASE USE QUOTES!"})
public static final Property<List<String>> COUNTRIES_WHITELIST =
newListProperty("Protection.countries", "US", "GB");
@Comment({"Countries not allowed to join the server and register",
"PLEASE USE QUOTES!"})
@Comment({
"Countries not allowed to join the server and register",
"PLEASE USE QUOTES!"})
public static final Property<List<String>> COUNTRIES_BLACKLIST =
newListProperty("Protection.countriesBlacklist", "A1");
@ -34,7 +37,13 @@ public class ProtectionSettings implements SettingsHolder {
public static final Property<Boolean> ENABLE_ANTIBOT =
newProperty("Protection.enableAntiBot", true);
@Comment("Max number of players allowed to login in 5 secs before the AntiBot system is enabled automatically")
@Comment("The interval in seconds")
public static final Property<Integer> ANTIBOT_INTERVAL =
newProperty("Protection.antiBotInterval", 5);
@Comment({
"Max number of players allowed to login in the interval",
"before the AntiBot system is enabled automatically"})
public static final Property<Integer> ANTIBOT_SENSIBILITY =
newProperty("Protection.antiBotSensibility", 10);

View File

@ -12,7 +12,7 @@ public class PurgeSettings implements SettingsHolder {
public static final Property<Boolean> USE_AUTO_PURGE =
newProperty("Purge.useAutoPurge", false);
@Comment("Number of Days an account become Unused")
@Comment("Number of days after which an account should be purged")
public static final Property<Integer> DAYS_BEFORE_REMOVE_PLAYER =
newProperty("Purge.daysBeforeRemovePlayer", 60);
@ -28,7 +28,7 @@ public class PurgeSettings implements SettingsHolder {
public static final Property<String> DEFAULT_WORLD =
newProperty("Purge.defaultWorld", "world");
@Comment("Do we need to remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge process ?")
@Comment("Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge?")
public static final Property<Boolean> REMOVE_LIMITED_CREATIVE_INVENTORIES =
newProperty("Purge.removeLimitedCreativesInventories", false);

View File

@ -52,8 +52,9 @@ public class RegistrationSettings implements SettingsHolder {
public static final Property<List<String>> FORCE_COMMANDS =
newListProperty("settings.forceCommands");
@Comment("Force these commands after /login as service console, without any '/'. "
+ "Use %p to replace with player name")
@Comment({
"Force these commands after /login as service console, without any '/'.",
"Use %p to replace with player name"})
public static final Property<List<String>> FORCE_COMMANDS_AS_CONSOLE =
newListProperty("settings.forceCommandsAsConsole");
@ -61,22 +62,25 @@ public class RegistrationSettings implements SettingsHolder {
public static final Property<List<String>> FORCE_REGISTER_COMMANDS =
newListProperty("settings.forceRegisterCommands");
@Comment("Force these commands after /register as a server console, without any '/'. "
+ "Use %p to replace with player name")
@Comment({
"Force these commands after /register as a server console, without any '/'.",
"Use %p to replace with player name"})
public static final Property<List<String>> FORCE_REGISTER_COMMANDS_AS_CONSOLE =
newListProperty("settings.forceRegisterCommandsAsConsole");
@Comment({
"Enable to display the welcome message (welcome.txt) after a login",
"You can use colors in this welcome.txt + some replaced strings:",
"{PLAYER}: player name, {ONLINE}: display number of online players, {MAXPLAYERS}: display server slots,",
"{IP}: player ip, {LOGINS}: number of players logged, {WORLD}: player current world, {SERVER}: server name",
"{PLAYER}: player name, {ONLINE}: display number of online players,",
"{MAXPLAYERS}: display server slots, {IP}: player ip, {LOGINS}: number of players logged,",
"{WORLD}: player current world, {SERVER}: server name",
"{VERSION}: get current bukkit version, {COUNTRY}: player country"})
public static final Property<Boolean> USE_WELCOME_MESSAGE =
newProperty("settings.useWelcomeMessage", true);
@Comment("Do we need to broadcast the welcome message to all server or only to the player? set true for "
+ "server or false for player")
@Comment({
"Broadcast the welcome message to the server or only to the player?",
"set true for server or false for player"})
public static final Property<Boolean> BROADCAST_WELCOME_MESSAGE =
newProperty("settings.broadcastWelcomeMessage", false);

View File

@ -36,7 +36,7 @@ public class RestrictionSettings implements SettingsHolder {
@Comment("Minimum allowed username length")
public static final Property<Integer> MIN_NICKNAME_LENGTH =
newProperty("settings.restrictions.minNicknameLength", 4);
newProperty("settings.restrictions.minNicknameLength", 3);
@Comment("Maximum allowed username length")
public static final Property<Integer> MAX_NICKNAME_LENGTH =
@ -50,9 +50,9 @@ public class RestrictionSettings implements SettingsHolder {
newProperty("settings.restrictions.ForceSingleSession", true);
@Comment({
"If enabled, every player that spawn in one of the world listed in \"ForceSpawnLocOnJoin.worlds\"",
"will be teleported to the spawnpoint after successful authentication.",
"The quit location of the player will be overwritten.",
"If enabled, every player that spawn in one of the world listed in",
"\"ForceSpawnLocOnJoin.worlds\" will be teleported to the spawnpoint after successful",
"authentication. The quit location of the player will be overwritten.",
"This is different from \"teleportUnAuthedToSpawn\" that teleport player",
"to the spawnpoint on join."})
public static final Property<Boolean> FORCE_SPAWN_LOCATION_AFTER_LOGIN =

View File

@ -18,10 +18,6 @@ public class SecuritySettings implements SettingsHolder {
public static final Property<Boolean> STOP_SERVER_ON_PROBLEM =
newProperty("Security.SQLProblem.stopServer", true);
@Comment("/reload support")
public static final Property<Boolean> USE_RELOAD_COMMAND_SUPPORT =
newProperty("Security.ReloadCommand.useReloadCommandSupport", true);
@Comment("Remove passwords from console?")
public static final Property<Boolean> REMOVE_PASSWORD_FROM_CONSOLE =
newProperty("Security.console.removePassword", true);
@ -85,11 +81,13 @@ public class SecuritySettings implements SettingsHolder {
newProperty("settings.security.supportOldPasswordHash", false);
@Comment({"Prevent unsafe passwords from being used; put them in lowercase!",
"You should always set 'help' as unsafePassword due to possible conflicts.",
"unsafePasswords:",
"- '123456'",
"- 'password'"})
"- 'password'",
"- 'help'"})
public static final Property<List<String>> UNSAFE_PASSWORDS =
newLowercaseListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321", "123456789");
newLowercaseListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321", "123456789", "help");
@Comment("Tempban a user's IP address if they enter the wrong password too many times")
public static final Property<Boolean> TEMPBAN_ON_MAX_LOGINS =

View File

@ -23,7 +23,7 @@ public final class FileUtils {
* Copy a resource file (from the JAR) to the given file if it doesn't exist.
*
* @param destinationFile The file to check and copy to (outside of JAR)
* @param resourcePath Absolute path to the resource file (path to file within JAR)
* @param resourcePath Local path to the resource file (path to file within JAR)
*
* @return False if the file does not exist and could not be copied, true otherwise
*/
@ -35,9 +35,7 @@ public final class FileUtils {
return false;
}
// ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/'
final String normalizedPath = resourcePath.replace("\\", "/");
try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) {
try (InputStream is = getResourceFromJar(resourcePath)) {
if (is == null) {
ConsoleLogger.warning(format("Cannot copy resource '%s' to file '%s': cannot load resource",
resourcePath, destinationFile.getPath()));
@ -52,6 +50,18 @@ public final class FileUtils {
return false;
}
/**
* Returns a JAR file as stream. Returns null if it doesn't exist.
*
* @param path the local path (starting from resources project, e.g. "config.yml" for 'resources/config.yml')
* @return the stream if the file exists, or false otherwise
*/
public static InputStream getResourceFromJar(String path) {
// ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/'
final String normalizedPath = path.replace("\\", "/");
return AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath);
}
/**
* Delete a given directory and all its content.
*

View File

@ -0,0 +1,44 @@
# Translation config for the AuthMe help, e.g. when /authme help or /authme help register is called
# -------------------------------------------------------
# List of texts used in the help section
common:
optional: 'Opcional'
hasPermission: 'Você tem permissão'
noPermission: 'Sem Permissão'
default: 'Default'
result: 'Resultado'
defaultPermissions:
notAllowed: 'Sem Permissão'
opOnly: 'OP''s only'
allowed: 'Todos podem'
# -------------------------------------------------------
# Titles of the individual help sections
# Set the translation text to empty text to disable the section, e.g. to hide alternatives:
# alternatives: ''
section:
command: 'Comando'
description: 'Pequena descrição'
detailedDescription: 'Descrição detalhada'
arguments: 'Argumentos'
permissions: 'Permissões'
alternatives: 'Alternativas'
children: 'Comandos'
# -------------------------------------------------------
# You can translate the data for all commands using the below pattern.
# For example to translate /authme reload, create a section "authme.reload", or "login" for /login
# If the command has arguments, you can use arg1 as below to translate the first argument, and so forth
# Translations don't need to be complete; any missing section will be taken from the default silently
# Important: Put main commands like "authme" before their children (e.g. "authme.reload")
commands:
authme.register:
description: 'Registra um jogador'
detailedDescription: 'Registra um esprecifico jogador com uma senha especifica.'
arg1:
label: 'player'
description: 'Nome do player'
arg2:
label: 'password'
description: 'Senha'

View File

@ -0,0 +1,154 @@
# Lingua Italiana creata da Maxetto e sgdc3.
# Translation config for the AuthMe help, e.g. when /authme help or /authme help register is called
# -------------------------------------------------------
# List of texts used in the help section
common:
header: '==========[ Assistenza AuthMeReloaded ]=========='
optional: 'Opzionale'
hasPermission: 'Hai il permesso'
noPermission: 'Non hai il permesso'
default: 'Configurazione base'
result: 'Risultato'
defaultPermissions:
notAllowed: 'Nessuno autorizzato'
opOnly: 'Solo per OP'
allowed: 'Tutti autorizzati'
# -------------------------------------------------------
# Titles of the individual help sections
# Set the translation text to empty text to disable the section, e.g. to hide alternatives:
# alternatives: ''
section:
command: 'Comando'
description: 'Descrizione breve'
detailedDescription: 'Descrizione dettagliata'
arguments: 'Parametri'
permissions: 'Permessi'
alternatives: 'Alternative'
children: 'Comandi'
# -------------------------------------------------------
# You can translate the data for all commands using the below pattern.
# For example to translate /authme reload, create a section "authme.reload", or "login" for /login
# If the command has arguments, you can use arg1 as below to translate the first argument, and so forth
# Translations don't need to be complete; any missing section will be taken from the default silently
# Important: Put main commands like "authme" before their children (e.g. "authme.reload")
commands:
authme.register:
description: 'Registra un giocatore'
detailedDescription: 'Registra il giocatore indicato con la password inserita.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore'
arg2:
label: 'password'
description: 'Password'
authme.unregister:
description: 'Rimuovi un giocatore'
detailedDescription: 'Rimuovi il giocatore indicato dal Database.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore'
authme.forcelogin:
description: 'Forza l''autenticazione ad un giocatore'
detailedDescription: 'Autentica il giocatore indicato.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore connesso'
authme.password:
description: 'Cambia la password di un giocatore'
detailedDescription: 'Cambia la password del giocatore indicato.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore'
arg2:
label: 'password'
description: 'Nuova Password'
authme.lastlogin:
description: 'Ultima autenticazione di un giocatore'
detailedDescription: 'Visualizza l''ultima data di autenticazione del giocatore indicato.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore'
authme.accounts:
description: 'Mostra i profili di un giocatore'
detailedDescription: 'Mostra tutti i profili di un giocatore attraverso il nome o l''indirizzo IP.'
arg1:
label: 'giocatore'
description: 'Nome o indirizzo IP del giocatore'
authme.email:
description: 'Mostra l''indirizzo email di un giocatore'
detailedDescription: 'Mostra l''indirizzo email del giocatore indicato se impostato.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore'
authme.setemail:
description: 'Cambia l''indirizzo email di un giocatore'
detailedDescription: 'Cambia l''indirizzo email del giocatore indicato.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore'
arg2:
label: 'email'
description: 'Indirizzo email del giocatore'
authme.getip:
description: 'Mostra l''indirizzo IP di un giocatore'
detailedDescription: 'Mostra l''indirizzo IP del giocatore indicato.'
arg1:
label: 'giocatore'
description: 'Nome del giocatore connesso'
authme.spawn:
description: 'Teletrasportati al punto di rigenerazione'
detailedDescription: 'Teletrasportati al punto di rigenerazione.'
authme.setspawn:
description: 'Cambia il punto di rigenerazione'
detailedDescription: 'Cambia il punto di rigenerazione dei giocatori alla tua posizione.'
authme.firstspawn:
description: 'Teletrasportati al punto di rigenerazione iniziale'
detailedDescription: 'Teletrasportati al punto di rigenerazione iniziale.'
authme.setfirstspawn:
description: 'Cambia il punto di rigenerazione iniziale'
detailedDescription: 'Cambia il punto di rigenerazione iniziale dei giocatori alla tua posizione.'
authme.purge:
description: 'Elimina i vecchi dati'
detailedDescription: 'Elimina i dati di AuthMeReloaded più vecchi dei giorni indicati.'
arg1:
label: 'giorni'
description: 'Numero di giorni'
arg2:
label: 'all'
description: 'Aggiungi ''all'' alla fine per eliminare anche i giocatori con l''ultima autenticazione pari a 0'
authme.resetpos:
description: 'Elimina l''ultima posizione di un giocatore'
detailedDescription: 'Elimina l''ultima posizione conosciuta del giocatore indicato o di tutti i giocatori.'
arg1:
label: 'giocatore/*'
description: 'Nome del giocatore o ''*'' per tutti i giocatori'
authme.purgebannedplayers:
description: 'Elimina i dati dei giocatori banditi'
detailedDescription: 'Elimina tutti i dati di AuthMeReloaded dei giocatori banditi.'
authme.switchantibot:
description: 'Cambia lo stato del servizio di AntiBot'
detailedDescription: 'Cambia lo stato del servizio di AntiBot allo stato indicato.'
arg1:
label: 'stato'
description: 'ON / OFF'
authme.reload:
description: 'Ricarica il plugin'
detailedDescription: 'Ricarica il plugin AuthMeReloaded.'
authme.version:
description: 'Informazioni sulla versione'
detailedDescription: 'Mostra informazioni dettagliate riguardo la versione di AuthMeReloaded in uso, gli sviluppatori, i collaboratori e la licenza.'
authme.converter:
description: 'Comando per il convertitore'
detailedDescription: 'Comando per il convertitore di AuthMeReloaded.'
arg1:
label: 'incarico'
description: 'Incarico di conversione: xauth / crazylogin / rakamak / royalauth / vauth / sqliteToSql / mysqlToSqlite'
authme.help:
description: 'Visualizza l''assistenza'
detailedDescription: 'Visualizza informazioni dettagliate per i comandi ''/authme''.'
arg1:
label: 'comando'
description: 'Il comando di cui vuoi ricevere assistenza'

View File

@ -61,6 +61,8 @@ email_confirm: '&cPor favor confirme seu endereço de email!'
email_changed: '&2Troca de email com sucesso.!'
email_send: '&2Recuperação de email enviada com sucesso! Por favor, verifique sua caixa de entrada de e-mail!'
email_exists: '&cUm e-mail de recuperação já foi enviado! Você pode descartá-lo e enviar um novo usando o comando abaixo:'
email_show: '&2O seu endereço de e-mail atual é: &f%email'
show_no_email: '&2Você atualmente não têm endereço de e-mail associado a esta conta.'
country_banned: '&4O seu país está banido neste servidor!'
antibot_auto_enabled: '&4[AntiBotService] habilitado devido ao enorme número de conexões!'
antibot_auto_disabled: '&2[AntiBotService] AntiBot desativada após %m minutos!'
@ -75,5 +77,3 @@ kicked_admin_registered: 'Um administrador registrou você; por favor faça logi
incomplete_email_settings: 'Erro: Nem todas as configurações necessárias estão definidas para o envio de e-mails. Entre em contato com um administrador.'
recovery_code_sent: 'Um código de recuperação para redefinir sua senha foi enviada para o seu e-mail.'
recovery_code_incorrect: 'O código de recuperação esta incorreto! Use /email recovery [email] para gerar um novo!'
# TODO email_show: '&2Your current email address is: &f%email'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.'

View File

@ -72,5 +72,5 @@ kicked_admin_registered: 'Adminisztrátor által regisztrálva lettél; kérlek
accounts_owned_self: '%count db regisztrációd van:'
recovery_code_incorrect: 'A visszaállító kód helytelen volt! Használd a következő parancsot: /email recovery [email címed] egy új generálásához'
recovery_code_sent: 'A jelszavad visszaállításához szükséges kódot sikeresen kiküldtük az email címedre!'
# TODO email_show: '&2Your current email address is: &f%email'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.'
email_show: '&2A jelenlegi email-ed a következő: &f%email'
show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.'

View File

@ -69,9 +69,9 @@ invalid_name_case: 'Dovresti entrare con questo nome utente: "%valid", al posto
tempban_max_logins: '&cSei stato temporaneamente bandito per aver fallito l''autenticazione troppe volte.'
accounts_owned_self: 'Possiedi %count account:'
accounts_owned_other: 'Il giocatore %name possiede %count account:'
kicked_admin_registered: 'Un amministratore ti ha appena registrato; per favore rientra nel server'
kicked_admin_registered: 'Un amministratore ti ha appena registrato, per favore rientra nel server'
incomplete_email_settings: 'Errore: non tutte le impostazioni richieste per inviare le email sono state impostate. Per favore contatta un amministratore.'
recovery_code_incorrect: 'Il codice di recupero inserito non è corretto! Scrivi /email recovery <email> per generarne uno nuovo'
recovery_code_sent: 'Una email contenente il codice di recupero per reimpostare la tua password è stata appena inviata al tuo indirizzo email.'
# TODO email_show: '&2Your current email address is: &f%email'
# TODO show_no_email: '&2You currently don''t have email address associated with this account.'
email_show: '&2Il tuo indirizzo email al momento è: &f%email'
show_no_email: '&2Al momento non hai nessun indirizzo email associato al tuo account.'

View File

@ -15,186 +15,197 @@ softdepend:
- EssentialsSpawn
- ProtocolLib
commands:
authme:
description: AuthMe op commands
usage: '/authme reload|register playername password|changepassword playername password|unregister playername|version|converter'
register:
description: Register an account
usage: /register <password> <confirmpassword>
aliases: [reg]
login:
description: Login command
usage: /login <password>
aliases: [l,log]
changepassword:
description: Change password of an account
usage: /changepassword <oldPassword> <newPassword>
aliases: [cp,changepass]
logout:
description: Logout
usage: /logout
unregister:
description: Unregister your account
usage: /unregister <password>
aliases: [unreg]
email:
description: Add Email or recover password
usage: '/email add your@email.com your@email.com|change oldEmail newEmail|recovery your@email.com'
captcha:
description: Captcha command
usage: /captcha <code>
authme:
description: AuthMe op commands
usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages
login:
description: Login command
usage: /login <password>
aliases:
- l
- log
logout:
description: Logout command
usage: /logout
register:
description: Register an account
usage: /register [password] [verifyPassword]
aliases:
- reg
unregister:
description: Unregister an account
usage: /unregister <password>
aliases:
- unreg
changepassword:
description: Change password of an account
usage: /changepassword <oldPassword> <newPassword>
aliases:
- changepass
- cp
email:
description: Add email or recover password
usage: /email show|add|change|recover
captcha:
description: Captcha Command
usage: /captcha <captcha>
permissions:
authme.admin.*:
description: Give access to all admin commands.
children:
authme.admin.accounts: true
authme.admin.changemail: true
authme.admin.changepassword: true
authme.admin.converter: true
authme.admin.firstspawn: true
authme.admin.forcelogin: true
authme.admin.getemail: true
authme.admin.getip: true
authme.admin.lastlogin: true
authme.admin.purge: true
authme.admin.purgebannedplayers: true
authme.admin.purgelastpos: true
authme.admin.register: true
authme.admin.reload: true
authme.admin.setfirstspawn: true
authme.admin.setspawn: true
authme.admin.spawn: true
authme.admin.switchantibot: true
authme.admin.unregister: true
authme.admin.register:
description: Administrator command to register a new user.
default: op
authme.admin.unregister:
description: Administrator command to unregister an existing user.
default: op
authme.admin.forcelogin:
description: Administrator command to force-login an existing user.
default: op
authme.admin.changepassword:
description: Administrator command to change the password of a user.
default: op
authme.admin.lastlogin:
description: Administrator command to see the last login date and time of a user.
default: op
authme.admin.accounts:
description: Administrator command to see all accounts associated with a user.
default: op
authme.admin.getemail:
description: Administrator command to get the email address of a user, if set.
default: op
authme.admin.changemail:
description: Administrator command to set or change the email address of a user.
default: op
authme.admin.getip:
description: Administrator command to get the last known IP of a user.
default: op
authme.admin.spawn:
description: Administrator command to teleport to the AuthMe spawn.
default: op
authme.admin.setspawn:
description: Administrator command to set the AuthMe spawn.
default: op
authme.admin.firstspawn:
description: Administrator command to teleport to the first AuthMe spawn.
default: op
authme.admin.setfirstspawn:
description: Administrator command to set the first AuthMe spawn.
default: op
authme.admin.purge:
description: Administrator command to purge old user data.
default: op
authme.admin.purgelastpos:
description: Administrator command to purge the last position of a user.
default: op
authme.admin.purgebannedplayers:
description: Administrator command to purge all data associated with banned players.
default: op
authme.admin.seeotheraccounts:
description: Permission for user to see other accounts.
default: op
authme.admin.switchantibot:
description: Administrator command to toggle the AntiBot protection status.
default: op
authme.admin.converter:
description: Administrator command to convert old or other data to AuthMe data.
default: op
authme.admin.reload:
description: Administrator command to reload the plugin configuration.
default: op
authme.admin.antibotmessages:
description: Permission to see Antibot messages
default: op
authme.player.*:
description: Permission to use all player (non-admin) commands.
children:
authme.player.canbeforced: true
authme.player.captcha: true
authme.player.changepassword: true
authme.player.email.add: true
authme.player.email.change: true
authme.player.email.recover: true
authme.player.login: true
authme.player.logout: true
authme.player.register: true
authme.player.unregister: true
authme.player.seeownaccounts: true
authme.player.email:
description: Gives access to player email commands
default: false
children:
authme.player.email.add: true
authme.player.email.change: true
authme.player.email.recover: true
authme.player.login:
description: Command permission to login.
default: true
authme.player.logout:
description: Command permission to logout.
default: true
authme.player.register:
description: Command permission to register.
default: true
authme.player.unregister:
description: Command permission to unregister.
default: true
authme.player.changepassword:
description: Command permission to change the password.
default: true
authme.player.email.add:
description: Command permission to add an email address.
default: true
authme.player.email.change:
description: Command permission to change the email address.
default: true
authme.player.email.recover:
description: Command permission to recover an account using its email address.
default: true
authme.player.captcha:
description: Command permission to use captcha.
default: true
authme.player.canbeforced:
description: Permission for users a login can be forced to.
default: true
authme.player.seeownaccounts:
description: Permission to use to see own other accounts.
default: true
authme.vip:
description: Allow vip slot when the server is full
default: op
authme.bypassantibot:
description: Bypass the AntiBot check
default: op
authme.allowmultipleaccounts:
description: Allow more accounts for same ip
default: op
authme.bypassforcesurvival:
description: Bypass all ForceSurvival features
default: op
authme.bypasspurge:
description: Permission to bypass the purging process
default: false
authme.admin.*:
description: Gives access to all admin commands
children:
authme.admin.accounts: true
authme.admin.antibotmessages: true
authme.admin.changemail: true
authme.admin.changepassword: true
authme.admin.converter: true
authme.admin.firstspawn: true
authme.admin.forcelogin: true
authme.admin.getemail: true
authme.admin.getip: true
authme.admin.lastlogin: true
authme.admin.purge: true
authme.admin.purgebannedplayers: true
authme.admin.purgelastpos: true
authme.admin.register: true
authme.admin.reload: true
authme.admin.seeotheraccounts: true
authme.admin.setfirstspawn: true
authme.admin.setspawn: true
authme.admin.spawn: true
authme.admin.switchantibot: true
authme.admin.unregister: true
authme.admin.updatemessages: true
authme.admin.accounts:
description: Administrator command to see all accounts associated with a user.
default: op
authme.admin.antibotmessages:
description: Permission to see Antibot messages.
default: op
authme.admin.changemail:
description: Administrator command to set or change the email address of a user.
default: op
authme.admin.changepassword:
description: Administrator command to change the password of a user.
default: op
authme.admin.converter:
description: Administrator command to convert old or other data to AuthMe data.
default: op
authme.admin.firstspawn:
description: Administrator command to teleport to the first AuthMe spawn.
default: op
authme.admin.forcelogin:
description: Administrator command to force-login an existing user.
default: op
authme.admin.getemail:
description: Administrator command to get the email address of a user, if set.
default: op
authme.admin.getip:
description: Administrator command to get the last known IP of a user.
default: op
authme.admin.lastlogin:
description: Administrator command to see the last login date and time of a user.
default: op
authme.admin.purge:
description: Administrator command to purge old user data.
default: op
authme.admin.purgebannedplayers:
description: Administrator command to purge all data associated with banned players.
default: op
authme.admin.purgelastpos:
description: Administrator command to purge the last position of a user.
default: op
authme.admin.register:
description: Administrator command to register a new user.
default: op
authme.admin.reload:
description: Administrator command to reload the plugin configuration.
default: op
authme.admin.seeotheraccounts:
description: Permission to see the other accounts of the players that log in.
default: op
authme.admin.setfirstspawn:
description: Administrator command to set the first AuthMe spawn.
default: op
authme.admin.setspawn:
description: Administrator command to set the AuthMe spawn.
default: op
authme.admin.spawn:
description: Administrator command to teleport to the AuthMe spawn.
default: op
authme.admin.switchantibot:
description: Administrator command to toggle the AntiBot protection status.
default: op
authme.admin.unregister:
description: Administrator command to unregister an existing user.
default: op
authme.admin.updatemessages:
description: Permission to use the update messages command.
default: op
authme.allowmultipleaccounts:
description: Permission to be able to register multiple accounts.
default: op
authme.bypassantibot:
description: Permission node to bypass AntiBot protection.
default: op
authme.bypassforcesurvival:
description: Permission for users to bypass force-survival mode.
default: op
authme.bypasspurge:
description: Permission to bypass the purging process.
default: false
authme.player.*:
description: Gives access to all player commands
children:
authme.player.canbeforced: true
authme.player.captcha: true
authme.player.changepassword: true
authme.player.email.add: true
authme.player.email.change: true
authme.player.email.recover: true
authme.player.login: true
authme.player.logout: true
authme.player.register: true
authme.player.seeownaccounts: true
authme.player.unregister: true
authme.player.canbeforced:
description: Permission for users a login can be forced to.
default: true
authme.player.captcha:
description: Command permission to use captcha.
default: true
authme.player.changepassword:
description: Command permission to change the password.
default: true
authme.player.email:
description: Gives access to all email commands
children:
authme.player.email.add: true
authme.player.email.change: true
authme.player.email.recover: true
authme.player.email.add:
description: Command permission to add an email address.
default: true
authme.player.email.change:
description: Command permission to change the email address.
default: true
authme.player.email.recover:
description: Command permission to recover an account using its email address.
default: true
authme.player.login:
description: Command permission to login.
default: true
authme.player.logout:
description: Command permission to logout.
default: true
authme.player.register:
description: Command permission to register.
default: true
authme.player.seeownaccounts:
description: Permission to use to see own other accounts.
default: true
authme.player.unregister:
description: Command permission to unregister.
default: true
authme.vip:
description: Permission node to identify VIP users.
default: op

View File

@ -18,6 +18,7 @@ import java.util.regex.Pattern;
import static fr.xephi.authme.permission.DefaultPermission.OP_ONLY;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
@ -32,7 +33,7 @@ public class CommandInitializerTest {
*/
private static int MAX_ALLOWED_DEPTH = 1;
private static Set<CommandDescription> commands;
private static Collection<CommandDescription> commands;
@BeforeClass
public static void initializeCommandCollection() {
@ -46,7 +47,7 @@ public class CommandInitializerTest {
// It obviously doesn't make sense to test much of the concrete data
// that is being initialized; we just want to guarantee with this test
// that data is indeed being initialized and we take a few "probes"
assertThat(commands.size(), equalTo(8));
assertThat(commands, hasSize(8));
assertThat(commandsIncludeLabel(commands, "authme"), equalTo(true));
assertThat(commandsIncludeLabel(commands, "register"), equalTo(true));
assertThat(commandsIncludeLabel(commands, "help"), equalTo(false));

View File

@ -39,7 +39,7 @@ import static org.mockito.Mockito.mock;
@RunWith(DelayedInjectionRunner.class)
public class CommandMapperTest {
private static Set<CommandDescription> commands;
private static List<CommandDescription> commands;
@InjectDelayed
private CommandMapper mapper;

View File

@ -1,8 +1,15 @@
package fr.xephi.authme.command;
import fr.xephi.authme.TestHelper;
import org.bukkit.ChatColor;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
@ -11,6 +18,13 @@ import static org.junit.Assert.assertThat;
*/
public class CommandUtilsTest {
private static Collection<CommandDescription> commands;
@BeforeClass
public static void setUpTestCommands() {
commands = TestCommandsUtil.generateCommands();
}
@Test
public void shouldReturnCommandPath() {
// given
@ -19,14 +33,14 @@ public class CommandUtilsTest {
.description("Base")
.detailedDescription("Test base command.")
.executableCommand(ExecutableCommand.class)
.build();
.register();
CommandDescription command = CommandDescription.builder()
.parent(base)
.labels("help", "h", "?")
.description("Child")
.detailedDescription("Test child command.")
.executableCommand(ExecutableCommand.class)
.build();
.register();
// when
String commandPath = CommandUtils.constructCommandPath(command);
@ -42,7 +56,7 @@ public class CommandUtilsTest {
@Test
public void shouldComputeMinAndMaxOnEmptyCommand() {
// given
CommandDescription command = getBuilderForArgsTest().build();
CommandDescription command = getBuilderForArgsTest().register();
// when / then
checkArgumentCount(command, 0, 0);
@ -54,7 +68,7 @@ public class CommandUtilsTest {
CommandDescription command = getBuilderForArgsTest()
.withArgument("Test", "Arg description", false)
.withArgument("Test22", "Arg description 2", false)
.build();
.register();
// when / then
checkArgumentCount(command, 2, 2);
@ -67,7 +81,7 @@ public class CommandUtilsTest {
.withArgument("arg1", "Arg description", false)
.withArgument("arg2", "Arg description 2", true)
.withArgument("arg3", "Arg description 3", true)
.build();
.register();
// when / then
checkArgumentCount(command, 1, 3);
@ -79,6 +93,46 @@ public class CommandUtilsTest {
TestHelper.validateHasOnlyPrivateEmptyConstructor(CommandUtils.class);
}
@Test
public void shouldFormatSimpleArgument() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme");
List<String> labels = Collections.singletonList("authme");
// when
String result = CommandUtils.buildSyntax(command, labels);
// then
assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW));
}
@Test
public void shouldFormatCommandWithMultipleArguments() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme", "register");
List<String> labels = Arrays.asList("authme", "reg");
// when
String result = CommandUtils.buildSyntax(command, labels);
// then
assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW + " reg <password> <confirmation>"));
}
@Test
public void shouldFormatCommandWithOptionalArgument() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "email");
List<String> labels = Collections.singletonList("email");
// when
String result = CommandUtils.buildSyntax(command, labels);
// then
assertThat(result, equalTo(ChatColor.WHITE + "/email" + ChatColor.YELLOW + " [player]"));
}
private static void checkArgumentCount(CommandDescription command, int expectedMin, int expectedMax) {
assertThat(CommandUtils.getMinNumberOfArguments(command), equalTo(expectedMin));

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command;
import com.google.common.collect.ImmutableList;
import fr.xephi.authme.command.executable.HelpCommand;
import fr.xephi.authme.permission.AdminPermission;
import fr.xephi.authme.permission.PermissionNode;
@ -8,9 +9,7 @@ import org.bukkit.command.CommandSender;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
@ -27,7 +26,7 @@ public final class TestCommandsUtil {
*
* @return The generated commands
*/
public static Set<CommandDescription> generateCommands() {
public static List<CommandDescription> generateCommands() {
// Register /authme
CommandDescription authMeBase = createCommand(null, null, singletonList("authme"), ExecutableCommand.class);
// Register /authme login <password>
@ -42,13 +41,13 @@ public final class TestCommandsUtil {
newArgument("player", true));
// Register /email helptest -- use only to test for help command arguments special case
CommandDescription.builder().parent(emailBase).labels("helptest").executableCommand(HelpCommand.class)
.description("test").detailedDescription("Test.").withArgument("Query", "", false).build();
.description("test").detailedDescription("Test.").withArgument("Query", "", false).register();
// Register /unregister <player>, alias: /unreg
CommandDescription unregisterBase = createCommand(AdminPermission.UNREGISTER, null,
asList("unregister", "unreg"), TestUnregisterCommand.class, newArgument("player", false));
return newHashSet(authMeBase, emailBase, unregisterBase);
return ImmutableList.of(authMeBase, emailBase, unregisterBase);
}
/**
@ -101,7 +100,7 @@ public final class TestCommandsUtil {
}
}
return command.build();
return command.register();
}
/** Shortcut command to initialize a new argument description. */

View File

@ -20,6 +20,10 @@ import static fr.xephi.authme.command.FoundResultStatus.INCORRECT_ARGUMENTS;
import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND;
import static fr.xephi.authme.command.FoundResultStatus.SUCCESS;
import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_COMMAND;
import static fr.xephi.authme.command.help.HelpProvider.SHOW_DESCRIPTION;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.hamcrest.CoreMatchers.containsString;
@ -108,7 +112,7 @@ public class HelpCommandTest {
CommandDescription commandDescription = mock(CommandDescription.class);
given(commandDescription.getLabelCount()).willReturn(1);
FoundCommandResult foundCommandResult = new FoundCommandResult(commandDescription, singletonList("authme"),
Collections.<String>emptyList(), 0.0, SUCCESS);
Collections.emptyList(), 0.0, SUCCESS);
given(commandMapper.mapPartsToCommand(sender, arguments)).willReturn(foundCommandResult);
// when
@ -116,7 +120,8 @@ public class HelpCommandTest {
// then
verify(sender, never()).sendMessage(anyString());
verify(helpProvider).outputHelp(sender, foundCommandResult, HelpProvider.SHOW_CHILDREN);
verify(helpProvider).outputHelp(sender, foundCommandResult,
SHOW_DESCRIPTION | SHOW_COMMAND | SHOW_CHILDREN | SHOW_ALTERNATIVES);
}
@Test
@ -126,7 +131,7 @@ public class HelpCommandTest {
CommandDescription commandDescription = mock(CommandDescription.class);
given(commandDescription.getLabelCount()).willReturn(2);
FoundCommandResult foundCommandResult = new FoundCommandResult(commandDescription, asList("authme", "getpos"),
Collections.<String>emptyList(), 0.0, INCORRECT_ARGUMENTS);
Collections.emptyList(), 0.0, INCORRECT_ARGUMENTS);
given(commandMapper.mapPartsToCommand(sender, arguments)).willReturn(foundCommandResult);
// when

View File

@ -1,76 +0,0 @@
package fr.xephi.authme.command.help;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.command.CommandDescription;
import fr.xephi.authme.command.TestCommandsUtil;
import org.bukkit.ChatColor;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
/**
* Test for {@link CommandSyntaxHelper}.
*/
public class CommandSyntaxHelperTest {
private static Set<CommandDescription> commands;
@BeforeClass
public static void setUpTestCommands() {
commands = TestCommandsUtil.generateCommands();
}
@Test
public void shouldFormatSimpleArgument() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme");
List<String> labels = Collections.singletonList("authme");
// when
String result = CommandSyntaxHelper.getSyntax(command, labels);
// then
assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW));
}
@Test
public void shouldFormatCommandWithMultipleArguments() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme", "register");
List<String> labels = Arrays.asList("authme", "reg");
// when
String result = CommandSyntaxHelper.getSyntax(command, labels);
// then
assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW + " reg <password> <confirmation>"));
}
@Test
public void shouldFormatCommandWithOptionalArgument() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "email");
List<String> labels = Collections.singletonList("email");
// when
String result = CommandSyntaxHelper.getSyntax(command, labels);
// then
assertThat(result, equalTo(ChatColor.WHITE + "/email" + ChatColor.YELLOW + " [player]"));
}
@Test
public void shouldHaveHiddenConstructor() {
// given / when / then
TestHelper.validateHasOnlyPrivateEmptyConstructor(CommandSyntaxHelper.class);
}
}

View File

@ -9,8 +9,8 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Test;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
@ -78,7 +78,7 @@ public class HelpMessagesConsistencyTest {
* @return the CommandDescription object for the {@code /authme register} command.
*/
private static CommandDescription getAuthMeRegisterDescription() {
Set<CommandDescription> commands = new CommandInitializer().getCommands();
Collection<CommandDescription> commands = new CommandInitializer().getCommands();
List<CommandDescription> children = commands.stream()
.filter(command -> command.getLabels().contains("authme"))

View File

@ -12,10 +12,11 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import java.util.Set;
import java.util.Collection;
import java.util.function.Function;
import static fr.xephi.authme.TestHelper.getJarFile;
import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.sameInstance;
@ -30,7 +31,7 @@ import static org.mockito.Matchers.any;
public class HelpMessagesServiceTest {
private static final String TEST_FILE = "/fr/xephi/authme/command/help/help_test.yml";
private static final Set<CommandDescription> COMMANDS = TestCommandsUtil.generateCommands();
private static final Collection<CommandDescription> COMMANDS = TestCommandsUtil.generateCommands();
@InjectDelayed
private HelpMessagesService helpMessagesService;
@ -48,7 +49,7 @@ public class HelpMessagesServiceTest {
@Test
public void shouldReturnLocalizedCommand() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "authme", "register");
CommandDescription command = getCommandWithLabel(COMMANDS, "authme", "register");
// when
CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command);
@ -68,7 +69,7 @@ public class HelpMessagesServiceTest {
@Test
public void shouldReturnLocalizedCommandWithDefaults() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "authme", "login");
CommandDescription command = getCommandWithLabel(COMMANDS, "authme", "login");
// when
CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command);
@ -84,7 +85,7 @@ public class HelpMessagesServiceTest {
@Test
public void shouldReturnSameCommandForNoLocalization() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "email");
CommandDescription command = getCommandWithLabel(COMMANDS, "email");
// when
CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command);
@ -96,7 +97,7 @@ public class HelpMessagesServiceTest {
@Test
public void shouldKeepChildrenInLocalCommand() {
// given
CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "authme");
CommandDescription command = getCommandWithLabel(COMMANDS, "authme");
// when
CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command);
@ -114,4 +115,28 @@ public class HelpMessagesServiceTest {
assertThat(helpMessagesService.getMessage(HelpMessage.RESULT), equalTo("res."));
assertThat(helpMessagesService.getMessage(HelpSection.ARGUMENTS), equalTo("arg."));
}
@Test
public void shouldGetLocalCommandDescription() {
// given
CommandDescription command = getCommandWithLabel(COMMANDS, "authme", "register");
// when
String description = helpMessagesService.getDescription(command);
// then
assertThat(description, equalTo("Registration"));
}
@Test
public void shouldFallbackToDescriptionOnCommandObject() {
// given
CommandDescription command = getCommandWithLabel(COMMANDS, "unregister");
// when
String description = helpMessagesService.getDescription(command);
// then
assertThat(description, equalTo(command.getDescription()));
}
}

View File

@ -20,9 +20,9 @@ import org.mockito.Mock;
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel;
@ -53,7 +53,7 @@ import static org.mockito.Mockito.verify;
@RunWith(DelayedInjectionRunner.class)
public class HelpProviderTest {
private static Set<CommandDescription> commands;
private static Collection<CommandDescription> commands;
@InjectDelayed
private HelpProvider helpProvider;
@ -251,6 +251,10 @@ public class HelpProviderTest {
// given
CommandDescription command = getCommandWithLabel(commands, "authme");
FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme"));
given(helpMessagesService.getDescription(getCommandWithLabel(commands, "authme", "login")))
.willReturn("Command for login [localized]");
given(helpMessagesService.getDescription(getCommandWithLabel(commands, "authme", "register")))
.willReturn("Registration command [localized]");
// when
helpProvider.outputHelp(sender, result, SHOW_CHILDREN);
@ -258,9 +262,9 @@ public class HelpProviderTest {
// then
List<String> lines = getLines(sender);
assertThat(lines, hasSize(4));
assertThat(lines.get(1), containsString("Children:"));
assertThat(lines.get(2), containsString("/authme login: login cmd"));
assertThat(lines.get(3), containsString("/authme register: register cmd"));
assertThat(lines.get(1), equalTo("Children:"));
assertThat(lines.get(2), equalTo(" /authme login: Command for login [localized]"));
assertThat(lines.get(3), equalTo(" /authme register: Registration command [localized]"));
}
@Test
@ -395,6 +399,24 @@ public class HelpProviderTest {
assertThat(lines.get(0), equalTo("Command: /authme register <password> <confirmation>"));
}
@Test
public void shouldShowAlternativesForRootCommand() {
// given
CommandDescription command = getCommandWithLabel(commands, "unregister");
FoundCommandResult result = newFoundResult(command, Collections.singletonList("unreg"));
// when
helpProvider.outputHelp(sender, result, SHOW_COMMAND | SHOW_ALTERNATIVES);
// then
List<String> lines = getLines(sender);
assertThat(lines, hasSize(4));
assertThat(lines.get(0), equalTo("Header"));
assertThat(lines.get(1), equalTo("Command: /unreg <player>"));
assertThat(lines.get(2), equalTo("Alternatives:"));
assertThat(lines.get(3), equalTo(" /unregister <player>"));
}
/**
* Generate an instance of {@link FoundCommandResult} with the given command and labels. All other fields aren't
* retrieved by {@link HelpProvider} and so are initialized to default values for the tests.

View File

@ -1,75 +0,0 @@
package fr.xephi.authme.message;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Test;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import static org.junit.Assert.fail;
/**
* Tests that all YML message files can be loaded.
*/
public class MessagesFileYamlCheckerTest {
/** Path in the resources folder where the message files are located. */
private static final String MESSAGES_FOLDER = "/messages/";
/** Pattern of the message file names. */
private static final Pattern MESSAGE_FILE_PATTERN = Pattern.compile("messages_\\w+\\.yml");
/** Message key that is present in all files. Used to make sure that text is returned. */
private static final MessageKey MESSAGE_KEY = MessageKey.LOGIN_MESSAGE;
@Test
public void shouldAllBeValidYaml() {
// given
List<File> messageFiles = getMessageFiles();
// when
List<String> errors = new ArrayList<>();
for (File file : messageFiles) {
String error = null;
try {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file);
if (StringUtils.isEmpty(configuration.getString(MESSAGE_KEY.getKey()))) {
error = "Message for '" + MESSAGE_KEY + "' is empty";
}
} catch (Exception e) {
error = "Could not load file: " + StringUtils.formatException(e);
}
if (!StringUtils.isEmpty(error)) {
errors.add(file.getName() + ": " + error);
}
}
// then
if (!errors.isEmpty()) {
fail("Errors during verification of message files:\n-" + String.join("\n-", errors));
}
}
private List<File> getMessageFiles() {
File folder = TestHelper.getJarFile(MESSAGES_FOLDER);
File[] files = folder.listFiles();
if (files == null) {
throw new IllegalStateException("Could not read folder '" + folder.getName() + "'");
}
List<File> messageFiles = new ArrayList<>();
for (File file : files) {
if (MESSAGE_FILE_PATTERN.matcher(file.getName()).matches()) {
messageFiles.add(file);
}
}
if (messageFiles.isEmpty()) {
throw new IllegalStateException("Error getting message files: list of files is empty");
}
return messageFiles;
}
}

View File

@ -0,0 +1,98 @@
package fr.xephi.authme.message;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.command.help.HelpSection;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import static org.junit.Assert.fail;
/**
* Tests that all YML text files can be loaded.
*/
public class YamlTextFileCheckerTest {
/** Path in the resources folder where the message files are located. */
private static final String MESSAGES_FOLDER = "/messages/";
/** Contains all files of the MESSAGES_FOLDER. */
private static List<File> messageFiles;
@BeforeClass
public static void loadMessagesFiles() {
File folder = TestHelper.getJarFile(MESSAGES_FOLDER);
File[] files = folder.listFiles();
if (files == null || files.length == 0) {
throw new IllegalStateException("Could not read folder '" + folder.getName() + "'");
}
messageFiles = Arrays.asList(files);
}
@Test
public void testAllMessagesYmlFiles() {
checkFiles(
Pattern.compile("messages_\\w+\\.yml"),
MessageKey.LOGIN_MESSAGE.getKey());
}
@Test
public void testAllHelpYmlFiles() {
checkFiles(
Pattern.compile("help_\\w+\\.yml"),
HelpSection.ALTERNATIVES.getKey());
}
/**
* Checks all files in the messages folder that match the given pattern.
*
* @param pattern the pattern the file name needs to match
* @param mandatoryKey key present in all matched files
*/
private void checkFiles(Pattern pattern, String mandatoryKey) {
List<String> errors = new ArrayList<>();
boolean hasMatch = false;
for (File file : messageFiles) {
if (pattern.matcher(file.getName()).matches()) {
checkFile(file, mandatoryKey, errors);
hasMatch = true;
}
}
if (!errors.isEmpty()) {
fail("Errors while checking files matching '" + pattern + "':\n-" + String.join("\n-", errors));
} else if (!hasMatch) {
fail("Could not find any files satisfying pattern '" + pattern + "'");
}
}
/**
* Checks that the provided YAML file can be loaded and that it contains a non-empty text
* for the provided mandatory key.
*
* @param file the file to check
* @param mandatoryKey the key for which text must be present
* @param errors collection of errors to add to if the verification fails
*/
private void checkFile(File file, String mandatoryKey, List<String> errors) {
String error = null;
try {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file);
if (StringUtils.isEmpty(configuration.getString(mandatoryKey))) {
error = "Message for '" + mandatoryKey + "' is empty";
}
} catch (Exception e) {
error = "Could not load file: " + StringUtils.formatException(e);
}
if (!StringUtils.isEmpty(error)) {
errors.add(file.getName() + ": " + error);
}
}
}

View File

@ -26,7 +26,7 @@ public class PermissionConsistencyTest {
/** All classes defining permission nodes. */
private static final Set<Class<? extends PermissionNode>> PERMISSION_CLASSES = ImmutableSet
.<Class<? extends PermissionNode>>of(PlayerPermission.class, AdminPermission.class, PlayerStatePermission.class);
.of(PlayerPermission.class, AdminPermission.class, PlayerStatePermission.class);
/** Wildcard permissions (present in plugin.yml but not in the codebase). */
private static final Set<String> PLUGIN_YML_PERMISSIONS_WILDCARDS =

View File

@ -51,6 +51,7 @@ public class AntiBotServiceTest {
@BeforeInjecting
public void initSettings() {
given(settings.getProperty(ProtectionSettings.ANTIBOT_DURATION)).willReturn(10);
given(settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL)).willReturn(5);
given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(5);
given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(true);
given(settings.getProperty(ProtectionSettings.ANTIBOT_DELAY)).willReturn(8);

View File

@ -1,105 +0,0 @@
package fr.xephi.authme.settings;
import com.github.authme.configme.knownproperties.ConfigurationData;
import com.github.authme.configme.migration.MigrationService;
import com.github.authme.configme.migration.PlainMigrationService;
import com.github.authme.configme.properties.Property;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import org.bukkit.configuration.MemorySection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
/**
* Test for {@link Settings} and the project's config.yml,
* verifying that no settings are missing from the file.
*/
public class ConfigFileConsistencyTest {
/** The file name of the project's sample config file. */
private static final String CONFIG_FILE = "/config.yml";
@Test
public void shouldHaveAllConfigs() throws IOException {
// given
File configFile = TestHelper.getJarFile(CONFIG_FILE);
PropertyResource resource = new YamlFileResource(configFile);
MigrationService migration = new PlainMigrationService();
// when
boolean result = migration.checkAndMigrate(
resource, AuthMeSettingsRetriever.buildConfigurationData().getProperties());
// then
if (result) {
Set<String> knownProperties = getAllKnownPropertyPaths();
List<String> missingProperties = new ArrayList<>();
for (String path : knownProperties) {
if (!resource.contains(path)) {
missingProperties.add(path);
}
}
fail("Found missing properties!\n-" + String.join("\n-", missingProperties));
}
}
@Test
public void shouldNotHaveUnknownConfigs() {
// given
File configFile = TestHelper.getJarFile(CONFIG_FILE);
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile);
Map<String, Object> allReadProperties = configuration.getValues(true);
Set<String> knownKeys = getAllKnownPropertyPaths();
// when
List<String> unknownPaths = new ArrayList<>();
for (Map.Entry<String, Object> entry : allReadProperties.entrySet()) {
// The value being a MemorySection means it's a parent node
if (!(entry.getValue() instanceof MemorySection) && !knownKeys.contains(entry.getKey())) {
unknownPaths.add(entry.getKey());
}
}
// then
if (!unknownPaths.isEmpty()) {
fail("Found " + unknownPaths.size() + " unknown property paths in the project's config.yml: \n- "
+ String.join("\n- ", unknownPaths));
}
}
@Test
public void shouldHaveValueCorrespondingToPropertyDefault() {
// given
File configFile = TestHelper.getJarFile(CONFIG_FILE);
PropertyResource resource = new YamlFileResource(configFile);
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
// when / then
for (Property<?> property : configurationData.getProperties()) {
assertThat("Default value of '" + property.getPath() + "' in config.yml should be the same as in Property",
property.getValue(resource).equals(property.getDefaultValue()), equalTo(true));
}
}
private static Set<String> getAllKnownPropertyPaths() {
return AuthMeSettingsRetriever.buildConfigurationData()
.getProperties().stream()
.map(Property::getPath)
.collect(Collectors.toSet());
}
}

View File

@ -0,0 +1,69 @@
package fr.xephi.authme.settings;
import com.github.authme.configme.knownproperties.ConfigurationData;
import com.github.authme.configme.properties.Property;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.fail;
/**
* Tests the consistency of the settings configuration.
*/
public class SettingsConsistencyTest {
/**
* Maximum characters one comment line may have (prevents horizontal scrolling).
*/
private static final int MAX_COMMENT_LENGTH = 90;
private static ConfigurationData configurationData;
@BeforeClass
public static void buildConfigurationData() {
configurationData = AuthMeSettingsRetriever.buildConfigurationData();
}
@Test
public void shouldHaveCommentOnEachProperty() {
// given
List<Property<?>> properties = configurationData.getProperties();
// when / then
for (Property<?> property : properties) {
if (configurationData.getCommentsForSection(property.getPath()).length == 0) {
fail("No comment defined for '" + property + "'");
}
}
}
@Test
public void shouldNotHaveVeryLongCommentLines() {
// given
List<Property<?>> properties = configurationData.getProperties();
List<Property<?>> badProperties = new ArrayList<>();
// when
for (Property<?> property : properties) {
for (String comment : configurationData.getCommentsForSection(property.getPath())) {
if (comment.length() > MAX_COMMENT_LENGTH) {
badProperties.add(property);
break;
}
}
}
// then
if (!badProperties.isEmpty()) {
fail("Comment lines should not be longer than " + MAX_COMMENT_LENGTH + " chars, "
+ "but found too long comments for:\n- "
+ badProperties.stream().map(Property::getPath).collect(Collectors.joining("\n- ")));
}
}
}

View File

@ -1,67 +0,0 @@
package fr.xephi.authme.settings;
import com.github.authme.configme.knownproperties.ConfigurationData;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import com.google.common.io.Files;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;
/**
* Test for {@link SettingsMigrationService}.
*/
public class SettingsMigrationServiceTest {
@Rule
public TemporaryFolder testFolderHandler = new TemporaryFolder();
private File testFolder;
private File configTestFile;
/**
* Ensure that AuthMe regards the JAR's own config.yml as complete.
* If something legitimately needs migrating, a test from {@link ConfigFileConsistencyTest} should fail.
* If none fails in that class, it means something is wrong with the migration service
* as it wants to perform a migration on our up-to-date config.yml.
*/
@Test
public void shouldNotRewriteJarConfig() throws IOException {
// given
copyConfigToTestFolder();
PropertyResource resource = new YamlFileResource(configTestFile);
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
assumeThat(testFolder.listFiles(), arrayWithSize(1));
SettingsMigrationService migrationService = new SettingsMigrationService(testFolder);
// when
boolean result = migrationService.checkAndMigrate(resource, configurationData.getProperties());
// then
assertThat(result, equalTo(false));
assertThat(testFolder.listFiles(), arrayWithSize(1));
}
private void copyConfigToTestFolder() throws IOException {
testFolder = testFolderHandler.newFolder("migrationtest");
final File testConfig = testFolderHandler.newFile("migrationtest/config.yml");
final File realConfig = TestHelper.getJarFile("/config.yml");
Files.copy(realConfig, testConfig);
if (!testConfig.exists()) {
throw new IOException("Could not copy project's config.yml to test folder");
}
configTestFile = testConfig;
}
}

View File

@ -11,6 +11,8 @@ import java.io.File;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
/**
@ -47,15 +49,15 @@ public class FileUtilsTest {
public void shouldCopyFileFromJar() throws IOException {
// given
File folder = temporaryFolder.newFolder();
File file = new File(folder, "some/folders/config.yml");
File file = new File(folder, "some/folders/welcome.txt");
// when
boolean result = FileUtils.copyFileFromResource(file, "config.yml");
boolean result = FileUtils.copyFileFromResource(file, "welcome.txt");
// then
assertThat(result, equalTo(true));
assertThat(file.exists(), equalTo(true));
File configJarFile = TestHelper.getJarFile("/config.yml");
File configJarFile = TestHelper.getJarFile("/welcome.txt");
assertThat(file.length(), equalTo(configJarFile.length()));
}
@ -119,6 +121,13 @@ public class FileUtilsTest {
// Nothing happens
}
@Test
public void shouldGetResourceFromJar() {
// given / when / then
assertThat(FileUtils.getResourceFromJar("config.yml"), not(nullValue()));
assertThat(FileUtils.getResourceFromJar("does-not-exist"), nullValue());
}
@Test
public void shouldConstructPath() {
// given/when

View File

@ -12,7 +12,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.stream.Collectors;
@ -29,11 +28,6 @@ public class CheckTestMocks implements AutoToolTask {
return "checkTestMocks";
}
@Override
public void execute(Scanner scanner) {
executeDefault();
}
@Override
public void executeDefault() {
ClassCollector collector = new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, TestHelper.PROJECT_ROOT);

View File

@ -1,26 +1,20 @@
package tools.docs;
import com.google.common.collect.ImmutableSet;
import tools.docs.commands.CommandPageCreater;
import tools.docs.hashmethods.HashAlgorithmsDescriptionTask;
import tools.docs.permissions.PermissionsListWriter;
import tools.docs.translations.TranslationPageGenerator;
import fr.xephi.authme.ClassCollector;
import fr.xephi.authme.TestHelper;
import tools.utils.AutoToolTask;
import tools.utils.ToolTask;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* Task that runs all tasks which update files in the docs folder.
*/
public class UpdateDocsTask implements AutoToolTask {
private static final Set<Class<? extends ToolTask>> TASKS = ImmutableSet
.of(CommandPageCreater.class, HashAlgorithmsDescriptionTask.class,
PermissionsListWriter.class, TranslationPageGenerator.class);
@Override
public String getTaskName() {
return "updateDocs";
@ -40,19 +34,18 @@ public class UpdateDocsTask implements AutoToolTask {
});
}
private static ToolTask instantiateTask(Class<? extends ToolTask> clazz) {
try {
return clazz.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new UnsupportedOperationException("Could not instantiate task class '" + clazz + "'", e);
}
}
private static void executeTasks(Consumer<ToolTask> taskRunner) {
for (Class<? extends ToolTask> taskClass : TASKS) {
ToolTask task = instantiateTask(taskClass);
private void executeTasks(Consumer<ToolTask> taskRunner) {
for (ToolTask task : getDocTasks()) {
System.out.println("\nRunning " + task.getTaskName() + "\n-------------------");
taskRunner.accept(task);
}
}
private List<ToolTask> getDocTasks() {
ClassCollector classCollector =
new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, "tools/docs");
return classCollector.getInstancesOfType(ToolTask.class).stream()
.filter(task -> task.getClass() != getClass())
.collect(Collectors.toList());
}
}

View File

@ -6,14 +6,12 @@ import fr.xephi.authme.command.CommandInitializer;
import fr.xephi.authme.command.CommandUtils;
import fr.xephi.authme.permission.PermissionNode;
import tools.utils.AutoToolTask;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import tools.utils.TagValue.NestedTagValue;
import tools.utils.TagValueHolder;
import tools.utils.ToolsConstants;
import java.util.Collection;
import java.util.Scanner;
import java.util.Set;
public class CommandPageCreater implements AutoToolTask {
@ -24,19 +22,14 @@ public class CommandPageCreater implements AutoToolTask {
return "createCommandPage";
}
@Override
public void execute(Scanner scanner) {
executeDefault();
}
@Override
public void executeDefault() {
CommandInitializer commandInitializer = new CommandInitializer();
final Set<CommandDescription> baseCommands = commandInitializer.getCommands();
final Collection<CommandDescription> baseCommands = commandInitializer.getCommands();
NestedTagValue commandTags = new NestedTagValue();
addCommandsInfo(commandTags, baseCommands);
FileUtils.generateFileFromTemplate(
FileIoUtils.generateFileFromTemplate(
ToolsConstants.TOOLS_SOURCE_ROOT + "docs/commands/commands.tpl.md",
OUTPUT_FILE,
TagValueHolder.create().put("commands", commandTags));

View File

@ -1,5 +1,5 @@
<!-- {gen_warning} -->
<!-- File auto-generated on {gen_date}. See commands/commands.tpl.md -->
<!-- File auto-generated on {gen_date}. See docs/commands/commands.tpl.md -->
## AuthMe Commands
You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >`

View File

@ -0,0 +1,49 @@
package tools.docs.config;
import com.github.authme.configme.SettingsManager;
import com.github.authme.configme.resource.YamlFileResource;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import fr.xephi.authme.util.FileUtils;
import tools.utils.AutoToolTask;
import tools.utils.FileIoUtils;
import tools.utils.TagValueHolder;
import tools.utils.ToolsConstants;
import java.io.File;
import java.io.IOException;
/**
* Task for updating the config docs page.
*/
public class UpdateConfigPageTask implements AutoToolTask {
private static final String TEMPLATE_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/config/config.tpl.md";
private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "config.md";
@Override
public String getTaskName() {
return "updateConfigPage";
}
@Override
public void executeDefault() {
File config = null;
try {
// Create empty temporary .yml file and save the config to it
config = File.createTempFile("authme-config-", ".yml");
SettingsManager settingsManager = new SettingsManager(
new YamlFileResource(config), null, AuthMeSettingsRetriever.buildConfigurationData());
settingsManager.save();
// Get the contents and generate template file
TagValueHolder tagValueHolder = TagValueHolder.create()
.put("config", FileIoUtils.readFromFile(config.toPath()));
FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, OUTPUT_FILE, tagValueHolder);
System.out.println("Wrote to '" + OUTPUT_FILE + "'");
} catch (IOException e) {
throw new IllegalStateException(e);
} finally {
FileUtils.delete(config);
}
}
}

View File

@ -0,0 +1,16 @@
<!-- {gen_warning} -->
<!-- File auto-generated on {gen_date}. See docs/config/config.tpl.md -->
## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
with which you can configure various settings. This following is the initial contents of
the generated config.yml file.
```yml
{config}
```
To change settings on a running server, save your changes to config.yml and use
`/authme reload`.
{gen_footer}

View File

@ -2,13 +2,12 @@ package tools.docs.hashmethods;
import fr.xephi.authme.security.HashAlgorithm;
import tools.utils.AutoToolTask;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import tools.utils.TagValue.NestedTagValue;
import tools.utils.TagValueHolder;
import tools.utils.ToolsConstants;
import java.util.Map;
import java.util.Scanner;
/**
* Task for generating the markdown page describing the AuthMe hash algorithms.
@ -20,11 +19,6 @@ public class HashAlgorithmsDescriptionTask implements AutoToolTask {
private static final String CUR_FOLDER = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/hashmethods/";
private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "hash_algorithms.md";
@Override
public void execute(Scanner scanner) {
executeDefault();
}
@Override
public void executeDefault() {
// Gather info and construct a row for each method
@ -34,7 +28,8 @@ public class HashAlgorithmsDescriptionTask implements AutoToolTask {
// Write to the docs file
TagValueHolder tags = TagValueHolder.create().put("algorithms", methodRows);
FileUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags);
FileIoUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags);
System.out.println("Wrote to '" + OUTPUT_FILE + "'");
}
private static NestedTagValue constructMethodRows(Map<HashAlgorithm, MethodDescription> descriptions) {

View File

@ -1,5 +1,5 @@
<!-- {gen_warning} -->
<!-- File auto-generated on {gen_date}. See hashmethods/hash_algorithms.tpl.md -->
<!-- File auto-generated on {gen_date}. See docs/hashmethods/hash_algorithms.tpl.md -->
## Hash Algorithms
AuthMe supports the following hash algorithms for storing your passwords safely.

View File

@ -2,15 +2,17 @@ package tools.docs.permissions;
import fr.xephi.authme.ClassCollector;
import fr.xephi.authme.permission.PermissionNode;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import tools.utils.ToolsConstants;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Gatherer to generate up-to-date lists of the AuthMe permission nodes.
@ -27,6 +29,11 @@ public class PermissionNodesGatherer {
+ "(.*?)\\s+\\*/" // Capture everything until we encounter '*/'
+ "\\s+([A-Z_]+)\\("); // Match the enum name (e.g. 'LOGIN'), until before the first '('
/**
* List of all enum classes that implement the {@link PermissionNode} interface.
*/
private List<Class<? extends PermissionNode>> permissionClasses;
/**
* Return a sorted collection of all permission nodes, including its JavaDoc description.
*
@ -39,14 +46,27 @@ public class PermissionNodesGatherer {
result.put("authme.player.*", "Permission to use all player (non-admin) commands.");
result.put("authme.player.email", "Grants all email permissions.");
new ClassCollector(ToolsConstants.MAIN_SOURCE_ROOT, "")
.collectClasses(PermissionNode.class)
.stream()
.filter(Class::isEnum)
.forEach(clz -> addDescriptionsForClass((Class<T>) clz, result));
getPermissionClasses().forEach(clz -> addDescriptionsForClass((Class<T>) clz, result));
return result;
}
/**
* Return all enum classes implementing the PermissionNode interface.
*
* @return all permission node enums
*/
public List<Class<? extends PermissionNode>> getPermissionClasses() {
if (permissionClasses == null) {
ClassCollector classCollector = new ClassCollector(ToolsConstants.MAIN_SOURCE_ROOT, "");
permissionClasses = classCollector
.collectClasses(PermissionNode.class)
.stream()
.filter(Class::isEnum)
.collect(Collectors.toList());
}
return permissionClasses;
}
private <T extends Enum<T> & PermissionNode> void addDescriptionsForClass(Class<T> clazz,
Map<String, String> descriptions) {
String classSource = getSourceForClass(clazz);
@ -83,7 +103,7 @@ public class PermissionNodesGatherer {
*/
private static <T extends Enum<T> & PermissionNode> String getSourceForClass(Class<T> clazz) {
String classFile = ToolsConstants.MAIN_SOURCE_ROOT + clazz.getName().replace(".", "/") + ".java";
return FileUtils.readFromFile(classFile);
return FileIoUtils.readFromFile(classFile);
}
}

View File

@ -1,13 +1,12 @@
package tools.docs.permissions;
import tools.utils.AutoToolTask;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import tools.utils.TagValue.NestedTagValue;
import tools.utils.TagValueHolder;
import tools.utils.ToolsConstants;
import java.util.Map;
import java.util.Scanner;
/**
* Task responsible for formatting a permissions node list and
@ -23,23 +22,13 @@ public class PermissionsListWriter implements AutoToolTask {
return "writePermissionsList";
}
@Override
public void execute(Scanner scanner) {
generateAndWriteFile();
}
@Override
public void executeDefault() {
generateAndWriteFile();
}
private static void generateAndWriteFile() {
final NestedTagValue permissionsTagValue = generatePermissionsList();
TagValueHolder tags = TagValueHolder.create().put("nodes", permissionsTagValue);
FileUtils.generateFileFromTemplate(TEMPLATE_FILE, PERMISSIONS_OUTPUT_FILE, tags);
FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, PERMISSIONS_OUTPUT_FILE, tags);
System.out.println("Wrote to '" + PERMISSIONS_OUTPUT_FILE + "'");
System.out.println("Before committing, please verify the output!");
}
private static NestedTagValue generatePermissionsList() {

View File

@ -1,5 +1,5 @@
<!-- {gen_warning} -->
<!-- File auto-generated on {gen_date}. See permissions/permission_nodes.tpl.md -->
<!-- File auto-generated on {gen_date}. See docs/permissions/permission_nodes.tpl.md -->
## AuthMe Permission Nodes
The following are the permission nodes that are currently supported by the latest dev builds.

View File

@ -3,14 +3,13 @@ package tools.docs.translations;
import com.google.common.collect.ImmutableMap;
import tools.docs.translations.TranslationsGatherer.TranslationInfo;
import tools.utils.AutoToolTask;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import tools.utils.TagValue.NestedTagValue;
import tools.utils.TagValueHolder;
import tools.utils.ToolsConstants;
import java.util.Arrays;
import java.util.Map;
import java.util.Scanner;
import java.util.stream.Collectors;
import static com.google.common.base.Objects.firstNonNull;
@ -42,11 +41,6 @@ public class TranslationPageGenerator implements AutoToolTask {
return "updateTranslations";
}
@Override
public void execute(Scanner scanner) {
executeDefault();
}
@Override
public void executeDefault() {
NestedTagValue translationValuesHolder = new NestedTagValue();
@ -63,7 +57,8 @@ public class TranslationPageGenerator implements AutoToolTask {
}
TagValueHolder tags = TagValueHolder.create().put("languages", translationValuesHolder);
FileUtils.generateFileFromTemplate(TEMPLATE_FILE, DOCS_PAGE, tags);
FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, DOCS_PAGE, tags);
System.out.println("Wrote to '" + DOCS_PAGE + "'");
}
/**

View File

@ -1,5 +1,5 @@
<!-- {gen_warning} -->
<!-- File auto-generated on {gen_date}. See translations/translations.tpl.md -->
<!-- File auto-generated on {gen_date}. See docs/translations/translations.tpl.md -->
# AuthMe Translations
The following translations are available in AuthMe. Set `messagesLanguage` to the language code

View File

@ -0,0 +1,176 @@
package tools.filegeneration;
import com.google.common.collect.ImmutableMap;
import fr.xephi.authme.command.CommandDescription;
import fr.xephi.authme.command.CommandInitializer;
import fr.xephi.authme.command.CommandUtils;
import fr.xephi.authme.permission.DefaultPermission;
import fr.xephi.authme.permission.PermissionNode;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import tools.docs.permissions.PermissionNodesGatherer;
import tools.utils.AutoToolTask;
import tools.utils.FileIoUtils;
import tools.utils.ToolsConstants;
import java.io.StringReader;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
/**
* Generates the command and permission section of plugin.yml.
*/
public class GeneratePluginYml implements AutoToolTask {
private static final String PLUGIN_YML_FILE = ToolsConstants.MAIN_RESOURCES_ROOT + "plugin.yml";
private static final Map<String, String> WILDCARD_PERMISSIONS = ImmutableMap.of(
"authme.player.*", "Gives access to all player commands",
"authme.admin.*", "Gives access to all admin commands",
"authme.player.email", "Gives access to all email commands");
private List<PermissionNode> permissionNodes;
private String pluginYmlStart;
@Override
public void executeDefault() {
FileConfiguration configuration = loadPartialPluginYmlFile();
configuration.set("commands", generateCommands());
configuration.set("permissions", generatePermissions());
FileIoUtils.writeToFile(PLUGIN_YML_FILE,
pluginYmlStart + "\n" + configuration.saveToString());
}
@Override
public String getTaskName() {
return "generatePluginYml";
}
/**
* Because some parts above the commands section have placeholders that aren't valid YAML, we need
* to split the contents into an upper part that we ignore and a lower part we load as YAML. When
* saving we prepend the YAML export with the stripped off part of the file again.
*
* @return file configuration with the lower part of the plugin.yml file
*/
private FileConfiguration loadPartialPluginYmlFile() {
List<String> pluginYmlLines = FileIoUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE));
int lineNr = 0;
for (String line : pluginYmlLines) {
if (line.equals("commands:")) {
break;
}
++lineNr;
}
if (lineNr == pluginYmlLines.size()) {
throw new IllegalStateException("Could not find line starting 'commands:' section");
}
pluginYmlStart = String.join("\n", pluginYmlLines.subList(0, lineNr));
String yamlContents = String.join("\n", pluginYmlLines.subList(lineNr, pluginYmlLines.size()));
return YamlConfiguration.loadConfiguration(new StringReader(yamlContents));
}
private static Map<String, Object> generateCommands() {
Collection<CommandDescription> commands = new CommandInitializer().getCommands();
Map<String, Object> entries = new LinkedHashMap<>();
for (CommandDescription command : commands) {
entries.put(command.getLabels().get(0), buildCommandEntry(command));
}
return entries;
}
private Map<String, Object> generatePermissions() {
PermissionNodesGatherer gatherer = new PermissionNodesGatherer();
Map<String, String> permissionDescriptions = gatherer.gatherNodesWithJavaDoc();
permissionNodes = gatherer.getPermissionClasses().stream()
// Note ljacqu 20161023: The compiler fails if we use method references below
.map(clz -> clz.getEnumConstants())
.flatMap((PermissionNode[] nodes) -> Arrays.stream(nodes))
.collect(Collectors.toList());
Map<String, Object> descriptions = new TreeMap<>();
for (PermissionNode node : permissionNodes) {
descriptions.put(node.getNode(), buildPermissionEntry(node, permissionDescriptions.get(node.getNode())));
}
addWildcardPermissions(descriptions);
return descriptions;
}
private void addWildcardPermissions(Map<String, Object> permissions) {
for (Map.Entry<String, String> entry : WILDCARD_PERMISSIONS.entrySet()) {
permissions.put(entry.getKey(),
buildWildcardPermissionEntry(entry.getValue(), gatherChildren(entry.getKey())));
}
}
private Map<String, Boolean> gatherChildren(String parentNode) {
String parentPath = parentNode.replaceAll("\\.\\*$", "");
Map<String, Boolean> children = new TreeMap<>();
for (PermissionNode node : permissionNodes) {
if (node.getNode().startsWith(parentPath)) {
children.put(node.getNode(), Boolean.TRUE);
}
}
return children;
}
private static Map<String, Object> buildCommandEntry(CommandDescription command) {
if (command.getLabels().size() > 1) {
return ImmutableMap.of(
"description", command.getDescription(),
"usage", buildUsage(command),
"aliases", command.getLabels().subList(1, command.getLabels().size()));
} else {
return ImmutableMap.of(
"description", command.getDescription(),
"usage", buildUsage(command));
}
}
private static String buildUsage(CommandDescription command) {
if (!command.getArguments().isEmpty()) {
return CommandUtils.buildSyntax(command);
}
final String commandStart = "/" + command.getLabels().get(0);
String usage = commandStart + " " + command.getChildren()
.stream()
.filter(cmd -> !cmd.getLabels().contains("help"))
.map(cmd -> cmd.getLabels().get(0))
.collect(Collectors.joining("|"));
return usage.trim();
}
private static Map<String, Object> buildPermissionEntry(PermissionNode permissionNode, String description) {
return ImmutableMap.of(
"description", description,
"default", convertDefaultPermission(permissionNode.getDefaultPermission()));
}
private static Map<String, Object> buildWildcardPermissionEntry(String description, Map<String, Boolean> children) {
return ImmutableMap.of(
"description", description,
"children", children);
}
private static Object convertDefaultPermission(DefaultPermission defaultPermission) {
switch (defaultPermission) {
// Returning true/false as booleans will make SnakeYAML avoid using quotes
case ALLOWED: return true;
case NOT_ALLOWED: return false;
case OP_ONLY: return "op";
default:
throw new IllegalArgumentException("Unknown default permission '" + defaultPermission + "'");
}
}
}

View File

@ -0,0 +1,171 @@
package tools.helptranslation;
import com.google.common.collect.Sets;
import de.bananaco.bpermissions.imp.YamlConfiguration;
import fr.xephi.authme.command.CommandDescription;
import fr.xephi.authme.command.CommandInitializer;
import fr.xephi.authme.command.CommandUtils;
import fr.xephi.authme.command.help.HelpMessage;
import fr.xephi.authme.command.help.HelpSection;
import org.bukkit.configuration.MemorySection;
import org.bukkit.configuration.file.FileConfiguration;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.google.common.collect.Lists.newArrayList;
/**
* Verifies a help messages translation.
*/
public class HelpTranslationVerifier {
private final FileConfiguration configuration;
// missing and unknown HelpSection and HelpMessage entries
private final List<String> missingSections = new ArrayList<>();
private final List<String> unknownSections = new ArrayList<>();
// missing and unknown command entries
private final List<String> missingCommands = new ArrayList<>();
private final List<String> unknownCommands = new ArrayList<>();
public HelpTranslationVerifier(File translation) {
this.configuration = YamlConfiguration.loadConfiguration(translation);
checkFile();
}
private void checkFile() {
checkHelpSections();
checkCommands();
}
public List<String> getMissingSections() {
return missingSections;
}
public List<String> getUnknownSections() {
return unknownSections;
}
public List<String> getMissingCommands() {
// All entries start with "command.", so remove that
return missingCommands.stream()
.map(s -> s.substring(9)).collect(Collectors.toList());
}
public List<String> getUnknownCommands() {
// All entries start with "command.", so remove that
return unknownCommands.stream()
.map(s -> s.substring(9)).collect(Collectors.toList());
}
/**
* Verifies that the file has the expected entries for {@link HelpSection} and {@link HelpMessage}.
*/
private void checkHelpSections() {
Set<String> knownSections = Arrays.stream(HelpSection.values())
.map(HelpSection::getKey).collect(Collectors.toSet());
knownSections.addAll(Arrays.stream(HelpMessage.values()).map(HelpMessage::getKey).collect(Collectors.toSet()));
knownSections.addAll(Arrays.asList("common.defaultPermissions.notAllowed",
"common.defaultPermissions.opOnly", "common.defaultPermissions.allowed"));
Set<String> sectionKeys = getLeafKeys("section");
sectionKeys.addAll(getLeafKeys("common"));
if (sectionKeys.isEmpty()) {
missingSections.addAll(knownSections);
} else {
missingSections.addAll(Sets.difference(knownSections, sectionKeys));
unknownSections.addAll(Sets.difference(sectionKeys, knownSections));
}
}
/**
* Verifies that the file has the expected entries for AuthMe commands.
*/
private void checkCommands() {
Set<String> commandPaths = buildCommandPaths();
Set<String> existingKeys = getLeafKeys("commands");
if (existingKeys.isEmpty()) {
missingCommands.addAll(commandPaths); // commandPaths should be empty in this case
} else {
missingCommands.addAll(Sets.difference(commandPaths, existingKeys));
unknownCommands.addAll(Sets.difference(existingKeys, commandPaths));
}
}
private Set<String> buildCommandPaths() {
Set<String> commandPaths = new LinkedHashSet<>();
for (CommandDescription command : new CommandInitializer().getCommands()) {
commandPaths.addAll(getYamlPaths(command));
command.getChildren().forEach(child -> commandPaths.addAll(getYamlPaths(child)));
}
return commandPaths;
}
private List<String> getYamlPaths(CommandDescription command) {
// e.g. commands.authme.register
String commandPath = "commands." + CommandUtils.constructParentList(command).stream()
.map(cmd -> cmd.getLabels().get(0))
.collect(Collectors.joining("."));
// The entire command is not present, so just add it as a missing command and don't return any YAML path
if (!configuration.contains(commandPath)) {
missingCommands.add(commandPath);
return Collections.emptyList();
}
// Entries each command can have
List<String> paths = newArrayList(commandPath + ".description", commandPath + ".detailedDescription");
// Add argument entries that may exist
for (int argIndex = 1; argIndex <= command.getArguments().size(); ++argIndex) {
String argPath = String.format("%s.arg%d", commandPath, argIndex);
paths.add(argPath + ".label");
paths.add(argPath + ".description");
}
return paths;
}
/**
* Returns the leaf keys of the section at the given path of the file configuration.
*
* @param path the path whose leaf keys should be retrieved
* @return leaf keys of the memory section,
* empty set if the configuration does not have a memory section at the given path
*/
private Set<String> getLeafKeys(String path) {
if (!(configuration.get(path) instanceof MemorySection)) {
return Collections.emptySet();
}
MemorySection memorySection = (MemorySection) configuration.get(path);
// MemorySection#getKeys(true) returns all keys on all levels, e.g. if the configuration has
// 'commands.authme.register' then it also has 'commands.authme' and 'commands'. We can traverse each node and
// build its parents (e.g. for commands.authme.register.description: commands.authme.register, commands.authme,
// and commands, which we can remove from the collection since we know they are not a leaf.
Set<String> leafKeys = memorySection.getKeys(true);
Set<String> allKeys = new HashSet<>(leafKeys);
for (String key : allKeys) {
List<String> pathParts = Arrays.asList(key.split("\\."));
// We perform construction of parents & their removal in reverse order so we can build the lowest-level
// parent of a node first. As soon as the parent doesn't exist in the set already, we know we can continue
// with the next node since another node has already removed the concerned parents.
for (int i = pathParts.size() - 1; i > 0; --i) {
// e.g. for commands.authme.register -> i = {2, 1} => {commands.authme, commands}
String parentPath = String.join(".", pathParts.subList(0, i));
if (!leafKeys.remove(parentPath)) {
break;
}
}
}
return leafKeys.stream().map(leaf -> path + "." + leaf).collect(Collectors.toSet());
}
}

View File

@ -0,0 +1,72 @@
package tools.helptranslation;
import tools.utils.ToolTask;
import tools.utils.ToolsConstants;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Verifies the help translations for validity and completeness.
*/
public class VerifyHelpTranslations implements ToolTask {
private static final Pattern HELP_MESSAGE_PATTERN = Pattern.compile("help_[a-z]{2,7}\\.yml");
private static final String FOLDER = ToolsConstants.MAIN_RESOURCES_ROOT + "messages/";
@Override
public String getTaskName() {
return "verifyHelpTranslations";
}
@Override
public void execute(Scanner scanner) {
System.out.println("Check specific language file?");
System.out.println("Enter the language code for a specific file (e.g. 'it' for help_it.yml)");
System.out.println("Empty line will check all files in the resources messages folder (default)");
String language = scanner.nextLine();
if (language.isEmpty()) {
getHelpTranslations().forEach(this::processFile);
} else {
processFile(new File(FOLDER, "help_" + language + ".yml"));
}
}
private void processFile(File file) {
System.out.println("Checking '" + file.getName() + "'");
HelpTranslationVerifier verifier = new HelpTranslationVerifier(file);
// Check and output errors
if (!verifier.getMissingSections().isEmpty()) {
System.out.println("Missing sections: " + String.join(", ", verifier.getMissingSections()));
}
if (!verifier.getUnknownSections().isEmpty()) {
System.out.println("Unknown sections: " + String.join(", ", verifier.getUnknownSections()));
}
if (!verifier.getMissingCommands().isEmpty()) {
System.out.println("Missing command entries: " + String.join(", ", verifier.getMissingCommands()));
}
if (!verifier.getUnknownCommands().isEmpty()) {
System.out.println("Unknown command entries: " + String.join(", ", verifier.getUnknownCommands()));
}
}
private static List<File> getHelpTranslations() {
File[] files = new File(FOLDER).listFiles();
if (files == null) {
throw new IllegalStateException("Could not get files from '" + FOLDER + "'");
}
List<File> helpFiles = Arrays.stream(files)
.filter(file -> HELP_MESSAGE_PATTERN.matcher(file.getName()).matches())
.collect(Collectors.toList());
if (helpFiles.isEmpty()) {
throw new IllegalStateException("Could not get any matching files!");
}
return helpFiles;
}
}

View File

@ -8,7 +8,7 @@ import com.google.common.collect.Multimap;
import fr.xephi.authme.message.MessageKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import java.io.File;
import java.util.ArrayList;
@ -104,7 +104,7 @@ public class MessageFileVerifier {
* @param defaultMessages The collection of default messages
*/
public void addMissingKeys(FileConfiguration defaultMessages) {
final List<String> fileLines = FileUtils.readLinesFromFile(messagesFile.toPath());
final List<String> fileLines = FileIoUtils.readLinesFromFile(messagesFile.toPath());
List<MissingKey> keysToAdd = new ArrayList<>();
for (MissingKey entry : missingKeys) {
@ -135,7 +135,7 @@ public class MessageFileVerifier {
addCommentForMissingTags(fileLines, key, entry.getValue());
}
FileUtils.writeToFile(messagesFile.toPath(), String.join("\n", fileLines));
FileIoUtils.writeToFile(messagesFile.toPath(), String.join("\n", fileLines));
}
/**

View File

@ -7,7 +7,7 @@ import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import tools.messages.MessageFileVerifier;
import tools.messages.VerifyMessagesTask;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import tools.utils.ToolTask;
import tools.utils.ToolsConstants;
@ -108,9 +108,9 @@ public class ImportMessagesTask implements ToolTask {
* @param file The file whose to-do comments should be removed
*/
private static void removeAllTodoComments(String file) {
String contents = FileUtils.readFromFile(file);
String contents = FileIoUtils.readFromFile(file);
String regex = "^# TODO .*$";
contents = Pattern.compile(regex, Pattern.MULTILINE).matcher(contents).replaceAll("");
FileUtils.writeToFile(file, contents);
FileIoUtils.writeToFile(file, contents);
}
}

View File

@ -2,7 +2,7 @@ package tools.messages.translation;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import tools.utils.FileUtils;
import tools.utils.FileIoUtils;
import tools.utils.ToolsConstants;
import java.io.File;
@ -31,7 +31,7 @@ public class WriteAllExportsTask extends ExportMessagesTask {
for (File file : messageFiles) {
String code = file.getName().substring("messages_".length(), file.getName().length() - ".yml".length());
String json = convertToJson(code, defaultMessages, YamlConfiguration.loadConfiguration(file));
FileUtils.writeToFile(OUTPUT_FOLDER + "messages_" + code + ".json", json);
FileIoUtils.writeToFile(OUTPUT_FOLDER + "messages_" + code + ".json", json);
}
}
}

View File

@ -1,5 +1,7 @@
package tools.utils;
import java.util.Scanner;
/**
* Interface for tasks that can be run automatically, i.e. without any user input.
*/
@ -10,4 +12,9 @@ public interface AutoToolTask extends ToolTask {
*/
void executeDefault();
@Override
default void execute(Scanner scanner) {
executeDefault();
}
}

View File

@ -9,11 +9,11 @@ import java.nio.file.StandardOpenOption;
import java.util.List;
/**
* Utility class for reading from and writing to files.
* Utility class for I/O operations on files.
*/
public final class FileUtils {
public final class FileIoUtils {
private FileUtils() {
private FileIoUtils() {
}
public static void generateFileFromTemplate(String templateFile, String destinationFile, TagValueHolder tags) {
@ -43,8 +43,12 @@ public final class FileUtils {
}
public static String readFromFile(String file) {
return readFromFile(Paths.get(file));
}
public static String readFromFile(Path file) {
try {
return new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
} catch (IOException e) {
throw new UnsupportedOperationException("Could not read from file '" + file + "'", e);
}

View File

@ -9,9 +9,6 @@ public final class ToolsConstants {
public static final String MAIN_RESOURCES_ROOT = "src/main/resources/";
// Add specific `fr.xephi.authme` package as not to include the tool tasks in the `tools` package
public static final String TEST_SOURCE_ROOT = "src/test/java/fr/xephi/authme";
public static final String TOOLS_SOURCE_ROOT = "src/test/java/tools/";
public static final String DOCS_FOLDER = "docs/";