Merge branch 'master' of https://github.com/AuthMe/AuthMeReloaded into 1141-optional-additional-2fa-auth

# Conflicts:
#	src/main/java/fr/xephi/authme/datasource/MySQL.java
This commit is contained in:
ljacqu 2018-03-19 22:32:16 +01:00
commit af6bee59bd
82 changed files with 831 additions and 326 deletions

115
README.md
View File

@ -1,53 +1,16 @@
<p align="center"><img src="http://i63.tinypic.com/rtp06o.png"></p> # AuthMeReloaded
<p align="center"><strong>The most used authentication plugin for the Spigot and derivates!</strong></p> **"The best authentication plugin for the Bukkit modding API!"**
<hr> <img src="http://i63.tinypic.com/rtp06o.png" alt="AuthMeLogo" style="width: 250px;"/>
##### Links and Contacts: | Type | Badges |
|-------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **General:** | ![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded?category=code) ![](https://tokei.rs/b1/github/AuthMe/AuthMeReloaded?category=files) |
| **Code quality:** | [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded) [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) |
| **Jenkins CI:** | [![Jenkins Status](https://img.shields.io/website-up-down-green-red/http/shields.io.svg?label=ci.codemc.org)](https://ci.codemc.org/) [![Build Status](https://ci.codemc.org/buildStatus/icon?job=AuthMe/AuthMeReloaded)](https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded) ![Build Tests](https://img.shields.io/jenkins/t/https/ci.codemc.org/job/AuthMe/job/AuthMeReloaded.svg) |
| **Other CIs:** | [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded) |
| **Dependencies:** | [![Dependency Status](https://gemnasium.com/badges/github.com/AuthMe/AuthMeReloaded.svg)](https://gemnasium.com/github.com/AuthMe/AuthMeReloaded) |
- Support: ## Description
- [GitHub issue tracker](https://github.com/AuthMe/AuthMeReloaded/issues)
- [BukkitDev page](https://dev.bukkit.org/projects/authme-reloaded)
- [Spigot page](https://www.spigotmc.org/resources/authmereloaded.6269/)
- [Discord](https://discord.gg/Vn9eCyE)
- CI Services:
- [Official Jenkins](http://ci.xephi.fr/job/AuthMeReloaded) (**DEVELOPMENT BUILDS**)
- CircleCI: [![CircleCI](https://circleci.com/gh/AuthMe/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/AuthMe/AuthMeReloaded)
- Project status:
- Test coverage: [![Coverage status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master)
- Code climate: [![Code Climate](https://codeclimate.com/github/AuthMe/AuthMeReloaded/badges/gpa.svg)](https://codeclimate.com/github/AuthMe/AuthMeReloaded)
- Development resources:
- <a href="http://ci.xephi.fr/job/AuthMeReloaded/javadoc/">JavaDocs</a>
- <a href="http://ci.xephi.fr/plugin/repository/everything/">Maven Repository</a>
- Statistics:
- bStats: [AuthMe on bstats.org](https://bstats.org/plugin/bukkit/AuthMe)
<hr>
##### Compiling requirements:
>- JDK 1.8
>- Maven
>- Git/Github (Optional)
##### How to compile the project:
>- Clone the project with Git/Github
>- Execute command "mvn clean package"
##### Running requirements:
>- Java 1.8
>- TacoSpigot, PaperSpigot or Spigot (1.7.10, 1.8.X, 1.9.X, 1.10.X, 1.11.X, 1.12.X)<br>
(In case you use Thermos, Cauldron or similar, you have to update the SpecialSource library to support Java 8 plugins.
HowTo: https://github.com/games647/FastLogin/issues/111#issuecomment-272331347)
>- ProtocolLib (optional, required by some features)
<hr>
### Plugin Description:
##### "The best authentication plugin for the Bukkit/Spigot API!"
Prevent username stealing on your server!<br> Prevent username stealing on your server!<br>
Use it to secure your Offline mode server or to increase your Online mode server's protection! Use it to secure your Offline mode server or to increase your Online mode server's protection!
@ -64,7 +27,7 @@ You can also create your own translation file and, if you want, you can share it
<ul> <ul>
<li><strong>E-Mail Recovery System !!!</strong></li> <li><strong>E-Mail Recovery System !!!</strong></li>
<li>Username spoofing protection.</li> <li>Username spoofing protection.</li>
<li>Countries Whitelist/Blacklist! <a href="http://dev.maxmind.com/geoip/legacy/codes/iso3166/">(country codes)</a></li> <li>Countries Whitelist/Blacklist! <a href="https://dev.maxmind.com/geoip/legacy/codes/iso3166/">(country codes)</a></li>
<li><strong>Built-in AntiBot System!</strong></li> <li><strong>Built-in AntiBot System!</strong></li>
<li><strong>ForceLogin Feature: Admins can login with all account via console command!</strong></li> <li><strong>ForceLogin Feature: Admins can login with all account via console command!</strong></li>
<li><strong>Avoid the "Logged in from another location" message!</strong></li> <li><strong>Avoid the "Logged in from another location" message!</strong></li>
@ -116,15 +79,51 @@ You can also create your own translation file and, if you want, you can share it
- [How to convert from Rakamak](https://dev.bukkit.org/projects/authme-reloaded/pages/how-to-import-database-from-rakamak) - [How to convert from Rakamak](https://dev.bukkit.org/projects/authme-reloaded/pages/how-to-import-database-from-rakamak)
- Convert between database types (e.g. SQLite to MySQL): /authme converter - Convert between database types (e.g. SQLite to MySQL): /authme converter
<hr>
##### Sponsor ## Links and Contacts
[GameHosting.it](http://www.gamehosting.it) is leader in Italy as Game Server Provider. With its own DataCenter offers Anti-DDoS solutions at affordable prices. Game Server of Minecraft based on Multicraft are equipped with the latest technology in hardware.
##### Credits - **Support:**
<p>Contributors: <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Development-team">developers</a>, <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Translators">translators</a> - [GitHub issue tracker](https://github.com/AuthMe/AuthMeReloaded/issues)
<p>Credit for old version of the plugin to: d4rkwarriors, fabe1337, Whoami2 and pomo4ka</p> - [BukkitDev page](https://dev.bukkit.org/projects/authme-reloaded)
<p>Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex</p> - [Spigot page](https://www.spigotmc.org/resources/authmereloaded.6269/)
- [Discord](https://discord.gg/Vn9eCyE)
##### GeoIP License - **Dev resources:**
This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com - <a href="https://ci.codemc.org/job/AuthMe/job/AuthMeReloaded/javadoc/">JavaDocs</a>
- <a href="http://repo.codemc.org/repository/maven-public/">Maven Repository</a>
- **Statistics:**
![Graph](https://bstats.org/signatures/bukkit/AuthMe.svg)
## Requirements
##### Compiling requirements:
>- JDK 1.8
>- Maven
>- Git/Github (Optional)
##### How to compile the project:
>- Clone the project with Git/Github
>- Execute command "mvn clean package"
##### Running requirements:
>- Java 1.8
>- TacoSpigot, PaperSpigot or Spigot (1.7.10, 1.8.X, 1.9.X, 1.10.X, 1.11.X, 1.12.X)<br>
(In case you use Thermos, Cauldron or similar, you have to update the SpecialSource library to support Java 8 plugins.
HowTo: https://github.com/games647/FastLogin/issues/111#issuecomment-272331347)
>- ProtocolLib (optional, required by some features)
## Credits
##### Sponsor:
[FastVM.io](https://fastvm.io) is leader in VPS hosting solutions. With its own DataCenter offers Anti-DDoS solutions at affordable prices.
##### Contributors:
Team members: <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Development-team">developers</a>, <a href="https://github.com/AuthMe/AuthMeReloaded/wiki/Translators">translators</a>
Credits for the old version of the plugin: d4rkwarriors, fabe1337, Whoami2 and pomo4ka
Thanks also to: AS1LV3RN1NJA, Hoeze and eprimex
##### GeoIP License:
This product uses data from the GeoLite API created by MaxMind, available at https://www.maxmind.com

62
pom.xml
View File

@ -189,10 +189,15 @@
<version>3.0.0</version> <version>3.0.0</version>
<executions> <executions>
<execution> <execution>
<id>attach-javadocs</id> <id>attach-javadoc</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
<execution>
<id>aggregate-javadoc</id>
<goals> <goals>
<goal>aggregate</goal> <goal>aggregate</goal>
<goal>jar</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
@ -243,7 +248,7 @@
<!-- Include all google libraries, because they are not available before 1.12 --> <!-- Include all google libraries, because they are not available before 1.12 -->
<relocation> <relocation>
<pattern>com.google</pattern> <pattern>com.google</pattern>
<shadedPattern>fr.xephi.authme.libs.google</shadedPattern> <shadedPattern>fr.xephi.authme.libs.com.google</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>ch.jalu.injector</pattern> <pattern>ch.jalu.injector</pattern>
@ -251,23 +256,27 @@
</relocation> </relocation>
<relocation> <relocation>
<pattern>ch.jalu.configme</pattern> <pattern>ch.jalu.configme</pattern>
<shadedPattern>fr.xephi.authme.libs.jalu.configme</shadedPattern> <shadedPattern>fr.xephi.authme.libs.ch.jalu.configme</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>com.zaxxer.hikari</pattern> <pattern>com.zaxxer.hikari</pattern>
<shadedPattern>fr.xephi.authme.libs.zaxxer.hikari</shadedPattern> <shadedPattern>fr.xephi.authme.libs.com.zaxxer.hikari</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>org.slf4j</pattern> <pattern>org.slf4j</pattern>
<shadedPattern>fr.xephi.authme.libs.slf4j.slf4j</shadedPattern> <shadedPattern>fr.xephi.authme.libs.org.slf4j</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>com.maxmind.geoip</pattern> <pattern>com.maxmind.db</pattern>
<shadedPattern>fr.xephi.authme.libs.maxmind.geoip</shadedPattern> <shadedPattern>fr.xephi.authme.libs.com.maxmind.db</shadedPattern>
</relocation>
<relocation>
<pattern>com.ice.tar</pattern>
<shadedPattern>fr.xephi.authme.libs.com.icetar.tar</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>net.ricecode.similarity</pattern> <pattern>net.ricecode.similarity</pattern>
<shadedPattern>fr.xephi.authme.libs.ricecode.similarity</shadedPattern> <shadedPattern>fr.xephi.authme.libs.ricecode.net.ricecode.similarity</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>de.rtner</pattern> <pattern>de.rtner</pattern>
@ -367,7 +376,6 @@
<groupId>ch.jalu</groupId> <groupId>ch.jalu</groupId>
<artifactId>injector</artifactId> <artifactId>injector</artifactId>
<version>1.0</version> <version>1.0</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -376,7 +384,6 @@
<groupId>net.ricecode</groupId> <groupId>net.ricecode</groupId>
<artifactId>string-similarity</artifactId> <artifactId>string-similarity</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -385,7 +392,6 @@
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.8.2</version> <version>2.8.2</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -394,16 +400,23 @@
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>24.0-jre</version> <version>24.0-jre</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- Maxmind GeoIp API --> <!-- MaxMind GEO IP with our modifications to use GSON in replacement of the big Jackson dependency -->
<!-- GSON is already included and therefore it reduces the file size in comparison to the original version -->
<dependency> <dependency>
<groupId>com.maxmind.geoip</groupId> <groupId>com.maxmind.db</groupId>
<artifactId>geoip-api</artifactId> <artifactId>maxmind-db-gson</artifactId>
<version>1.3.1</version> <version>2.0.2-SNAPSHOT</version>
<scope>compile</scope> <optional>true</optional>
</dependency>
<!-- Library for tar archives -->
<dependency>
<groupId>javatar</groupId>
<artifactId>javatar</artifactId>
<version>2.5</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -412,7 +425,6 @@
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-email</artifactId> <artifactId>commons-email</artifactId>
<version>1.5</version> <version>1.5</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -429,7 +441,6 @@
<groupId>com.zaxxer</groupId> <groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId> <artifactId>HikariCP</artifactId>
<version>2.7.8</version> <version>2.7.8</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -443,7 +454,6 @@
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId> <artifactId>slf4j-simple</artifactId>
<version>1.7.25</version> <version>1.7.25</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -452,7 +462,6 @@
<groupId>de.rtner</groupId> <groupId>de.rtner</groupId>
<artifactId>PBKDF2</artifactId> <artifactId>PBKDF2</artifactId>
<version>1.1.2</version> <version>1.1.2</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -460,8 +469,7 @@
<dependency> <dependency>
<groupId>de.mkammerer</groupId> <groupId>de.mkammerer</groupId>
<artifactId>argon2-jvm-nolibs</artifactId> <artifactId>argon2-jvm-nolibs</artifactId>
<version>2.3</version> <version>2.4</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -509,7 +517,6 @@
<groupId>ch.jalu</groupId> <groupId>ch.jalu</groupId>
<artifactId>configme</artifactId> <artifactId>configme</artifactId>
<version>0.4.1</version> <version>0.4.1</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -524,7 +531,6 @@
<groupId>org.bstats</groupId> <groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId> <artifactId>bstats-bukkit</artifactId>
<version>1.2</version> <version>1.2</version>
<scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
@ -532,7 +538,7 @@
<dependency> <dependency>
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-API</artifactId> <artifactId>ProtocolLib-API</artifactId>
<version>4.4.0-SNAPSHOT</version> <version>4.3.0</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@ -550,7 +556,7 @@
<dependency> <dependency>
<groupId>me.lucko.luckperms</groupId> <groupId>me.lucko.luckperms</groupId>
<artifactId>luckperms-api</artifactId> <artifactId>luckperms-api</artifactId>
<version>4.0</version> <version>4.1</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>

View File

@ -2,7 +2,9 @@ package fr.xephi.authme;
import ch.jalu.injector.Injector; import ch.jalu.injector.Injector;
import ch.jalu.injector.InjectorBuilder; import ch.jalu.injector.InjectorBuilder;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.api.NewAPI;
import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
@ -33,6 +35,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.task.CleanupTask; import fr.xephi.authme.task.CleanupTask;
import fr.xephi.authme.task.purge.PurgeService; import fr.xephi.authme.task.purge.PurgeService;
import fr.xephi.authme.util.ExceptionUtils; import fr.xephi.authme.util.ExceptionUtils;
import java.io.File;
import org.apache.commons.lang.SystemUtils; import org.apache.commons.lang.SystemUtils;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.Command; import org.bukkit.command.Command;
@ -43,8 +48,6 @@ import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader; import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import java.io.File;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE; import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.util.Utils.isClassLoaded; import static fr.xephi.authme.util.Utils.isClassLoaded;
@ -156,9 +159,9 @@ public class AuthMe extends JavaPlugin {
OnStartupTasks.sendMetrics(this, settings); OnStartupTasks.sendMetrics(this, settings);
// Sponsor messages // Sponsor messages
ConsoleLogger.info("Development builds are available on our jenkins, thanks to f14stelt."); ConsoleLogger.info("Development builds are available on our jenkins, thanks to FastVM.io");
ConsoleLogger.info("Do you want a good game server? Look at our sponsor GameHosting.it leader " ConsoleLogger.info("Do you want a good vps for your game server? Look at our sponsor FastVM.io leader "
+ "in Italy as Game Server Provider!"); + "as virtual server provider!");
// Successful message // Successful message
ConsoleLogger.info("AuthMe " + getPluginVersion() + " build n." + getPluginBuildNumber() ConsoleLogger.info("AuthMe " + getPluginVersion() + " build n." + getPluginBuildNumber()

View File

@ -74,7 +74,7 @@ public class RegisterAdminCommand implements ExecutableCommand {
final Player player = bukkitService.getPlayerExact(playerName); final Player player = bukkitService.getPlayerExact(playerName);
if (player != null) { if (player != null) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() ->
player.kickPlayer(commonService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER))); player.kickPlayer(commonService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)));
} }
}); });
} }

View File

@ -172,6 +172,11 @@ class MySqlDefaultChanger implements DebugSection {
+ sender.getName() + "'"); + sender.getName() + "'");
} }
/**
* Outputs the current definitions of all {@link Columns} which can be migrated.
*
* @param sender command sender to output the data to
*/
private void showColumnDetails(CommandSender sender) { private void showColumnDetails(CommandSender sender) {
sender.sendMessage(ChatColor.BLUE + "MySQL column details"); sender.sendMessage(ChatColor.BLUE + "MySQL column details");
final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE); final String tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);

View File

@ -97,7 +97,7 @@ public class TempbanManager implements SettingsDependent, HasCleanup {
if (isEnabled) { if (isEnabled) {
final String name = player.getName(); final String name = player.getName();
final String ip = PlayerUtils.getPlayerIp(player); final String ip = PlayerUtils.getPlayerIp(player);
final String reason = messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS); final String reason = messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS);
final Date expires = new Date(); final Date expires = new Date();
long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE); long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE);

View File

@ -49,9 +49,9 @@ class LimboPlayerTaskManager {
*/ */
void registerMessageTask(Player player, LimboPlayer limbo, boolean isRegistered) { void registerMessageTask(Player player, LimboPlayer limbo, boolean isRegistered) {
int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL); int interval = settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL);
MessageResult messageResult = getMessageKey(player.getName(), isRegistered); MessageResult result = getMessageKey(player.getName(), isRegistered);
if (interval > 0) { if (interval > 0) {
String[] joinMessage = messages.retrieveSingle(messageResult.messageKey, messageResult.args).split("\n"); String[] joinMessage = messages.retrieveSingle(player, result.messageKey, result.args).split("\n");
MessageTask messageTask = new MessageTask(player, joinMessage); MessageTask messageTask = new MessageTask(player, joinMessage);
bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND); bukkitService.runTaskTimer(messageTask, 2 * TICKS_PER_SECOND, interval * TICKS_PER_SECOND);
limbo.setMessageTask(messageTask); limbo.setMessageTask(messageTask);
@ -67,7 +67,7 @@ class LimboPlayerTaskManager {
void registerTimeoutTask(Player player, LimboPlayer limbo) { void registerTimeoutTask(Player player, LimboPlayer limbo) {
final int timeout = settings.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND; final int timeout = settings.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
if (timeout > 0) { if (timeout > 0) {
String message = messages.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR); String message = messages.retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR);
BukkitTask task = bukkitService.runTaskLater(new TimeoutTask(player, message, playerCache), timeout); BukkitTask task = bukkitService.runTaskLater(new TimeoutTask(player, message, playerCache), timeout);
limbo.setTimeoutTask(task); limbo.setTimeoutTask(task);
} }

View File

@ -28,6 +28,10 @@ import java.util.Set;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong; import static fr.xephi.authme.datasource.SqlDataSourceUtils.getNullableLong;
import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException; import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
/**
* MySQL data source.
*/
@SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore
public class MySQL implements DataSource { public class MySQL implements DataSource {
private boolean useSsl; private boolean useSsl;
@ -747,6 +751,13 @@ public class MySQL implements DataSource {
return false; return false;
} }
/**
* Creates a {@link PlayerAuth} object with the data from the provided result set.
*
* @param row the result set to read from
* @return generated player auth object with the data from the result set
* @throws SQLException .
*/
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT); String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT);
int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP); int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP);

View File

@ -28,6 +28,7 @@ import static fr.xephi.authme.datasource.SqlDataSourceUtils.logSqlException;
/** /**
* SQLite data source. * SQLite data source.
*/ */
@SuppressWarnings({"checkstyle:AbbreviationAsWordInName"}) // Justification: Class name cannot be changed anymore
public class SQLite implements DataSource { public class SQLite implements DataSource {
private final Settings settings; private final Settings settings;

View File

@ -123,7 +123,7 @@ public class OnJoinVerifier implements Reloadable {
return false; return false;
} else if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) { } else if (!permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)) {
// Server is full and player is NOT VIP; set kick message and proceed with kick // Server is full and player is NOT VIP; set kick message and proceed with kick
event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)); event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER));
return true; return true;
} }
@ -135,12 +135,12 @@ public class OnJoinVerifier implements Reloadable {
} }
Player nonVipPlayer = generateKickPlayer(onlinePlayers); Player nonVipPlayer = generateKickPlayer(onlinePlayers);
if (nonVipPlayer != null) { if (nonVipPlayer != null) {
nonVipPlayer.kickPlayer(messages.retrieveSingle(MessageKey.KICK_FOR_VIP)); nonVipPlayer.kickPlayer(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP));
event.allow(); event.allow();
return false; return false;
} else { } else {
ConsoleLogger.info("VIP player " + player.getName() + " tried to join, but the server was full"); ConsoleLogger.info("VIP player " + player.getName() + " tried to join, but the server was full");
event.setKickMessage(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)); event.setKickMessage(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER));
return true; return true;
} }
} }

View File

@ -257,7 +257,7 @@ public class PlayerListener implements Listener {
try { try {
runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress()); runOnJoinChecks(JoiningPlayer.fromName(name), event.getAddress().getHostAddress());
} catch (FailedVerificationException e) { } catch (FailedVerificationException e) {
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); event.setKickMessage(m.retrieveSingle(name, e.getReason(), e.getArgs()));
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
} }
} }
@ -285,7 +285,7 @@ public class PlayerListener implements Listener {
try { try {
runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress()); runOnJoinChecks(JoiningPlayer.fromPlayerObject(player), event.getAddress().getHostAddress());
} catch (FailedVerificationException e) { } catch (FailedVerificationException e) {
event.setKickMessage(m.retrieveSingle(e.getReason(), e.getArgs())); event.setKickMessage(m.retrieveSingle(player, e.getReason(), e.getArgs()));
event.setResult(PlayerLoginEvent.Result.KICK_OTHER); event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
} }
} }

View File

@ -12,10 +12,11 @@ import javax.inject.Inject;
public class PlayerListener19Spigot implements Listener { public class PlayerListener19Spigot implements Listener {
private static boolean isPlayerSpawnLocationEventCalled = false;
@Inject @Inject
private TeleportationService teleportationService; private TeleportationService teleportationService;
private static boolean isPlayerSpawnLocationEventCalled = false;
public static boolean isPlayerSpawnLocationEventCalled() { public static boolean isPlayerSpawnLocationEventCalled() {
return isPlayerSpawnLocationEventCalled; return isPlayerSpawnLocationEventCalled;

View File

@ -5,9 +5,9 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.service.BukkitService;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
@ -46,7 +46,7 @@ public class ProtocolLibService implements SettingsDependent {
if (protectInvBeforeLogin) { if (protectInvBeforeLogin) {
ConsoleLogger.warning("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); ConsoleLogger.warning("WARNING! The protectInventory feature requires ProtocolLib! Disabling it...");
} }
if (denyTabCompleteBeforeLogin) { if (denyTabCompleteBeforeLogin) {
ConsoleLogger.warning("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it..."); ConsoleLogger.warning("WARNING! The denyTabComplete feature requires ProtocolLib! Disabling it...");
} }
@ -56,16 +56,21 @@ public class ProtocolLibService implements SettingsDependent {
} }
// Set up packet adapters // Set up packet adapters
if (protectInvBeforeLogin && inventoryPacketAdapter == null) { if (protectInvBeforeLogin) {
inventoryPacketAdapter = new InventoryPacketAdapter(plugin, playerCache); if (inventoryPacketAdapter == null) {
inventoryPacketAdapter.register(); inventoryPacketAdapter = new InventoryPacketAdapter(plugin, playerCache);
inventoryPacketAdapter.register();
}
} else if (inventoryPacketAdapter != null) { } else if (inventoryPacketAdapter != null) {
inventoryPacketAdapter.unregister(); inventoryPacketAdapter.unregister();
inventoryPacketAdapter = null; inventoryPacketAdapter = null;
} }
if (denyTabCompleteBeforeLogin && tabCompletePacketAdapter == null) {
tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, playerCache); if (denyTabCompleteBeforeLogin) {
tabCompletePacketAdapter.register(); if (tabCompletePacketAdapter == null) {
tabCompletePacketAdapter = new TabCompletePacketAdapter(plugin, playerCache);
tabCompletePacketAdapter.register();
}
} else if (tabCompletePacketAdapter != null) { } else if (tabCompletePacketAdapter != null) {
tabCompletePacketAdapter.unregister(); tabCompletePacketAdapter.unregister();
tabCompletePacketAdapter = null; tabCompletePacketAdapter = null;
@ -106,7 +111,7 @@ public class ProtocolLibService implements SettingsDependent {
this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN); this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
//it was true and will be deactivated now, so we need to restore the inventory for every player //it was true and will be deactivated now, so we need to restore the inventory for every player
if (oldProtectInventory && !protectInvBeforeLogin) { if (oldProtectInventory && !protectInvBeforeLogin && inventoryPacketAdapter != null) {
inventoryPacketAdapter.unregister(); inventoryPacketAdapter.unregister();
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) { for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
if (!playerCache.isAuthenticated(onlinePlayer.getName())) { if (!playerCache.isAuthenticated(onlinePlayer.getName())) {

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.util.expiring.Duration; import fr.xephi.authme.util.expiring.Duration;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.Map; import java.util.Map;
@ -18,7 +19,9 @@ public class Messages {
// Custom Authme tag replaced to new line // Custom Authme tag replaced to new line
private static final String NEWLINE_TAG = "%nl%"; private static final String NEWLINE_TAG = "%nl%";
private static final String PLAYER_TAG = "%username%"; // Global tag replacements
private static final String USERNAME_TAG = "%username%";
private static final String DISPLAYNAME_TAG = "%displayname%";
/** Contains the keys of the singular messages for time units. */ /** Contains the keys of the singular messages for time units. */
private static final Map<TimeUnit, MessageKey> TIME_UNIT_SINGULARS = ImmutableMap.<TimeUnit, MessageKey>builder() private static final Map<TimeUnit, MessageKey> TIME_UNIT_SINGULARS = ImmutableMap.<TimeUnit, MessageKey>builder()
@ -51,9 +54,9 @@ public class Messages {
* @param key The key of the message to send * @param key The key of the message to send
*/ */
public void send(CommandSender sender, MessageKey key) { public void send(CommandSender sender, MessageKey key) {
String[] lines = retrieve(key); String[] lines = retrieve(key, sender);
for (String line : lines) { for (String line : lines) {
sender.sendMessage(line.replaceAll(PLAYER_TAG, sender.getName())); sender.sendMessage(line);
} }
} }
@ -67,7 +70,7 @@ public class Messages {
* @param replacements The replacements to apply for the tags * @param replacements The replacements to apply for the tags
*/ */
public void send(CommandSender sender, MessageKey key, String... replacements) { public void send(CommandSender sender, MessageKey key, String... replacements) {
String message = retrieveSingle(key, replacements).replaceAll(PLAYER_TAG, sender.getName()); String message = retrieveSingle(sender, key, replacements);
for (String line : message.split("\n")) { for (String line : message.split("\n")) {
sender.sendMessage(line); sender.sendMessage(line);
} }
@ -77,10 +80,11 @@ public class Messages {
* Retrieve the message from the text file and return it split by new line as an array. * Retrieve the message from the text file and return it split by new line as an array.
* *
* @param key The message key to retrieve * @param key The message key to retrieve
* @param sender The entity to send the message to
* @return The message split by new lines * @return The message split by new lines
*/ */
public String[] retrieve(MessageKey key) { public String[] retrieve(MessageKey key, CommandSender sender) {
String message = retrieveMessage(key); String message = retrieveMessage(key, sender);
if (message.isEmpty()) { if (message.isEmpty()) {
// Return empty array instead of array with 1 empty string as entry // Return empty array instead of array with 1 empty string as entry
return new String[0]; return new String[0];
@ -101,18 +105,43 @@ public class Messages {
? TIME_UNIT_SINGULARS.get(duration.getTimeUnit()) ? TIME_UNIT_SINGULARS.get(duration.getTimeUnit())
: TIME_UNIT_PLURALS.get(duration.getTimeUnit()); : TIME_UNIT_PLURALS.get(duration.getTimeUnit());
return value + " " + retrieveMessage(timeUnitKey); return value + " " + retrieveMessage(timeUnitKey, "");
} }
/** /**
* Retrieve the message from the text file. * Retrieve the message from the text file.
* *
* @param key The message key to retrieve * @param key The message key to retrieve
* @param sender The entity to send the message to
* @return The message from the file * @return The message from the file
*/ */
private String retrieveMessage(MessageKey key) { private String retrieveMessage(MessageKey key, CommandSender sender) {
return formatMessage( String message = messagesFileHandler.getMessage(key.getKey());
messagesFileHandler.getMessage(key.getKey())); String displayName = sender.getName();
if (sender instanceof Player) {
displayName = ((Player) sender).getDisplayName();
}
return ChatColor.translateAlternateColorCodes('&', message)
.replace(NEWLINE_TAG, "\n")
.replace(USERNAME_TAG, sender.getName())
.replace(DISPLAYNAME_TAG, displayName);
}
/**
* Retrieve the message from the text file.
*
* @param key The message key to retrieve
* @param name The name of the entity to send the message to
* @return The message from the file
*/
private String retrieveMessage(MessageKey key, String name) {
String message = messagesFileHandler.getMessage(key.getKey());
return ChatColor.translateAlternateColorCodes('&', message)
.replace(NEWLINE_TAG, "\n")
.replace(USERNAME_TAG, name)
.replace(DISPLAYNAME_TAG, name);
} }
/** /**
@ -120,12 +149,13 @@ public class Messages {
* logs an error if the number of supplied replacements doesn't correspond to the number of tags * logs an error if the number of supplied replacements doesn't correspond to the number of tags
* the message key contains. * the message key contains.
* *
* @param sender The entity to send the message to
* @param key The key of the message to send * @param key The key of the message to send
* @param replacements The replacements to apply for the tags * @param replacements The replacements to apply for the tags
* @return The message from the file with replacements * @return The message from the file with replacements
*/ */
public String retrieveSingle(MessageKey key, String... replacements) { public String retrieveSingle(CommandSender sender, MessageKey key, String... replacements) {
String message = retrieveMessage(key); String message = retrieveMessage(key, sender);
String[] tags = key.getTags(); String[] tags = key.getTags();
if (replacements.length == tags.length) { if (replacements.length == tags.length) {
for (int i = 0; i < tags.length; ++i) { for (int i = 0; i < tags.length; ++i) {
@ -137,9 +167,26 @@ public class Messages {
return message; return message;
} }
private static String formatMessage(String message) { /**
return ChatColor.translateAlternateColorCodes('&', message) * Retrieve the given message code with the given tag replacements. Note that this method
.replace(NEWLINE_TAG, "\n"); * logs an error if the number of supplied replacements doesn't correspond to the number of tags
* the message key contains.
*
* @param name The name of the entity to send the message to
* @param key The key of the message to send
* @param replacements The replacements to apply for the tags
* @return The message from the file with replacements
*/
public String retrieveSingle(String name, MessageKey key, String... replacements) {
String message = retrieveMessage(key, name);
String[] tags = key.getTags();
if (replacements.length == tags.length) {
for (int i = 0; i < tags.length; ++i) {
message = message.replace(tags[i], replacements[i]);
}
} else {
ConsoleLogger.warning("Invalid number of replacements for message key '" + key + "'");
}
return message;
} }
} }

View File

@ -62,7 +62,8 @@ public class LuckPermsHandler implements PermissionHandler {
return false; return false;
} }
DataMutateResult result = user.setPermissionUnchecked(luckPermsApi.getNodeFactory().makeGroupNode(newGroup).build()); DataMutateResult result = user.setPermissionUnchecked(
luckPermsApi.getNodeFactory().makeGroupNode(newGroup).build());
if (result == DataMutateResult.FAIL) { if (result == DataMutateResult.FAIL) {
return false; return false;
} }

View File

@ -138,7 +138,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
private void handlePlayerWithUnmetNameRestriction(Player player, String ip) { private void handlePlayerWithUnmetNameRestriction(Player player, String ip) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> { bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
player.kickPlayer(service.retrieveSingleMessage(MessageKey.NOT_OWNER_ERROR)); player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.NOT_OWNER_ERROR));
if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) { if (service.getProperty(RestrictionSettings.BAN_UNKNOWN_IP)) {
server.banIP(ip); server.banIP(ip);
} }
@ -188,7 +188,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
&& countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) { && countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE))); () -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.SAME_IP_ONLINE)));
return false; return false;
} }
return true; return true;

View File

@ -214,7 +214,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
tempbanManager.tempbanPlayer(player); tempbanManager.tempbanPlayer(player);
} else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) { } else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask( bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD))); () -> player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.WRONG_PASSWORD)));
} else { } else {
service.send(player, MessageKey.WRONG_PASSWORD); service.send(player, MessageKey.WRONG_PASSWORD);

View File

@ -10,7 +10,9 @@ import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
/**
* Performs synchronous tasks after a successful {@link RegistrationType#EMAIL email registration}.
*/
public class ProcessSyncEmailRegister implements SynchronousProcess { public class ProcessSyncEmailRegister implements SynchronousProcess {
@Inject @Inject
@ -22,6 +24,11 @@ public class ProcessSyncEmailRegister implements SynchronousProcess {
ProcessSyncEmailRegister() { ProcessSyncEmailRegister() {
} }
/**
* Performs sync tasks for a player which has just registered by email.
*
* @param player the recently registered player
*/
public void processEmailRegister(Player player) { public void processEmailRegister(Player player) {
service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED); service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED);
limboService.replaceTasksAfterRegistration(player); limboService.replaceTasksAfterRegistration(player);

View File

@ -15,6 +15,7 @@ import org.bukkit.entity.Player;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
* Performs synchronous tasks after a successful {@link RegistrationType#PASSWORD password registration}.
*/ */
public class ProcessSyncPasswordRegister implements SynchronousProcess { public class ProcessSyncPasswordRegister implements SynchronousProcess {
@ -46,6 +47,11 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess {
} }
} }
/**
* Processes a player having registered with a password.
*
* @param player the newly registered player
*/
public void processPasswordRegister(Player player) { public void processPasswordRegister(Player player) {
service.send(player, MessageKey.REGISTER_SUCCESS); service.send(player, MessageKey.REGISTER_SUCCESS);
@ -58,7 +64,7 @@ public class ProcessSyncPasswordRegister implements SynchronousProcess {
// Kick Player after Registration is enabled, kick the player // Kick Player after Registration is enabled, kick the player
if (service.getProperty(RegistrationSettings.FORCE_KICK_AFTER_REGISTER)) { if (service.getProperty(RegistrationSettings.FORCE_KICK_AFTER_REGISTER)) {
player.kickPlayer(service.retrieveSingleMessage(MessageKey.REGISTER_SUCCESS)); player.kickPlayer(service.retrieveSingleMessage(player, MessageKey.REGISTER_SUCCESS));
return; return;
} }

View File

@ -63,11 +63,12 @@ public class CommonService {
/** /**
* Retrieves a message in one piece. * Retrieves a message in one piece.
* *
* @param sender The entity to send the message to
* @param key the key of the message * @param key the key of the message
* @return the message * @return the message
*/ */
public String retrieveSingleMessage(MessageKey key) { public String retrieveSingleMessage(CommandSender sender, MessageKey key) {
return messages.retrieveSingle(key); return messages.retrieveSingle(sender, key);
} }
/** /**

View File

@ -1,46 +1,81 @@
package fr.xephi.authme.service; package fr.xephi.authme.service;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.maxmind.geoip.LookupService; import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.google.common.io.Resources;
import com.ice.tar.TarEntry;
import com.ice.tar.TarInputStream;
import com.maxmind.db.GeoIp2Provider;
import com.maxmind.db.Reader;
import com.maxmind.db.Reader.FileMode;
import com.maxmind.db.cache.CHMCache;
import com.maxmind.db.model.Country;
import com.maxmind.db.model.CountryResponse;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.util.FileUtils; import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.InternetProtocolUtils; import fr.xephi.authme.util.InternetProtocolUtils;
import javax.inject.Inject; import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.InetAddress;
import java.net.URL; import java.net.URL;
import java.net.URLConnection; import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import static com.maxmind.geoip.LookupService.GEOIP_MEMORY_CACHE; import javax.inject.Inject;
public class GeoIpService { public class GeoIpService {
private static final String LICENSE =
"[LICENSE] This product uses data from the GeoLite API created by MaxMind, available at http://www.maxmind.com";
private static final String GEOIP_URL =
"http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz";
private LookupService lookupService;
private Thread downloadTask;
private final File dataFile; private static final String LICENSE =
"[LICENSE] This product includes GeoLite2 data created by MaxMind, available at https://www.maxmind.com";
private static final String DATABASE_NAME = "GeoLite2-Country";
private static final String DATABASE_EXT = ".mmdb";
private static final String DATABASE_FILE = DATABASE_NAME + DATABASE_EXT;
private static final String ARCHIVE_FILE = DATABASE_NAME + ".tar.gz";
private static final String ARCHIVE_URL = "https://geolite.maxmind.com/download/geoip/database/" + ARCHIVE_FILE;
private static final String CHECKSUM_URL = ARCHIVE_URL + ".md5";
private static final int UPDATE_INTERVAL_DAYS = 30;
private final Path dataFile;
private final BukkitService bukkitService;
private GeoIp2Provider databaseReader;
private volatile boolean downloading;
@Inject @Inject
GeoIpService(@DataFolder File dataFolder) { GeoIpService(@DataFolder File dataFolder, BukkitService bukkitService) {
this.dataFile = new File(dataFolder, "GeoIP.dat"); this.bukkitService = bukkitService;
this.dataFile = dataFolder.toPath().resolve(DATABASE_FILE);
// Fires download of recent data or the initialization of the look up service // Fires download of recent data or the initialization of the look up service
isDataAvailable(); isDataAvailable();
} }
@VisibleForTesting @VisibleForTesting
GeoIpService(@DataFolder File dataFolder, LookupService lookupService) { GeoIpService(@DataFolder File dataFolder, BukkitService bukkitService, GeoIp2Provider reader) {
this.dataFile = dataFolder; this.bukkitService = bukkitService;
this.lookupService = lookupService; this.dataFile = dataFolder.toPath().resolve(DATABASE_FILE);
this.databaseReader = reader;
} }
/** /**
@ -49,94 +84,186 @@ public class GeoIpService {
* @return True if the data is available, false otherwise. * @return True if the data is available, false otherwise.
*/ */
private synchronized boolean isDataAvailable() { private synchronized boolean isDataAvailable() {
if (downloadTask != null && downloadTask.isAlive()) { if (downloading) {
// we are currently downloading the database
return false; return false;
} }
if (lookupService != null) {
if (databaseReader != null) {
// everything is initialized
return true; return true;
} }
if (dataFile.exists()) { if (Files.exists(dataFile)) {
boolean dataIsOld = (System.currentTimeMillis() - dataFile.lastModified()) > TimeUnit.DAYS.toMillis(30); try {
if (!dataIsOld) { FileTime lastModifiedTime = Files.getLastModifiedTime(dataFile);
try { if (Duration.between(lastModifiedTime.toInstant(), Instant.now()).toDays() <= UPDATE_INTERVAL_DAYS) {
lookupService = new LookupService(dataFile, GEOIP_MEMORY_CACHE); databaseReader = new Reader(dataFile.toFile(), FileMode.MEMORY, new CHMCache());
ConsoleLogger.info(LICENSE); ConsoleLogger.info(LICENSE);
// don't fire the update task - we are up to date
return true; return true;
} catch (IOException e) { } else {
ConsoleLogger.logException("Failed to load GeoLiteAPI database", e); ConsoleLogger.debug("GEO Ip database is older than " + UPDATE_INTERVAL_DAYS + " Days");
return false;
} }
} else { } catch (IOException ioEx) {
FileUtils.delete(dataFile); ConsoleLogger.logException("Failed to load GeoLiteAPI database", ioEx);
return false;
} }
} }
// Ok, let's try to download the data file!
downloadTask = createDownloadTask(); // File is outdated or doesn't exist - let's try to download the data file!
downloadTask.start(); startDownloadTask();
return false; return false;
} }
/** /**
* Create a thread which will attempt to download new data from the GeoLite website. * Create a thread which will attempt to download new data from the GeoLite website.
*
* @return the generated download thread
*/ */
private Thread createDownloadTask() { private void startDownloadTask() {
return new Thread(new Runnable() { downloading = true;
@Override
public void run() { // use bukkit's cached threads
try { bukkitService.runTaskAsynchronously(() -> {
URL downloadUrl = new URL(GEOIP_URL); ConsoleLogger.info("Downloading GEO IP database, because the old database is outdated or doesn't exist");
URLConnection conn = downloadUrl.openConnection();
conn.setConnectTimeout(10000); Path tempFile = null;
conn.connect(); try {
InputStream input = conn.getInputStream(); // download database to temporarily location
if (conn.getURL().toString().endsWith(".gz")) { tempFile = Files.createTempFile(ARCHIVE_FILE, null);
input = new GZIPInputStream(input); try (OutputStream out = Files.newOutputStream(tempFile)) {
} Resources.copy(new URL(ARCHIVE_URL), out);
OutputStream output = new FileOutputStream(dataFile); }
byte[] buffer = new byte[2048];
int length = input.read(buffer); // MD5 checksum verification
while (length >= 0) { String targetChecksum = Resources.toString(new URL(CHECKSUM_URL), StandardCharsets.UTF_8);
output.write(buffer, 0, length); if (!verifyChecksum(Hashing.md5(), tempFile, targetChecksum)) {
length = input.read(buffer); return;
} }
output.close();
input.close(); // tar extract database and copy to target destination
} catch (IOException e) { if (!extractDatabase(tempFile, dataFile)) {
ConsoleLogger.logException("Could not download GeoLiteAPI database", e); ConsoleLogger.warning("Cannot find database inside downloaded GEO IP file at " + tempFile);
return;
}
ConsoleLogger.info("Successfully downloaded new GEO IP database to " + dataFile);
//only set this value to false on success otherwise errors could lead to endless download triggers
downloading = false;
} catch (IOException ioEx) {
ConsoleLogger.logException("Could not download GeoLiteAPI database", ioEx);
} finally {
// clean up
if (tempFile != null) {
FileUtils.delete(tempFile.toFile());
} }
} }
}); });
} }
/**
* Verify if the expected checksum is equal to the checksum of the given file.
*
* @param function the checksum function like MD5, SHA256 used to generate the checksum from the file
* @param file the file we want to calculate the checksum from
* @param expectedChecksum the expected checksum
* @return true if equal, false otherwise
* @throws IOException on I/O error reading the file
*/
private boolean verifyChecksum(HashFunction function, Path file, String expectedChecksum) throws IOException {
HashCode actualHash = function.hashBytes(Files.readAllBytes(file));
HashCode expectedHash = HashCode.fromString(expectedChecksum);
if (Objects.equals(actualHash, expectedHash)) {
return true;
}
ConsoleLogger.warning("GEO IP checksum verification failed");
ConsoleLogger.warning("Expected: " + expectedHash + " Actual: " + actualHash);
return false;
}
/**
* Extract the database from the tar archive. Existing outputFile will be replaced if it already exists.
*
* @param tarInputFile gzipped tar input file where the database is
* @param outputFile destination file for the database
* @return true if the database was found, false otherwise
* @throws IOException on I/O error reading the tar archive or writing the output
*/
private boolean extractDatabase(Path tarInputFile, Path outputFile) throws IOException {
// .gz -> gzipped file
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(tarInputFile));
TarInputStream tarIn = new TarInputStream(new GZIPInputStream(in))) {
TarEntry entry;
while ((entry = tarIn.getNextEntry()) != null) {
if (!entry.isDirectory()) {
// filename including folders (absolute path inside the archive)
String filename = entry.getName();
if (filename.endsWith(DATABASE_EXT)) {
// found the database file
Files.copy(tarIn, outputFile, StandardCopyOption.REPLACE_EXISTING);
// update the last modification date to be same as in the archive
Files.setLastModifiedTime(outputFile, FileTime.from(entry.getModTime().toInstant()));
return true;
}
}
}
}
return false;
}
/** /**
* Get the country code of the given IP address. * Get the country code of the given IP address.
* *
* @param ip textual IP address to lookup. * @param ip textual IP address to lookup.
*
* @return two-character ISO 3166-1 alpha code for the country. * @return two-character ISO 3166-1 alpha code for the country.
*/ */
public String getCountryCode(String ip) { public String getCountryCode(String ip) {
if (!InternetProtocolUtils.isLocalAddress(ip) && isDataAvailable()) { return getCountry(ip).map(Country::getIsoCode).orElse("--");
return lookupService.getCountry(ip).getCode();
}
return "--";
} }
/** /**
* Get the country name of the given IP address. * Get the country name of the given IP address.
* *
* @param ip textual IP address to lookup. * @param ip textual IP address to lookup.
*
* @return The name of the country. * @return The name of the country.
*/ */
public String getCountryName(String ip) { public String getCountryName(String ip) {
if (!InternetProtocolUtils.isLocalAddress(ip) && isDataAvailable()) { return getCountry(ip).map(Country::getName).orElse("N/A");
return lookupService.getCountry(ip).getName();
}
return "N/A";
} }
/**
* Get the country of the given IP address
*
* @param ip textual IP address to lookup
* @return the wrapped Country model or {@link Optional#empty()} if
* <ul>
* <li>Database reader isn't initialized</li>
* <li>MaxMind has no record about this IP address</li>
* <li>IP address is local</li>
* <li>Textual representation is not a valid IP address</li>
* </ul>
*/
private Optional<Country> getCountry(String ip) {
if (ip == null || ip.isEmpty() || InternetProtocolUtils.isLocalAddress(ip) || !isDataAvailable()) {
return Optional.empty();
}
try {
InetAddress address = InetAddress.getByName(ip);
//Reader.getCountry() can be null for unknown addresses
return Optional.ofNullable(databaseReader.getCountry(address)).map(CountryResponse::getCountry);
} catch (UnknownHostException e) {
// Ignore invalid ip addresses
// Legacy GEO IP Database returned a unknown country object with Country-Code: '--' and Country-Name: 'N/A'
} catch (IOException ioEx) {
ConsoleLogger.logException("Cannot lookup country for " + ip + " at GEO IP database", ioEx);
}
return Optional.empty();
}
} }

View File

@ -45,11 +45,13 @@ public class SessionService implements Reloadable {
database.setUnlogged(name); database.setUnlogged(name);
database.revokeSession(name); database.revokeSession(name);
PlayerAuth auth = database.getAuth(name); PlayerAuth auth = database.getAuth(name);
if (hasValidSessionData(auth, player)) {
SessionState state = fetchSessionStatus(auth, player);
if (state.equals(SessionState.VALID)) {
RestoreSessionEvent event = bukkitService.createAndCallEvent( RestoreSessionEvent event = bukkitService.createAndCallEvent(
isAsync -> new RestoreSessionEvent(player, isAsync)); isAsync -> new RestoreSessionEvent(player, isAsync));
return !event.isCancelled(); return !event.isCancelled();
} else { } else if (state.equals(SessionState.IP_CHANGED)) {
service.send(player, MessageKey.SESSION_EXPIRED); service.send(player, MessageKey.SESSION_EXPIRED);
} }
} }
@ -62,19 +64,26 @@ public class SessionService implements Reloadable {
* *
* @param auth the player auth * @param auth the player auth
* @param player the associated player * @param player the associated player
* @return true if the player may resume his login session, false otherwise * @return SessionState based on the state of the session (VALID, NOT_VALID, OUTDATED, IP_CHANGED)
*/ */
private boolean hasValidSessionData(PlayerAuth auth, Player player) { private SessionState fetchSessionStatus(PlayerAuth auth, Player player) {
if (auth == null) { if (auth == null) {
ConsoleLogger.warning("No PlayerAuth in database for '" + player.getName() + "' during session check"); ConsoleLogger.warning("No PlayerAuth in database for '" + player.getName() + "' during session check");
return false; return SessionState.NOT_VALID;
} else if (auth.getLastLogin() == null) { } else if (auth.getLastLogin() == null) {
return false; return SessionState.NOT_VALID;
} }
long timeSinceLastLogin = System.currentTimeMillis() - auth.getLastLogin(); long timeSinceLastLogin = System.currentTimeMillis() - auth.getLastLogin();
return PlayerUtils.getPlayerIp(player).equals(auth.getLastIp())
&& timeSinceLastLogin > 0 if (timeSinceLastLogin > 0
&& timeSinceLastLogin < service.getProperty(PluginSettings.SESSIONS_TIMEOUT) * MILLIS_PER_MINUTE; && timeSinceLastLogin < service.getProperty(PluginSettings.SESSIONS_TIMEOUT) * MILLIS_PER_MINUTE) {
if (PlayerUtils.getPlayerIp(player).equals(auth.getLastIp())) {
return SessionState.VALID;
} else {
return SessionState.IP_CHANGED;
}
}
return SessionState.OUTDATED;
} }
public void grantSession(String name) { public void grantSession(String name) {

View File

@ -0,0 +1,13 @@
package fr.xephi.authme.service;
public enum SessionState {
VALID,
NOT_VALID,
OUTDATED,
IP_CHANGED
}

View File

@ -65,7 +65,7 @@ public class BungeeSender implements SettingsDependent {
public void connectPlayerOnLogin(Player player) { public void connectPlayerOnLogin(Player player) {
if (isEnabled && !destinationServerOnLogin.isEmpty()) { if (isEnabled && !destinationServerOnLogin.isEmpty()) {
bukkitService.scheduleSyncDelayedTask(() -> bukkitService.scheduleSyncDelayedTask(() ->
sendBungeecordMessage("ConnectOther", player.getName(), destinationServerOnLogin), 20L); sendBungeecordMessage("ConnectOther", player.getName(), destinationServerOnLogin), 5L);
} }
} }

View File

@ -56,9 +56,9 @@ public class SettingsWarner {
// Warn if spigot.yml has settings.bungeecord set to true but config.yml has Hooks.bungeecord set to false // Warn if spigot.yml has settings.bungeecord set to true but config.yml has Hooks.bungeecord set to false
if (Utils.isSpigot() && Bukkit.spigot().getConfig().getBoolean("settings.bungeecord") if (Utils.isSpigot() && Bukkit.spigot().getConfig().getBoolean("settings.bungeecord")
&& !settings.getProperty(HooksSettings.BUNGEECORD)) { && !settings.getProperty(HooksSettings.BUNGEECORD)) {
ConsoleLogger.warning("Note: Hooks.bungeecord is set to false but your server appears to be running in" + ConsoleLogger.warning("Note: Hooks.bungeecord is set to false but your server appears to be running in"
" bungeecord mode (see your spigot.yml). In order to allow the datasource caching and the AuthMeBungee" + + " bungeecord mode (see your spigot.yml). In order to allow the datasource caching and the"
" add-on to work properly you have to enable this option!"); + " AuthMeBungee add-on to work properly you have to enable this option!");
} }
// Check if argon2 library is present and can be loaded // Check if argon2 library is present and can be loaded

View File

@ -22,7 +22,7 @@ public final class ProtectionSettings implements SettingsHolder {
@Comment({ @Comment({
"Countries allowed to join the server and register. For country codes, see", "Countries allowed to join the server and register. For country codes, see",
"http://dev.maxmind.com/geoip/legacy/codes/iso3166/", "https://dev.maxmind.com/geoip/legacy/codes/iso3166/",
"PLEASE USE QUOTES!"}) "PLEASE USE QUOTES!"})
public static final Property<List<String>> COUNTRIES_WHITELIST = public static final Property<List<String>> COUNTRIES_WHITELIST =
newListProperty("Protection.countries", "US", "GB"); newListProperty("Protection.countries", "US", "GB");

View File

@ -61,6 +61,11 @@ public class PurgeExecutor {
purgePermissions(players); purgePermissions(players);
} }
/**
* Purges data from the AntiXray plugin.
*
* @param cleared the players whose data should be cleared
*/
synchronized void purgeAntiXray(Collection<String> cleared) { synchronized void purgeAntiXray(Collection<String> cleared) {
if (!settings.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) { if (!settings.getProperty(PurgeSettings.REMOVE_ANTI_XRAY_FILE)) {
return; return;
@ -95,6 +100,11 @@ public class PurgeExecutor {
ConsoleLogger.info(ChatColor.GOLD + "Deleted " + names.size() + " user accounts"); ConsoleLogger.info(ChatColor.GOLD + "Deleted " + names.size() + " user accounts");
} }
/**
* Purges data from the LimitedCreative plugin.
*
* @param cleared the players whose data should be cleared
*/
synchronized void purgeLimitedCreative(Collection<String> cleared) { synchronized void purgeLimitedCreative(Collection<String> cleared) {
if (!settings.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) { if (!settings.getProperty(PurgeSettings.REMOVE_LIMITED_CREATIVE_INVENTORIES)) {
return; return;
@ -191,6 +201,11 @@ public class PurgeExecutor {
ConsoleLogger.info("AutoPurge: Removed " + deletedFiles + " EssentialsFiles"); ConsoleLogger.info("AutoPurge: Removed " + deletedFiles + " EssentialsFiles");
} }
/**
* Removes permission data (groups a user belongs to) for the given players.
*
* @param cleared the players to remove data for
*/
synchronized void purgePermissions(Collection<OfflinePlayer> cleared) { synchronized void purgePermissions(Collection<OfflinePlayer> cleared) {
if (!settings.getProperty(PurgeSettings.REMOVE_PERMISSIONS)) { if (!settings.getProperty(PurgeSettings.REMOVE_PERMISSIONS)) {
return; return;

View File

@ -1,6 +1,7 @@
package fr.xephi.authme.util; package fr.xephi.authme.util;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender; import org.bukkit.command.ConsoleCommandSender;
@ -28,7 +29,12 @@ public final class Utils {
* @return true if the running server instance is spigot-based. * @return true if the running server instance is spigot-based.
*/ */
public static boolean isSpigot() { public static boolean isSpigot() {
return isClassLoaded("org.spigotmc.SpigotConfig"); try {
Bukkit.spigot();
return true;
} catch (NoSuchMethodError e) {
return false;
}
} }
/** /**

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cРегистрациите са изключени!' disabled: '&cРегистрациите са изключени!'

View File

@ -1,6 +1,10 @@
#Tradução pt/br Authme Reloaded #Tradução pt/br Authme Reloaded
#Feito por GabrielDev(DeathRush) e Frani (PotterCraft_) #Feito por GabrielDev(DeathRush) e Frani (PotterCraft_)
# http://gamersboard.com.br/ | www.magitechserver.com # http://gamersboard.com.br/ | www.magitechserver.com
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cRegistrace je zakázána!' disabled: '&cRegistrace je zakázána!'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cRegistrierungen sind deaktiviert' disabled: '&cRegistrierungen sind deaktiviert'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
register_request: '&3Please, register to the server with the command: /register <password> <ConfirmPassword>' register_request: '&3Please, register to the server with the command: /register <password> <ConfirmPassword>'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cEn-ludo registriĝo estas malebligita!' disabled: '&cEn-ludo registriĝo estas malebligita!'

View File

@ -1,4 +1,8 @@
# This file must be in ANSI if win, or UTF-8 if linux. # This file must be in ANSI if win, or UTF-8 if linux.
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cMängusisene registreerimine välja lülitatud!' disabled: '&cMängusisene registreerimine välja lülitatud!'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cErregistroa desgaitua' disabled: '&cErregistroa desgaitua'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cRekisteröinti on suljettu!' disabled: '&cRekisteröinti on suljettu!'

View File

@ -1,7 +1,10 @@
# Traduction par: André & Twonox # Traduction par: André & Twonox
# Pour afficher une apostrophe, vous devez en mettre deux consécutivement (ex: «J''ai» au lieu de «J'ai») # Pour afficher une apostrophe, vous devez en mettre deux consécutivement (ex: «J''ai» au lieu de «J'ai»)
# Pour passer à la ligne, utilisez: %nl% # List of global tags:
# %nl% - Pour passer à la ligne.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cO rexistro está deshabilitado' disabled: '&cO rexistro está deshabilitado'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cA regisztráció letiltva!' disabled: '&cA regisztráció letiltva!'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cRegister dalam game tidak diaktifkan!' disabled: '&cRegister dalam game tidak diaktifkan!'

View File

@ -1,6 +1,10 @@
# Lingua Italiana creata da Maxetto e sgdc3. # Lingua Italiana creata da Maxetto.
# Tag globali disponibili:
# %nl% - Vai a capo.
# %username% - Sostituisce il nome dell'utente che riceve il messaggio.
# %displayname% - Sostituisce il nickname (e i colori) dell'utente che riceve il messaggio.
# Registration # Registrazione
registration: registration:
disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.' disabled: '&cLa registrazione tramite i comandi di gioco è disabilitata.'
name_taken: '&cHai già eseguito la registrazione, non puoi eseguirla nuovamente.' name_taken: '&cHai già eseguito la registrazione, non puoi eseguirla nuovamente.'
@ -10,7 +14,7 @@ registration:
success: '&2Registrato correttamente!' success: '&2Registrato correttamente!'
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'
# Password errors on registration # Errori della password durante la registrazione
password: password:
match_error: '&cLe password non corrispondono!' match_error: '&cLe password non corrispondono!'
name_in_password: '&cNon puoi usare il tuo nome utente come password, per favore scegline un''altra...' name_in_password: '&cNon puoi usare il tuo nome utente come password, per favore scegline un''altra...'
@ -18,7 +22,7 @@ password:
forbidden_characters: '&4La tua password contiene caratteri non consentiti. I caratteri consentiti sono: %valid_chars' forbidden_characters: '&4La tua password contiene caratteri non consentiti. I caratteri consentiti sono: %valid_chars'
wrong_length: '&cLa password che hai inserito è troppo corta o troppo lunga, per favore scegline un''altra...' wrong_length: '&cLa password che hai inserito è troppo corta o troppo lunga, per favore scegline un''altra...'
# Login # Autenticazione
login: login:
command_usage: '&cUtilizzo: /login <password>' command_usage: '&cUtilizzo: /login <password>'
wrong_password: '&cPassword non corretta!' wrong_password: '&cPassword non corretta!'
@ -26,7 +30,7 @@ login:
login_request: '&cPer favore, esegui l''autenticazione con il comando: /login <password>' login_request: '&cPer favore, esegui l''autenticazione con il comando: /login <password>'
timeout_error: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!' timeout_error: '&4Tempo scaduto per eseguire l''autenticazione, sei stato espulso dal server, per favore riprova!'
# Errors # Errori
error: error:
denied_command: '&cPer poter usare questo comando devi essere autenticato!' denied_command: '&cPer poter usare questo comando devi essere autenticato!'
denied_chat: '&cPer poter scrivere messaggi in chat devi essere autenticato!' denied_chat: '&cPer poter scrivere messaggi in chat devi essere autenticato!'
@ -45,12 +49,12 @@ antibot:
auto_enabled: '&4Il servizio di AntiBot è stato automaticamente abilitato a seguito delle numerose connessioni!' auto_enabled: '&4Il servizio di AntiBot è stato automaticamente abilitato a seguito delle numerose connessioni!'
auto_disabled: '&2Il servizio di AntiBot è stato automaticamente disabilitato dopo %m minuti!' auto_disabled: '&2Il servizio di AntiBot è stato automaticamente disabilitato dopo %m minuti!'
# Unregister # Rimozione dal Database
unregister: unregister:
success: '&2Sei stato correttamente rimosso dal database!' success: '&2Sei stato correttamente rimosso dal database!'
command_usage: '&cUtilizzo: /unregister <password>' command_usage: '&cUtilizzo: /unregister <password>'
# Other messages # Altri messaggi
misc: misc:
account_not_activated: '&cIl tuo account non è stato ancora verificato, controlla fra le tue email per scoprire come attivarlo!' account_not_activated: '&cIl tuo account non è stato ancora verificato, controlla fra le tue email per scoprire come attivarlo!'
password_changed: '&2Password cambiata correttamente!' password_changed: '&2Password cambiata correttamente!'
@ -61,12 +65,12 @@ misc:
accounts_owned_self: 'Possiedi %count account:' accounts_owned_self: 'Possiedi %count account:'
accounts_owned_other: 'Il giocatore %name possiede %count account:' accounts_owned_other: 'Il giocatore %name possiede %count account:'
# Session messages # Messaggi della sessione
session: session:
valid_session: '&2Autenticato automaticamente attraverso la precedente sessione!' valid_session: '&2Autenticato automaticamente attraverso la precedente sessione!'
invalid_session: '&cIl tuo indirizzo IP è cambiato e la tua sessione è stata terminata!' invalid_session: '&cIl tuo indirizzo IP è cambiato e la tua sessione è stata terminata!'
# Error messages when joining # Messaggi di errore durante l'accesso
on_join_validation: on_join_validation:
same_ip_online: 'Un giocatore con il tuo stesso IP è già connesso sul server!' same_ip_online: 'Un giocatore con il tuo stesso IP è già connesso sul server!'
same_nick_online: '&4Un giocatore con il tuo stesso nome utente è già connesso sul server!' same_nick_online: '&4Un giocatore con il tuo stesso nome utente è già connesso sul server!'
@ -96,7 +100,7 @@ email:
change_password_expired: 'Non puoi più cambiare la tua password con questo comando.' change_password_expired: 'Non puoi più cambiare la tua password con questo comando.'
email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.' email_cooldown_error: '&cUna email di recupero ti è già stata inviata recentemente. Devi attendere %time prima di poterne richiedere una nuova.'
# Password recovery by email # Recupero password via Email
recovery: recovery:
forgot_password_hint: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery <email>' forgot_password_hint: '&3Hai dimenticato la tua password? Puoi recuperarla eseguendo il comando: /email recovery <email>'
command_usage: '&cUtilizzo: /email recovery <email>' command_usage: '&cUtilizzo: /email recovery <email>'
@ -116,7 +120,7 @@ captcha:
captcha_for_registration: 'Per poterti registrare devi prima risolvere un captcha, per favore scrivi: /captcha %captcha_code' captcha_for_registration: 'Per poterti registrare devi prima risolvere un captcha, per favore scrivi: /captcha %captcha_code'
register_captcha_valid: '&2Il captcha inserito è valido! Ora puoi eseguire la registrazione con: /register <password> <confermaPassword>' register_captcha_valid: '&2Il captcha inserito è valido! Ora puoi eseguire la registrazione con: /register <password> <confermaPassword>'
# Verification code # Codice di verifica
verification: verification:
code_required: '&3Questo comando va a modificare dati sensibili e richiede una verifica tramite email! Controlla la tua posta in arrivo e segui le istruzioni nell''email.' code_required: '&3Questo comando va a modificare dati sensibili e richiede una verifica tramite email! Controlla la tua posta in arrivo e segui le istruzioni nell''email.'
command_usage: '&cUtilizzo: /verification <codice>' command_usage: '&cUtilizzo: /verification <codice>'
@ -126,7 +130,7 @@ verification:
code_expired: '&3Il tuo codice è scaduto! Esegui nuovamente un comando che modifica dati sensibili per ricevere uno nuovo codice!' code_expired: '&3Il tuo codice è scaduto! Esegui nuovamente un comando che modifica dati sensibili per ricevere uno nuovo codice!'
email_needed: '&3Per verificare la tua identità devi collegare un indirizzo email al tuo account!' email_needed: '&3Per verificare la tua identità devi collegare un indirizzo email al tuo account!'
# Time units # Unità di tempo
time: time:
second: 'secondo' second: 'secondo'
seconds: 'secondi' seconds: 'secondi'

View File

@ -1,5 +1,9 @@
#Translated by Kirito (kds123321@naver.com), System32(me@syst32.com), Adeuran(adeuran@tistory.com) #Translated by Kirito (kds123321@naver.com), System32(me@syst32.com), Adeuran(adeuran@tistory.com)
#14.05.2017 Thanks for use #14.05.2017 Thanks for use
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&6Registracija yra isjungta' disabled: '&6Registracija yra isjungta'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cRegistratie is uitgeschakeld!' disabled: '&cRegistratie is uitgeschakeld!'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&4Rejestracja jest wyłączona.' disabled: '&4Rejestracja jest wyłączona.'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cRegisto de novos utilizadores desactivado' disabled: '&cRegisto de novos utilizadores desactivado'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cInregistrarea in joc nu este activata!' disabled: '&cInregistrarea in joc nu este activata!'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cРегистрация отключена.' disabled: '&cРегистрация отключена.'

View File

@ -4,6 +4,10 @@
# in future there can be more translators # # in future there can be more translators #
# if they are not listed here # # if they are not listed here #
# check Translators on GitHub Wiki. # # check Translators on GitHub Wiki. #
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cOyun icin kayit olma kapatildi!' disabled: '&cOyun icin kayit olma kapatildi!'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cВнутрішньоігрову реєстрацію зараз вимкнено.' disabled: '&cВнутрішньоігрову реєстрацію зараз вимкнено.'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&cKhông cho phép đăng ký tài khoản trong máy chủ!' disabled: '&cKhông cho phép đăng ký tài khoản trong máy chủ!'

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&8[&6玩家系统&8] &c目前服务器暂时禁止注册请到服务器论坛以得到更多资讯' disabled: '&8[&6玩家系统&8] &c目前服务器暂时禁止注册请到服务器论坛以得到更多资讯'

View File

@ -1,6 +1,10 @@
# Translator: lifehome<m@lifeho.me> # # Translator: lifehome<m@lifeho.me> #
# Last modif: 1508689979 UTC # # Last modif: 1508689979 UTC #
# -------------------------------------------- # # -------------------------------------------- #
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:

View File

@ -1,3 +1,8 @@
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:
disabled: '&c遊戲內註冊已停用!' disabled: '&c遊戲內註冊已停用!'

View File

@ -1,5 +1,9 @@
# Translators: MineWolf50, lifehome, haer0248 # # Translators: MineWolf50, lifehome, haer0248 #
# -------------------------------------------- # # -------------------------------------------- #
# List of global tags:
# %nl% - Goes to new line.
# %username% - Replaces the username of the player receiving the message.
# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
# Registration # Registration
registration: registration:

View File

@ -11,6 +11,7 @@ import java.util.Objects;
/** /**
* Custom matchers for AuthMe entities. * Custom matchers for AuthMe entities.
*/ */
@SuppressWarnings("checkstyle:JavadocMethod") // Justification: Javadoc would be huge because of the many parameters
public final class AuthMeMatchers { public final class AuthMeMatchers {
private AuthMeMatchers() { private AuthMeMatchers() {

View File

@ -67,7 +67,7 @@ public class ClassCollector {
public List<Class<?>> collectClasses(Predicate<Class<?>> filter) { public List<Class<?>> collectClasses(Predicate<Class<?>> filter) {
File rootFolder = new File(root); File rootFolder = new File(root);
List<Class<?>> collection = new ArrayList<>(); List<Class<?>> collection = new ArrayList<>();
collectClasses(rootFolder, filter, collection); gatherClassesFromFile(rootFolder, filter, collection);
return collection; return collection;
} }
@ -124,14 +124,14 @@ public class ClassCollector {
* @param filter the class predicate * @param filter the class predicate
* @param collection collection to add classes to * @param collection collection to add classes to
*/ */
private void collectClasses(File folder, Predicate<Class<?>> filter, List<Class<?>> collection) { private void gatherClassesFromFile(File folder, Predicate<Class<?>> filter, List<Class<?>> collection) {
File[] files = folder.listFiles(); File[] files = folder.listFiles();
if (files == null) { if (files == null) {
throw new IllegalStateException("Could not read files from '" + folder + "'"); throw new IllegalStateException("Could not read files from '" + folder + "'");
} }
for (File file : files) { for (File file : files) {
if (file.isDirectory()) { if (file.isDirectory()) {
collectClasses(file, filter, collection); gatherClassesFromFile(file, filter, collection);
} else if (file.isFile()) { } else if (file.isFile()) {
Class<?> clazz = loadTaskClassFromFile(file); Class<?> clazz = loadTaskClassFromFile(file);
if (clazz != null && filter.test(clazz)) { if (clazz != null && filter.test(clazz)) {

View File

@ -82,7 +82,6 @@ public final class ReflectionTestUtils {
* @param clazz the class to retrieve a method from * @param clazz the class to retrieve a method from
* @param methodName the name of the method * @param methodName the name of the method
* @param parameterTypes the parameter types the method to retrieve has * @param parameterTypes the parameter types the method to retrieve has
*
* @return the method of the class, set to be accessible * @return the method of the class, set to be accessible
*/ */
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) { public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
@ -96,6 +95,15 @@ public final class ReflectionTestUtils {
} }
} }
/**
* Invokes the given method on the provided instance with the given parameters.
*
* @param method the method to invoke
* @param instance the instance to invoke the method on (null for static methods)
* @param parameters the parameters to pass to the method
* @param <V> return value of the method
* @return method return value
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <V> V invokeMethod(Method method, Object instance, Object... parameters) { public static <V> V invokeMethod(Method method, Object instance, Object... parameters) {
method.setAccessible(true); method.setAccessible(true);

View File

@ -160,7 +160,7 @@ public class RegisterAdminCommandTest {
Player player = mock(Player.class); Player player = mock(Player.class);
given(bukkitService.getPlayerExact(user)).willReturn(player); given(bukkitService.getPlayerExact(user)).willReturn(player);
String kickForAdminRegister = "Admin registered you -- log in again"; String kickForAdminRegister = "Admin registered you -- log in again";
given(commandService.retrieveSingleMessage(MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister); given(commandService.retrieveSingleMessage(player, MessageKey.KICK_FOR_ADMIN_REGISTER)).willReturn(kickForAdminRegister);
CommandSender sender = mock(CommandSender.class); CommandSender sender = mock(CommandSender.class);
setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService); setBukkitServiceToScheduleSyncTaskFromOptionallyAsyncTask(bukkitService);
setBukkitServiceToRunTaskOptionallyAsync(bukkitService); setBukkitServiceToRunTaskOptionallyAsync(bukkitService);

View File

@ -149,7 +149,7 @@ public class TempbanManagerTest {
String ip = "123.45.67.89"; String ip = "123.45.67.89";
TestHelper.mockPlayerIp(player, ip); TestHelper.mockPlayerIp(player, ip);
String banReason = "IP ban too many logins"; String banReason = "IP ban too many logins";
given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); given(messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason);
Settings settings = mockSettings(2, 100, ""); Settings settings = mockSettings(2, 100, "");
TempbanManager manager = new TempbanManager(bukkitService, messages, settings); TempbanManager manager = new TempbanManager(bukkitService, messages, settings);
setBukkitServiceToScheduleSyncDelayedTask(bukkitService); setBukkitServiceToScheduleSyncDelayedTask(bukkitService);
@ -195,7 +195,7 @@ public class TempbanManagerTest {
String ip = "22.44.66.88"; String ip = "22.44.66.88";
TestHelper.mockPlayerIp(player, ip); TestHelper.mockPlayerIp(player, ip);
String banReason = "kick msg"; String banReason = "kick msg";
given(messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason); given(messages.retrieveSingle(player, MessageKey.TEMPBAN_MAX_LOGINS)).willReturn(banReason);
Settings settings = mockSettings(10, 60, ""); Settings settings = mockSettings(10, 60, "");
TempbanManager manager = new TempbanManager(bukkitService, messages, settings); TempbanManager manager = new TempbanManager(bukkitService, messages, settings);
manager.increaseCount(ip, "user"); manager.increaseCount(ip, "user");

View File

@ -14,6 +14,7 @@ import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
/** /**
* Contains matchers for LimboPlayer. * Contains matchers for LimboPlayer.
*/ */
@SuppressWarnings("checkstyle:JavadocMethod") // Justification: Javadoc would be huge because of the many parameters
public final class LimboPlayerMatchers { public final class LimboPlayerMatchers {
private LimboPlayerMatchers() { private LimboPlayerMatchers() {
@ -45,7 +46,8 @@ public final class LimboPlayerMatchers {
@Override @Override
public void describeMismatchSafely(LimboPlayer item, Description description) { public void describeMismatchSafely(LimboPlayer item, Description description) {
description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s, walkSpeed=%f, flySpeed=%f", description.appendText(format("Limbo with isOp=%s, groups={%s}, canFly=%s, walkSpeed=%f, flySpeed=%f",
item.isOperator(), String.join(" ,", item.getGroups()), item.isCanFly(), item.getWalkSpeed(), item.getFlySpeed())); item.isOperator(), String.join(" ,", item.getGroups()), item.isCanFly(),
item.getWalkSpeed(), item.getFlySpeed()));
} }
}; };
} }

View File

@ -71,7 +71,7 @@ public class LimboPlayerTaskManagerTest {
Player player = mock(Player.class); Player player = mock(Player.class);
LimboPlayer limboPlayer = mock(LimboPlayer.class); LimboPlayer limboPlayer = mock(LimboPlayer.class);
MessageKey key = MessageKey.REGISTER_MESSAGE; MessageKey key = MessageKey.REGISTER_MESSAGE;
given(messages.retrieveSingle(key)).willReturn("Please register!"); given(messages.retrieveSingle(player, key)).willReturn("Please register!");
int interval = 12; int interval = 12;
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(interval); given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(interval);
@ -80,7 +80,7 @@ public class LimboPlayerTaskManagerTest {
// then // then
verify(limboPlayer).setMessageTask(any(MessageTask.class)); verify(limboPlayer).setMessageTask(any(MessageTask.class));
verify(messages).retrieveSingle(key); verify(messages).retrieveSingle(player, key);
verify(bukkitService).runTaskTimer( verify(bukkitService).runTaskTimer(
any(MessageTask.class), eq(2L * TICKS_PER_SECOND), eq((long) interval * TICKS_PER_SECOND)); any(MessageTask.class), eq(2L * TICKS_PER_SECOND), eq((long) interval * TICKS_PER_SECOND));
} }
@ -110,7 +110,7 @@ public class LimboPlayerTaskManagerTest {
MessageTask existingMessageTask = mock(MessageTask.class); MessageTask existingMessageTask = mock(MessageTask.class);
limboPlayer.setMessageTask(existingMessageTask); limboPlayer.setMessageTask(existingMessageTask);
given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(8); given(settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)).willReturn(8);
given(messages.retrieveSingle(MessageKey.REGISTER_MESSAGE)).willReturn("Please register!"); given(messages.retrieveSingle(player, MessageKey.REGISTER_MESSAGE)).willReturn("Please register!");
// when // when
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false);
@ -119,7 +119,7 @@ public class LimboPlayerTaskManagerTest {
assertThat(limboPlayer.getMessageTask(), not(nullValue())); assertThat(limboPlayer.getMessageTask(), not(nullValue()));
assertThat(limboPlayer.getMessageTask(), not(sameInstance(existingMessageTask))); assertThat(limboPlayer.getMessageTask(), not(sameInstance(existingMessageTask)));
verify(registrationCaptchaManager).isCaptchaRequired(name); verify(registrationCaptchaManager).isCaptchaRequired(name);
verify(messages).retrieveSingle(MessageKey.REGISTER_MESSAGE); verify(messages).retrieveSingle(player, MessageKey.REGISTER_MESSAGE);
verify(existingMessageTask).cancel(); verify(existingMessageTask).cancel();
} }
@ -134,14 +134,14 @@ public class LimboPlayerTaskManagerTest {
given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true); given(registrationCaptchaManager.isCaptchaRequired(name)).willReturn(true);
String captcha = "M032"; String captcha = "M032";
given(registrationCaptchaManager.getCaptchaCodeOrGenerateNew(name)).willReturn(captcha); given(registrationCaptchaManager.getCaptchaCodeOrGenerateNew(name)).willReturn(captcha);
given(messages.retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha)).willReturn("Need to use captcha"); given(messages.retrieveSingle(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha)).willReturn("Need to use captcha");
// when // when
limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false); limboPlayerTaskManager.registerMessageTask(player, limboPlayer, false);
// then // then
assertThat(limboPlayer.getMessageTask(), not(nullValue())); assertThat(limboPlayer.getMessageTask(), not(nullValue()));
verify(messages).retrieveSingle(MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha); verify(messages).retrieveSingle(player, MessageKey.CAPTCHA_FOR_REGISTRATION_REQUIRED, captcha);
} }
@Test @Test
@ -159,7 +159,7 @@ public class LimboPlayerTaskManagerTest {
// then // then
verify(limboPlayer).setTimeoutTask(bukkitTask); verify(limboPlayer).setTimeoutTask(bukkitTask);
verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(600L)); // 30 * TICKS_PER_SECOND verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(600L)); // 30 * TICKS_PER_SECOND
verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR); verify(messages).retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR);
} }
@Test @Test
@ -194,7 +194,7 @@ public class LimboPlayerTaskManagerTest {
verify(existingTask).cancel(); verify(existingTask).cancel();
assertThat(limboPlayer.getTimeoutTask(), equalTo(bukkitTask)); assertThat(limboPlayer.getTimeoutTask(), equalTo(bukkitTask));
verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(360L)); // 18 * TICKS_PER_SECOND verify(bukkitService).runTaskLater(any(TimeoutTask.class), eq(360L)); // 18 * TICKS_PER_SECOND
verify(messages).retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR); verify(messages).retrieveSingle(player, MessageKey.LOGIN_TIMEOUT_ERROR);
} }
} }

View File

@ -27,6 +27,15 @@ public final class SqlDataSourceTestUtil {
return new MySQL(settings, hikariDataSource, extensionsFactory); return new MySQL(settings, hikariDataSource, extensionsFactory);
} }
/**
* Creates a SQLite implementation for testing purposes. Methods are overridden so the
* provided connection is never overridden.
*
* @param settings settings instance
* @param dataFolder data folder
* @param connection connection to use
* @return the created SQLite instance
*/
public static SQLite createSqlite(Settings settings, File dataFolder, Connection connection) { public static SQLite createSqlite(Settings settings, File dataFolder, Connection connection) {
return new SQLite(settings, dataFolder, connection) { return new SQLite(settings, dataFolder, connection) {
// Override reload() so it doesn't run SQLite#connect, since we're given a specific Connection to use // Override reload() so it doesn't run SQLite#connect, since we're given a specific Connection to use

View File

@ -101,7 +101,7 @@ public class OnJoinVerifierTest {
event.setResult(PlayerLoginEvent.Result.KICK_FULL); event.setResult(PlayerLoginEvent.Result.KICK_FULL);
given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(false); given(permissionsManager.hasPermission(player, PlayerStatePermission.IS_VIP)).willReturn(false);
String serverFullMessage = "server is full"; String serverFullMessage = "server is full";
given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)).willReturn(serverFullMessage); given(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)).willReturn(serverFullMessage);
// when // when
boolean result = onJoinVerifier.refusePlayerForFullServer(event); boolean result = onJoinVerifier.refusePlayerForFullServer(event);
@ -125,7 +125,7 @@ public class OnJoinVerifierTest {
given(permissionsManager.hasPermission(onlinePlayers.get(1), PlayerStatePermission.IS_VIP)).willReturn(false); given(permissionsManager.hasPermission(onlinePlayers.get(1), PlayerStatePermission.IS_VIP)).willReturn(false);
returnOnlineListFromBukkitServer(onlinePlayers); returnOnlineListFromBukkitServer(onlinePlayers);
given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); given(server.getMaxPlayers()).willReturn(onlinePlayers.size());
given(messages.retrieveSingle(MessageKey.KICK_FOR_VIP)).willReturn("kick for vip"); given(messages.retrieveSingle(player, MessageKey.KICK_FOR_VIP)).willReturn("kick for vip");
// when // when
boolean result = onJoinVerifier.refusePlayerForFullServer(event); boolean result = onJoinVerifier.refusePlayerForFullServer(event);
@ -149,7 +149,7 @@ public class OnJoinVerifierTest {
given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true); given(permissionsManager.hasPermission(onlinePlayers.get(0), PlayerStatePermission.IS_VIP)).willReturn(true);
returnOnlineListFromBukkitServer(onlinePlayers); returnOnlineListFromBukkitServer(onlinePlayers);
given(server.getMaxPlayers()).willReturn(onlinePlayers.size()); given(server.getMaxPlayers()).willReturn(onlinePlayers.size());
given(messages.retrieveSingle(MessageKey.KICK_FULL_SERVER)).willReturn("kick full server"); given(messages.retrieveSingle(player, MessageKey.KICK_FULL_SERVER)).willReturn("kick full server");
// when // when
boolean result = onJoinVerifier.refusePlayerForFullServer(event); boolean result = onJoinVerifier.refusePlayerForFullServer(event);

View File

@ -596,7 +596,7 @@ public class PlayerListenerTest {
MessageKey.INVALID_NAME_CHARACTERS, "[a-z]"); MessageKey.INVALID_NAME_CHARACTERS, "[a-z]");
doThrow(exception).when(onJoinVerifier).checkIsValidName(name); doThrow(exception).when(onJoinVerifier).checkIsValidName(name);
String message = "Invalid characters!"; String message = "Invalid characters!";
given(messages.retrieveSingle(exception.getReason(), exception.getArgs())).willReturn(message); given(messages.retrieveSingle(player, exception.getReason(), exception.getArgs())).willReturn(message);
// when // when
listener.onPlayerLogin(event); listener.onPlayerLogin(event);

View File

@ -81,9 +81,11 @@ public class MessagesIntegrationTest {
public void shouldLoadMessageAndSplitAtNewLines() { public void shouldLoadMessageAndSplitAtNewLines() {
// given // given
MessageKey key = MessageKey.UNKNOWN_USER; MessageKey key = MessageKey.UNKNOWN_USER;
CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
String[] message = messages.retrieve(key); String[] message = messages.retrieve(key, sender);
// then // then
String[] lines = new String[]{"We've got", "new lines", "and ' apostrophes"}; String[] lines = new String[]{"We've got", "new lines", "and ' apostrophes"};
@ -94,9 +96,11 @@ public class MessagesIntegrationTest {
public void shouldLoadMessageAsStringWithNewLines() { public void shouldLoadMessageAsStringWithNewLines() {
// given // given
MessageKey key = MessageKey.UNKNOWN_USER; MessageKey key = MessageKey.UNKNOWN_USER;
CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
String message = messages.retrieveSingle(key); String message = messages.retrieveSingle(sender, key);
// then // then
assertThat(message, equalTo("We've got\nnew lines\nand ' apostrophes")); assertThat(message, equalTo("We've got\nnew lines\nand ' apostrophes"));
@ -106,9 +110,11 @@ public class MessagesIntegrationTest {
public void shouldFormatColorCodes() { public void shouldFormatColorCodes() {
// given // given
MessageKey key = MessageKey.LOGIN_SUCCESS; MessageKey key = MessageKey.LOGIN_SUCCESS;
CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
String[] message = messages.retrieve(key); String[] message = messages.retrieve(key, sender);
// then // then
assertThat(message, arrayWithSize(1)); assertThat(message, arrayWithSize(1));
@ -120,6 +126,7 @@ public class MessagesIntegrationTest {
// given // given
MessageKey key = MessageKey.EMAIL_ALREADY_USED_ERROR; MessageKey key = MessageKey.EMAIL_ALREADY_USED_ERROR;
CommandSender sender = mock(CommandSender.class); CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
messages.send(sender, key); messages.send(sender, key);
@ -133,6 +140,8 @@ public class MessagesIntegrationTest {
// given // given
MessageKey key = MessageKey.LOGIN_SUCCESS; MessageKey key = MessageKey.LOGIN_SUCCESS;
Player player = Mockito.mock(Player.class); Player player = Mockito.mock(Player.class);
given(player.getName()).willReturn("Tester");
given(player.getDisplayName()).willReturn("§cTesty");
// when // when
messages.send(player, key); messages.send(player, key);
@ -146,6 +155,8 @@ public class MessagesIntegrationTest {
// given // given
MessageKey key = MessageKey.UNKNOWN_USER; MessageKey key = MessageKey.UNKNOWN_USER;
Player player = Mockito.mock(Player.class); Player player = Mockito.mock(Player.class);
given(player.getName()).willReturn("Tester");
given(player.getDisplayName()).willReturn("§cTesty");
// when // when
messages.send(player, key); messages.send(player, key);
@ -157,11 +168,27 @@ public class MessagesIntegrationTest {
assertThat(captor.getAllValues(), contains(lines)); assertThat(captor.getAllValues(), contains(lines));
} }
@Test
public void shouldSendMessageToPlayerWithNameReplacement() {
// given
MessageKey key = MessageKey.REGISTER_MESSAGE;
Player player = Mockito.mock(Player.class);
given(player.getName()).willReturn("Tester");
given(player.getDisplayName()).willReturn("§cTesty");
// when
messages.send(player, key);
// then
verify(player).sendMessage("§3Please Tester, register to the §cTesty§3.");
}
@Test @Test
public void shouldSendMessageToPlayerWithTagReplacement() { public void shouldSendMessageToPlayerWithTagReplacement() {
// given // given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
CommandSender sender = Mockito.mock(CommandSender.class); CommandSender sender = Mockito.mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
messages.send(sender, key, "1234"); messages.send(sender, key, "1234");
@ -175,6 +202,7 @@ public class MessagesIntegrationTest {
// given // given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
CommandSender sender = mock(CommandSender.class); CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
messages.send(sender, key); messages.send(sender, key);
@ -189,9 +217,11 @@ public class MessagesIntegrationTest {
Logger logger = mock(Logger.class); Logger logger = mock(Logger.class);
ConsoleLogger.setLogger(logger); ConsoleLogger.setLogger(logger);
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
messages.send(mock(CommandSender.class), key, "rep", "rep2"); messages.send(sender, key, "rep", "rep2");
// then // then
verify(logger).warning(argThat(containsString("Invalid number of replacements"))); verify(logger).warning(argThat(containsString("Invalid number of replacements")));
@ -203,9 +233,11 @@ public class MessagesIntegrationTest {
Logger logger = mock(Logger.class); Logger logger = mock(Logger.class);
ConsoleLogger.setLogger(logger); ConsoleLogger.setLogger(logger);
MessageKey key = MessageKey.UNKNOWN_USER; MessageKey key = MessageKey.UNKNOWN_USER;
CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
messages.send(mock(CommandSender.class), key, "Replacement"); messages.send(sender, key, "Replacement");
// then // then
verify(logger).warning(argThat(containsString("Invalid number of replacements"))); verify(logger).warning(argThat(containsString("Invalid number of replacements")));
@ -216,9 +248,11 @@ public class MessagesIntegrationTest {
// given // given
// Key is present in both files // Key is present in both files
MessageKey key = MessageKey.WRONG_PASSWORD; MessageKey key = MessageKey.WRONG_PASSWORD;
CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
String message = messages.retrieveSingle(key); String message = messages.retrieveSingle(sender, key);
// then // then
assertThat(message, equalTo("§cWrong password!")); assertThat(message, equalTo("§cWrong password!"));
@ -228,9 +262,11 @@ public class MessagesIntegrationTest {
public void shouldRetrieveMessageWithReplacements() { public void shouldRetrieveMessageWithReplacements() {
// given // given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR; MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
CommandSender sender = mock(CommandSender.class);
given(sender.getName()).willReturn("Tester");
// when // when
String result = messages.retrieveSingle(key, "24680"); String result = messages.retrieveSingle(sender.getName(), key, "24680");
// then // then
assertThat(result, equalTo("Use /captcha 24680 to solve the captcha")); assertThat(result, equalTo("Use /captcha 24680 to solve the captcha"));

View File

@ -83,15 +83,16 @@ public class CommonServiceTest {
public void shouldRetrieveSingleMessage() { public void shouldRetrieveSingleMessage() {
// given // given
MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED; MessageKey key = MessageKey.ACCOUNT_NOT_ACTIVATED;
Player player = mock(Player.class);
String text = "Test text"; String text = "Test text";
given(messages.retrieveSingle(key)).willReturn(text); given(messages.retrieveSingle(player, key)).willReturn(text);
// when // when
String result = commonService.retrieveSingleMessage(key); String result = commonService.retrieveSingleMessage(player, key);
// then // then
assertThat(result, equalTo(text)); assertThat(result, equalTo(text));
verify(messages).retrieveSingle(key); verify(messages).retrieveSingle(player, key);
} }
@Test @Test

View File

@ -1,7 +1,13 @@
package fr.xephi.authme.service; package fr.xephi.authme.service;
import com.maxmind.geoip.Country; import com.maxmind.db.GeoIp2Provider;
import com.maxmind.geoip.LookupService; import com.maxmind.db.model.Country;
import com.maxmind.db.model.CountryResponse;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -10,13 +16,11 @@ import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import java.io.File; import static org.mockito.ArgumentMatchers.any;
import java.io.IOException; import static org.mockito.BDDMockito.given;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -29,8 +33,12 @@ public class GeoIpServiceTest {
private GeoIpService geoIpService; private GeoIpService geoIpService;
private File dataFolder; private File dataFolder;
@Mock @Mock
private LookupService lookupService; private GeoIp2Provider lookupService;
@Mock
private BukkitService bukkitService;
@Rule @Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder(); public TemporaryFolder temporaryFolder = new TemporaryFolder();
@ -38,20 +46,24 @@ public class GeoIpServiceTest {
@Before @Before
public void initializeGeoLiteApi() throws IOException { public void initializeGeoLiteApi() throws IOException {
dataFolder = temporaryFolder.newFolder(); dataFolder = temporaryFolder.newFolder();
geoIpService = new GeoIpService(dataFolder, lookupService); geoIpService = new GeoIpService(dataFolder, bukkitService, lookupService);
} }
@Test @Test
public void shouldGetCountry() { public void shouldGetCountry() throws Exception {
// given // given
String ip = "123.45.67.89"; InetAddress ip = InetAddress.getByName("123.45.67.89");
String countryCode = "XX"; String countryCode = "XX";
Country country = mock(Country.class); Country country = mock(Country.class);
given(country.getCode()).willReturn(countryCode); given(country.getIsoCode()).willReturn(countryCode);
given(lookupService.getCountry(ip)).willReturn(country);
CountryResponse response = mock(CountryResponse.class);
given(response.getCountry()).willReturn(country);
given(lookupService.getCountry(ip)).willReturn(response);
// when // when
String result = geoIpService.getCountryCode(ip); String result = geoIpService.getCountryCode(ip.getHostAddress());
// then // then
assertThat(result, equalTo(countryCode)); assertThat(result, equalTo(countryCode));
@ -59,7 +71,7 @@ public class GeoIpServiceTest {
} }
@Test @Test
public void shouldNotLookUpCountryForLocalhostIp() { public void shouldNotLookUpCountryForLocalhostIp() throws Exception {
// given // given
String ip = "127.0.0.1"; String ip = "127.0.0.1";
@ -68,20 +80,24 @@ public class GeoIpServiceTest {
// then // then
assertThat(result, equalTo("--")); assertThat(result, equalTo("--"));
verify(lookupService, never()).getCountry(anyString()); verify(lookupService, never()).getCountry(any());
} }
@Test @Test
public void shouldLookUpCountryName() { public void shouldLookUpCountryName() throws Exception {
// given // given
String ip = "24.45.167.89"; InetAddress ip = InetAddress.getByName("24.45.167.89");
String countryName = "Ecuador"; String countryName = "Ecuador";
Country country = mock(Country.class); Country country = mock(Country.class);
given(country.getName()).willReturn(countryName); given(country.getName()).willReturn(countryName);
given(lookupService.getCountry(ip)).willReturn(country);
CountryResponse response = mock(CountryResponse.class);
given(response.getCountry()).willReturn(country);
given(lookupService.getCountry(ip)).willReturn(response);
// when // when
String result = geoIpService.getCountryName(ip); String result = geoIpService.getCountryName(ip.getHostAddress());
// then // then
assertThat(result, equalTo(countryName)); assertThat(result, equalTo(countryName));
@ -89,16 +105,15 @@ public class GeoIpServiceTest {
} }
@Test @Test
public void shouldNotLookUpCountryNameForLocalhostIp() { public void shouldNotLookUpCountryNameForLocalhostIp() throws Exception {
// given // given
String ip = "127.0.0.1"; InetAddress ip = InetAddress.getByName("127.0.0.1");
// when // when
String result = geoIpService.getCountryName(ip); String result = geoIpService.getCountryName(ip.getHostAddress());
// then // then
assertThat(result, equalTo("N/A")); assertThat(result, equalTo("N/A"));
verify(lookupService, never()).getCountry(ip); verify(lookupService, never()).getCountry(ip);
} }
} }

View File

@ -106,7 +106,6 @@ public class SessionServiceTest {
// then // then
assertThat(result, equalTo(false)); assertThat(result, equalTo(false));
verify(commonService).getProperty(PluginSettings.SESSIONS_ENABLED); verify(commonService).getProperty(PluginSettings.SESSIONS_ENABLED);
verify(commonService).send(player, MessageKey.SESSION_EXPIRED);
verify(dataSource).hasSession(name); verify(dataSource).hasSession(name);
verify(dataSource).setUnlogged(name); verify(dataSource).setUnlogged(name);
verify(dataSource).revokeSession(name); verify(dataSource).revokeSession(name);
@ -132,7 +131,6 @@ public class SessionServiceTest {
// then // then
assertThat(result, equalTo(false)); assertThat(result, equalTo(false));
verify(commonService).getProperty(PluginSettings.SESSIONS_ENABLED); verify(commonService).getProperty(PluginSettings.SESSIONS_ENABLED);
verify(commonService).send(player, MessageKey.SESSION_EXPIRED);
verify(dataSource).hasSession(name); verify(dataSource).hasSession(name);
verify(dataSource).setUnlogged(name); verify(dataSource).setUnlogged(name);
verify(dataSource).revokeSession(name); verify(dataSource).revokeSession(name);
@ -145,9 +143,10 @@ public class SessionServiceTest {
String ip = "127.3.12.15"; String ip = "127.3.12.15";
Player player = mockPlayerWithNameAndIp(name, ip); Player player = mockPlayerWithNameAndIp(name, ip);
given(dataSource.hasSession(name)).willReturn(true); given(dataSource.hasSession(name)).willReturn(true);
given(commonService.getProperty(PluginSettings.SESSIONS_TIMEOUT)).willReturn(8);
PlayerAuth auth = PlayerAuth.builder() PlayerAuth auth = PlayerAuth.builder()
.name(name) .name(name)
.lastLogin(System.currentTimeMillis()) .lastLogin(System.currentTimeMillis() - 7 * 60 * 1000)
.lastIp("8.8.8.8").build(); .lastIp("8.8.8.8").build();
given(dataSource.getAuth(name)).willReturn(auth); given(dataSource.getAuth(name)).willReturn(auth);
@ -219,6 +218,7 @@ public class SessionServiceTest {
String name = "Charles"; String name = "Charles";
Player player = mockPlayerWithNameAndIp(name, "144.117.118.145"); Player player = mockPlayerWithNameAndIp(name, "144.117.118.145");
given(dataSource.hasSession(name)).willReturn(true); given(dataSource.hasSession(name)).willReturn(true);
given(commonService.getProperty(PluginSettings.SESSIONS_TIMEOUT)).willReturn(8);
PlayerAuth auth = PlayerAuth.builder() PlayerAuth auth = PlayerAuth.builder()
.name(name) .name(name)
.lastIp(null) .lastIp(null)

View File

@ -131,7 +131,8 @@ public class DrawDependency implements ToolTask {
private Class<?> unwrapGenericClass(Type genericType) { private Class<?> unwrapGenericClass(Type genericType) {
if (genericType == Factory.class || genericType == SingletonStore.class) { if (genericType == Factory.class || genericType == SingletonStore.class) {
Class<?> parameterType = ReflectionUtils.getGenericType(genericType); Class<?> parameterType = ReflectionUtils.getGenericType(genericType);
Objects.requireNonNull(parameterType, "Parameter type for '" + genericType + "' should be a concrete class"); Objects.requireNonNull(parameterType,
"Parameter type for '" + genericType + "' should be a concrete class");
return parameterType; return parameterType;
} }
return InjectorUtils.convertToClass(genericType); return InjectorUtils.convertToClass(genericType);

View File

@ -56,6 +56,12 @@ public class EncryptionMethodInfoGatherer {
} }
} }
/**
* Creates a description of the given hash algorithm based on its annotations.
*
* @param algorithm the algorithm to describe
* @return description of the hash algorithm
*/
private static MethodDescription createDescription(HashAlgorithm algorithm) { private static MethodDescription createDescription(HashAlgorithm algorithm) {
Class<? extends EncryptionMethod> clazz = algorithm.getClazz(); Class<? extends EncryptionMethod> clazz = algorithm.getClazz();
EncryptionMethod method = createEncryptionMethod(clazz); EncryptionMethod method = createEncryptionMethod(clazz);

View File

@ -37,6 +37,7 @@ public class PermissionNodesGatherer {
/** /**
* Return a sorted collection of all permission nodes, including its JavaDoc description. * Return a sorted collection of all permission nodes, including its JavaDoc description.
* *
* @param <T> permission node enum type
* @return Ordered map whose keys are the permission nodes and the values the associated JavaDoc * @return Ordered map whose keys are the permission nodes and the values the associated JavaDoc
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -4,18 +4,20 @@ import tools.utils.AutoToolTask;
import tools.utils.FileIoUtils; import tools.utils.FileIoUtils;
import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValue.NestedTagValue;
import tools.utils.TagValueHolder; import tools.utils.TagValueHolder;
import tools.utils.ToolsConstants;
import java.util.Map; import java.util.Map;
import static tools.utils.ToolsConstants.DOCS_FOLDER;
import static tools.utils.ToolsConstants.TOOLS_SOURCE_ROOT;
/** /**
* Task responsible for formatting a permissions node list and * Task responsible for formatting a permissions node list and
* for writing it to a file if desired. * for writing it to a file if desired.
*/ */
public class PermissionsListWriter implements AutoToolTask { public class PermissionsListWriter implements AutoToolTask {
private static final String TEMPLATE_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/permissions/permission_nodes.tpl.md"; private static final String TEMPLATE_FILE = TOOLS_SOURCE_ROOT + "docs/permissions/permission_nodes.tpl.md";
private static final String PERMISSIONS_OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "permission_nodes.md"; private static final String PERMISSIONS_OUTPUT_FILE = DOCS_FOLDER + "permission_nodes.md";
@Override @Override
public String getTaskName() { public String getTaskName() {

View File

@ -45,10 +45,10 @@ public class TranslationPageGenerator implements AutoToolTask {
NestedTagValue translationValuesHolder = new NestedTagValue(); NestedTagValue translationValuesHolder = new NestedTagValue();
for (TranslationInfo translation : gatherer.getTranslationInfo()) { for (TranslationInfo translation : gatherer.getTranslationInfo()) {
int percentage = (int) Math.round(translation.percentTranslated * 100); int percentage = (int) Math.round(translation.getPercentTranslated() * 100);
String name = firstNonNull(LANGUAGE_NAMES.get(translation.code), "?"); String name = firstNonNull(LANGUAGE_NAMES.get(translation.getCode()), "?");
TagValueHolder valueHolder = TagValueHolder.create() TagValueHolder valueHolder = TagValueHolder.create()
.put("code", translation.code) .put("code", translation.getCode())
.put("name", name) .put("name", name)
.put("percentage", Integer.toString(percentage)) .put("percentage", Integer.toString(percentage))
.put("color", computeColor(percentage)); .put("color", computeColor(percentage));

View File

@ -25,7 +25,7 @@ public class TranslationsGatherer {
public TranslationsGatherer() { public TranslationsGatherer() {
gatherTranslations(); gatherTranslations();
translationInfo.sort((e1, e2) -> getCode(e1).compareTo(getCode(e2))); translationInfo.sort((e1, e2) -> getSortCode(e1).compareTo(getSortCode(e2)));
} }
public List<TranslationInfo> getTranslationInfo() { public List<TranslationInfo> getTranslationInfo() {
@ -61,16 +61,6 @@ public class TranslationsGatherer {
return null; return null;
} }
public static final class TranslationInfo {
public final String code;
public final double percentTranslated;
TranslationInfo(String code, double percentTranslated) {
this.code = code;
this.percentTranslated = percentTranslated;
}
}
/** /**
* Returns the language code from the translation info for sorting purposes. * Returns the language code from the translation info for sorting purposes.
* Returns "a" for "en" language code to sort English on top. * Returns "a" for "en" language code to sort English on top.
@ -78,8 +68,26 @@ public class TranslationsGatherer {
* @param info the translation info * @param info the translation info
* @return the language code for sorting * @return the language code for sorting
*/ */
private static String getCode(TranslationInfo info) { private static String getSortCode(TranslationInfo info) {
return "en".equals(info.code) ? "a" : info.code; return "en".equals(info.code) ? "a" : info.code;
} }
public static final class TranslationInfo {
private final String code;
private final double percentTranslated;
TranslationInfo(String code, double percentTranslated) {
this.code = code;
this.percentTranslated = percentTranslated;
}
public String getCode() {
return code;
}
public double getPercentTranslated() {
return percentTranslated;
}
}
} }

View File

@ -67,7 +67,7 @@ public class GeneratePluginYml implements AutoToolTask {
List<String> pluginYmlLines = FileIoUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE)); List<String> pluginYmlLines = FileIoUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE));
int lineNr = 0; int lineNr = 0;
for (String line : pluginYmlLines) { for (String line : pluginYmlLines) {
if (line.equals("commands:")) { if ("commands:".equals(line)) {
break; break;
} }
++lineNr; ++lineNr;

View File

@ -33,8 +33,8 @@ public class CheckMessageKeyUsages implements AutoToolTask {
if (unusedKeys.isEmpty()) { if (unusedKeys.isEmpty()) {
System.out.println("No unused MessageKey entries found :)"); System.out.println("No unused MessageKey entries found :)");
} else { } else {
System.out.println("Did not find usages for keys:\n- " + System.out.println("Did not find usages for keys:\n- "
String.join("\n- ", Lists.transform(unusedKeys, MessageKey::name))); + String.join("\n- ", Lists.transform(unusedKeys, MessageKey::name)));
} }
} }
@ -51,21 +51,6 @@ public class CheckMessageKeyUsages implements AutoToolTask {
return keys; return keys;
} }
private List<File> findUsagesOfKey(MessageKey key) {
List<File> filesUsingKey = new ArrayList<>();
File sourceFolder = new File(ToolsConstants.MAIN_SOURCE_ROOT);
Consumer<File> usagesCollector = file -> {
String source = FileIoUtils.readFromFile(file.toPath());
if (source.contains(key.name())) {
filesUsingKey.add(file);
}
};
walkJavaFileTree(sourceFolder, usagesCollector);
return filesUsingKey;
}
private static void walkJavaFileTree(File folder, Consumer<File> javaFileConsumer) { private static void walkJavaFileTree(File folder, Consumer<File> javaFileConsumer) {
for (File file : FileIoUtils.listFilesOrThrow(folder)) { for (File file : FileIoUtils.listFilesOrThrow(folder)) {
if (file.isDirectory()) { if (file.isDirectory()) {

View File

@ -27,6 +27,12 @@ public final class FileIoUtils {
writeToFile(Paths.get(outputFile), contents); writeToFile(Paths.get(outputFile), contents);
} }
/**
* Writes the given contents to the file, overriding any existing content.
*
* @param path the file to write to
* @param contents the contents to write
*/
public static void writeToFile(Path path, String contents) { public static void writeToFile(Path path, String contents) {
try { try {
Files.write(path, contents.getBytes()); Files.write(path, contents.getBytes());
@ -35,6 +41,12 @@ public final class FileIoUtils {
} }
} }
/**
* Adds the given contents to the file while keeping any existing content.
*
* @param outputFile the file to write to
* @param contents the contents to append
*/
public static void appendToFile(String outputFile, String contents) { public static void appendToFile(String outputFile, String contents) {
try { try {
Files.write(Paths.get(outputFile), contents.getBytes(), StandardOpenOption.APPEND); Files.write(Paths.get(outputFile), contents.getBytes(), StandardOpenOption.APPEND);
@ -47,6 +59,12 @@ public final class FileIoUtils {
return readFromFile(Paths.get(file)); return readFromFile(Paths.get(file));
} }
/**
* Returns the given file's contents as string.
*
* @param file the file to read
* @return the file's contents
*/
public static String readFromFile(Path file) { public static String readFromFile(Path file) {
try { try {
return new String(Files.readAllBytes(file), StandardCharsets.UTF_8); return new String(Files.readAllBytes(file), StandardCharsets.UTF_8);
@ -55,6 +73,12 @@ public final class FileIoUtils {
} }
} }
/**
* Returns the lines of the given file.
*
* @param path the path of the file to read
* @return the lines of the file
*/
public static List<String> readLinesFromFile(Path path) { public static List<String> readLinesFromFile(Path path) {
try { try {
return Files.readAllLines(path, StandardCharsets.UTF_8); return Files.readAllLines(path, StandardCharsets.UTF_8);

View File

@ -2,6 +2,8 @@
error: error:
unregistered_user: 'We''ve got%nl%new lines%nl%and '' apostrophes' unregistered_user: 'We''ve got%nl%new lines%nl%and '' apostrophes'
registration:
register_request: '&3Please %username%, register to the %displayname%&3.'
login: login:
success: '&cHere we have&bdefined some colors &dand some other &lthings' success: '&cHere we have&bdefined some colors &dand some other &lthings'
wrong_password: '&cWrong password!' wrong_password: '&cWrong password!'