Merge pull request #1431 from AuthMe/5.5-dev

5.5.0 [WIP]
This commit is contained in:
Gabriele C 2017-12-01 09:58:40 +01:00 committed by GitHub
commit c150adb665
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 494 additions and 237 deletions

View File

@ -1,8 +1,8 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Tue Oct 31 15:56:59 CET 2017. See docs/config/config.tpl.md --> <!-- File auto-generated on Tue Nov 28 12:49:57 CET 2017. See docs/config/config.tpl.md -->
## AuthMe Configuration ## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
with which you can configure various settings. This following is the initial contents of with which you can configure various settings. This following is the initial contents of
the generated config.yml file. the generated config.yml file.
@ -131,7 +131,7 @@ settings:
# Hide the chat log from players who are not authenticated? # Hide the chat log from players who are not authenticated?
hideChat: false hideChat: false
# Allowed commands for unauthenticated players # Allowed commands for unauthenticated players
allowCommands: allowCommands:
- '/login' - '/login'
- '/register' - '/register'
- '/l' - '/l'
@ -158,7 +158,7 @@ settings:
enabled: false enabled: false
# WorldNames where we need to force the spawn location # WorldNames where we need to force the spawn location
# Case-sensitive! # Case-sensitive!
worlds: worlds:
- 'world' - 'world'
- 'world_nether' - 'world_nether'
- 'world_the_end' - 'world_the_end'
@ -202,8 +202,8 @@ settings:
# Should we display all other accounts from a player when he joins? # Should we display all other accounts from a player when he joins?
# permission: /authme.admin.accounts # permission: /authme.admin.accounts
displayOtherAccounts: true displayOtherAccounts: true
# Spawn priority; values: authme, essentials, multiverse, default # Spawn priority; values: authme, essentials, cmi, multiverse, default
spawnPriority: 'authme,essentials,multiverse,default' spawnPriority: 'authme,essentials,cmi,multiverse,default'
# Maximum Login authorized by IP # Maximum Login authorized by IP
maxLoginPerIp: 0 maxLoginPerIp: 0
# Maximum Join authorized by IP # Maximum Join authorized by IP
@ -258,7 +258,7 @@ settings:
# - '123456' # - '123456'
# - 'password' # - 'password'
# - 'help' # - 'help'
unsafePasswords: unsafePasswords:
- '123456' - '123456'
- 'password' - 'password'
- 'qwerty' - 'qwerty'
@ -364,7 +364,7 @@ Email:
# Delay in minute for the recall scheduler # Delay in minute for the recall scheduler
delayRecall: 5 delayRecall: 5
# Blacklist these domains for emails # Blacklist these domains for emails
emailBlacklisted: emailBlacklisted:
- '10minutemail.com' - '10minutemail.com'
# Whitelist ONLY these domains for emails # Whitelist ONLY these domains for emails
emailWhitelisted: [] emailWhitelisted: []
@ -391,12 +391,12 @@ Protection:
# 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/ # http://dev.maxmind.com/geoip/legacy/codes/iso3166/
# PLEASE USE QUOTES! # PLEASE USE QUOTES!
countries: countries:
- 'US' - 'US'
- 'GB' - 'GB'
# Countries not allowed to join the server and register # Countries not allowed to join the server and register
# PLEASE USE QUOTES! # PLEASE USE QUOTES!
countriesBlacklist: countriesBlacklist:
- 'A1' - 'A1'
# Do we need to enable automatic antibot system? # Do we need to enable automatic antibot system?
enableAntiBot: true enableAntiBot: true
@ -555,9 +555,9 @@ Converter:
password: '' password: ''
``` ```
To change settings on a running server, save your changes to config.yml and use To change settings on a running server, save your changes to config.yml and use
`/authme reload`. `/authme reload`.
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Tue Oct 31 15:56:59 CET 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Tue Nov 28 12:49:57 CET 2017

View File

@ -6,7 +6,7 @@
<groupId>fr.xephi</groupId> <groupId>fr.xephi</groupId>
<artifactId>authme</artifactId> <artifactId>authme</artifactId>
<version>5.4.0</version> <version>5.5.0-SNAPSHOT</version>
<name>AuthMeReloaded</name> <name>AuthMeReloaded</name>
<description>The first authentication plugin for the Bukkit API!</description> <description>The first authentication plugin for the Bukkit API!</description>
@ -302,7 +302,7 @@
<dependency> <dependency>
<groupId>ch.jalu</groupId> <groupId>ch.jalu</groupId>
<artifactId>injector</artifactId> <artifactId>injector</artifactId>
<version>0.4.1</version> <version>1.0</version>
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>

View File

@ -12,8 +12,6 @@ import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
import fr.xephi.authme.initialization.OnStartupTasks; import fr.xephi.authme.initialization.OnStartupTasks;
import fr.xephi.authme.initialization.SettingsProvider; import fr.xephi.authme.initialization.SettingsProvider;
import fr.xephi.authme.initialization.TaskCloser; import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.initialization.factory.FactoryDependencyHandler;
import fr.xephi.authme.initialization.factory.SingletonStoreDependencyHandler;
import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.listener.EntityListener; import fr.xephi.authme.listener.EntityListener;
import fr.xephi.authme.listener.PlayerListener; import fr.xephi.authme.listener.PlayerListener;
@ -201,7 +199,6 @@ public class AuthMe extends JavaPlugin {
// Create injector, provide elements from the Bukkit environment and register providers // Create injector, provide elements from the Bukkit environment and register providers
injector = new InjectorBuilder() injector = new InjectorBuilder()
.addHandlers(new FactoryDependencyHandler(), new SingletonStoreDependencyHandler())
.addDefaultHandlers("fr.xephi.authme") .addDefaultHandlers("fr.xephi.authme")
.create(); .create();
injector.register(AuthMe.class, this); injector.register(AuthMe.class, this);

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.command; package fr.xephi.authme.command;
import ch.jalu.injector.factory.Factory;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages; import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;

View File

@ -17,6 +17,7 @@ import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand;
import fr.xephi.authme.command.executable.authme.PurgeCommand; import fr.xephi.authme.command.executable.authme.PurgeCommand;
import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand; import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand;
import fr.xephi.authme.command.executable.authme.PurgePlayerCommand; import fr.xephi.authme.command.executable.authme.PurgePlayerCommand;
import fr.xephi.authme.command.executable.authme.RecentPlayersCommand;
import fr.xephi.authme.command.executable.authme.RegisterAdminCommand; import fr.xephi.authme.command.executable.authme.RegisterAdminCommand;
import fr.xephi.authme.command.executable.authme.ReloadCommand; import fr.xephi.authme.command.executable.authme.ReloadCommand;
import fr.xephi.authme.command.executable.authme.SetEmailCommand; import fr.xephi.authme.command.executable.authme.SetEmailCommand;
@ -433,6 +434,15 @@ public class CommandInitializer {
.executableCommand(MessagesCommand.class) .executableCommand(MessagesCommand.class)
.register(); .register();
CommandDescription.builder()
.parent(authmeBase)
.labels("recent")
.description("See players who have recently logged in")
.detailedDescription("Shows the last players that have logged in.")
.permission(AdminPermission.SEE_RECENT_PLAYERS)
.executableCommand(RecentPlayersCommand.class)
.register();
CommandDescription.builder() CommandDescription.builder()
.parent(authmeBase) .parent(authmeBase)
.labels("debug", "dbg") .labels("debug", "dbg")

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.factory.Factory;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedMap;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
@ -13,7 +14,6 @@ import fr.xephi.authme.datasource.converter.RoyalAuthConverter;
import fr.xephi.authme.datasource.converter.SqliteToSql; import fr.xephi.authme.datasource.converter.SqliteToSql;
import fr.xephi.authme.datasource.converter.VAuthConverter; import fr.xephi.authme.datasource.converter.VAuthConverter;
import fr.xephi.authme.datasource.converter.XAuthConverter; import fr.xephi.authme.datasource.converter.XAuthConverter;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;

View File

@ -0,0 +1,50 @@
package fr.xephi.authme.command.executable.authme;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import javax.inject.Inject;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import static java.time.Instant.ofEpochMilli;
/**
* Command showing the most recent logged in players.
*/
public class RecentPlayersCommand implements ExecutableCommand {
/** DateTime formatter, producing Strings such as "10:42 AM, 11 Jul". */
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("hh:mm a, dd MMM");
@Inject
private DataSource dataSource;
@Override
public void executeCommand(CommandSender sender, List<String> arguments) {
List<PlayerAuth> recentPlayers = dataSource.getRecentlyLoggedInPlayers();
sender.sendMessage(ChatColor.BLUE + "[AuthMe] Recently logged in players");
for (PlayerAuth auth : recentPlayers) {
sender.sendMessage(formatPlayerMessage(auth));
}
}
@VisibleForTesting
ZoneId getZoneId() {
return ZoneId.systemDefault();
}
private String formatPlayerMessage(PlayerAuth auth) {
LocalDateTime lastLogin = LocalDateTime.ofInstant(ofEpochMilli(auth.getLastLogin()), getZoneId());
String lastLoginText = DATE_FORMAT.format(lastLogin);
return "- " + auth.getRealName() + " (" + lastLoginText + " with IP " + auth.getLastIp() + ")";
}
}

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.initialization.factory.SingletonStore;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.authme.debug; package fr.xephi.authme.command.executable.authme.debug;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.data.limbo.LimboService; import fr.xephi.authme.data.limbo.LimboService;
import fr.xephi.authme.datasource.CacheDataSource; import fr.xephi.authme.datasource.CacheDataSource;
@ -7,7 +8,6 @@ import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.initialization.factory.SingletonStore;
import fr.xephi.authme.permission.DebugSectionPermissions; import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionNode;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.command.executable.authme.debug; package fr.xephi.authme.command.executable.authme.debug;
import ch.jalu.injector.factory.Factory;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.data.limbo.persistence; package fr.xephi.authme.data.limbo.persistence;
import ch.jalu.injector.factory.Factory;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.LimboSettings; import fr.xephi.authme.settings.properties.LimboSettings;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;

View File

@ -263,6 +263,11 @@ public class CacheDataSource implements DataSource {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
return source.getRecentlyLoggedInPlayers();
}
@Override @Override
public void invalidateCache(String playerName) { public void invalidateCache(String playerName) {
cachedAuths.invalidate(playerName); cachedAuths.invalidate(playerName);

View File

@ -225,6 +225,13 @@ public interface DataSource extends Reloadable {
*/ */
List<PlayerAuth> getAllAuths(); List<PlayerAuth> getAllAuths();
/**
* Returns the last ten players who have recently logged in (first ten players with highest last login date).
*
* @return the 10 last players who last logged in
*/
List<PlayerAuth> getRecentlyLoggedInPlayers();
/** /**
* Reload the data source. * Reload the data source.
*/ */

View File

@ -393,6 +393,11 @@ public class FlatFile implements DataSource {
throw new UnsupportedOperationException("Flat file no longer supported"); throw new UnsupportedOperationException("Flat file no longer supported");
} }
@Override
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
throw new UnsupportedOperationException("Flat file no longer supported");
}
/** /**
* Creates a PlayerAuth object from the read data. * Creates a PlayerAuth object from the read data.
* *

View File

@ -716,6 +716,22 @@ public class MySQL implements DataSource {
return players; return players;
} }
@Override
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
List<PlayerAuth> players = new ArrayList<>();
String sql = "SELECT * FROM " + tableName + " ORDER BY " + col.LAST_LOGIN + " DESC LIMIT 10;";
try (Connection con = getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql)) {
while (rs.next()) {
players.add(buildAuthFromResultSet(rs));
}
} catch (SQLException e) {
logSqlException(e);
}
return players;
}
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

@ -639,6 +639,20 @@ public class SQLite implements DataSource {
return players; return players;
} }
@Override
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
List<PlayerAuth> players = new ArrayList<>();
String sql = "SELECT * FROM " + tableName + " ORDER BY " + col.LAST_LOGIN + " DESC LIMIT 10;";
try (Statement st = con.createStatement(); ResultSet rs = st.executeQuery(sql)) {
while (rs.next()) {
players.add(buildAuthFromResultSet(rs));
}
} catch (SQLException e) {
logSqlException(e);
}
return players;
}
private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException { private PlayerAuth buildAuthFromResultSet(ResultSet row) throws SQLException {
String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null; String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null;

View File

@ -1,19 +0,0 @@
package fr.xephi.authme.initialization.factory;
/**
* Injectable factory that creates new instances of a certain type.
*
* @param <P> the parent type to which the factory is limited to
*/
public interface Factory<P> {
/**
* Creates an instance of the given class.
*
* @param clazz the class to instantiate
* @param <C> the class type
* @return new instance of the class
*/
<C extends P> C newInstance(Class<C> clazz);
}

View File

@ -1,46 +0,0 @@
package fr.xephi.authme.initialization.factory;
import ch.jalu.injector.Injector;
import ch.jalu.injector.context.ResolvedInstantiationContext;
import ch.jalu.injector.handlers.dependency.DependencyHandler;
import ch.jalu.injector.handlers.instantiation.DependencyDescription;
import ch.jalu.injector.utils.ReflectionUtils;
/**
* Dependency handler that builds {@link Factory} objects.
*/
public class FactoryDependencyHandler implements DependencyHandler {
@Override
public Object resolveValue(ResolvedInstantiationContext<?> context, DependencyDescription dependencyDescription) {
if (dependencyDescription.getType() == Factory.class) {
Class<?> genericType = ReflectionUtils.getGenericType(dependencyDescription.getGenericType());
if (genericType == null) {
throw new IllegalStateException("Factory fields must have concrete generic type. "
+ "Cannot get generic type for field in '" + context.getMappedClass() + "'");
}
return new FactoryImpl<>(genericType, context.getInjector());
}
return null;
}
private static final class FactoryImpl<P> implements Factory<P> {
private final Injector injector;
private final Class<P> parentClass;
FactoryImpl(Class<P> parentClass, Injector injector) {
this.parentClass = parentClass;
this.injector = injector;
}
@Override
public <C extends P> C newInstance(Class<C> clazz) {
if (parentClass.isAssignableFrom(clazz)) {
return injector.newInstance(clazz);
}
throw new IllegalArgumentException(clazz + " not child of " + parentClass);
}
}
}

View File

@ -1,37 +0,0 @@
package fr.xephi.authme.initialization.factory;
import java.util.Collection;
/**
* Injectable object to retrieve and create singletons of a common parent.
*
* @param <P> the parent class to which this store is limited to
*/
public interface SingletonStore<P> {
/**
* Returns the singleton of the given type, creating it if it hasn't been yet created.
*
* @param clazz the class to get the singleton for
* @param <C> type of the singleton
* @return the singleton of type {@code C}
*/
<C extends P> C getSingleton(Class<C> clazz);
/**
* Returns all existing singletons of this store's type.
*
* @return all registered singletons of type {@code P}
*/
Collection<P> retrieveAllOfType();
/**
* Returns all existing singletons of the given type.
*
* @param clazz the type to get singletons for
* @param <C> class type
* @return all registered singletons of type {@code C}
*/
<C extends P> Collection<C> retrieveAllOfType(Class<C> clazz);
}

View File

@ -1,61 +0,0 @@
package fr.xephi.authme.initialization.factory;
import ch.jalu.injector.Injector;
import ch.jalu.injector.context.ResolvedInstantiationContext;
import ch.jalu.injector.handlers.dependency.DependencyHandler;
import ch.jalu.injector.handlers.instantiation.DependencyDescription;
import ch.jalu.injector.utils.ReflectionUtils;
import java.util.Collection;
/**
* Dependency handler that builds {@link SingletonStore} objects.
*/
public class SingletonStoreDependencyHandler implements DependencyHandler {
@Override
public Object resolveValue(ResolvedInstantiationContext<?> context, DependencyDescription dependencyDescription) {
if (dependencyDescription.getType() == SingletonStore.class) {
Class<?> genericType = ReflectionUtils.getGenericType(dependencyDescription.getGenericType());
if (genericType == null) {
throw new IllegalStateException("Singleton store fields must have concrete generic type. "
+ "Cannot get generic type for field in '" + context.getMappedClass() + "'");
}
return new SingletonStoreImpl<>(genericType, context.getInjector());
}
return null;
}
private static final class SingletonStoreImpl<P> implements SingletonStore<P> {
private final Injector injector;
private final Class<P> parentClass;
SingletonStoreImpl(Class<P> parentClass, Injector injector) {
this.parentClass = parentClass;
this.injector = injector;
}
@Override
public <C extends P> C getSingleton(Class<C> clazz) {
if (parentClass.isAssignableFrom(clazz)) {
return injector.getSingleton(clazz);
}
throw new IllegalArgumentException(clazz + " not child of " + parentClass);
}
@Override
public Collection<P> retrieveAllOfType() {
return retrieveAllOfType(parentClass);
}
@Override
public <C extends P> Collection<C> retrieveAllOfType(Class<C> clazz) {
if (parentClass.isAssignableFrom(clazz)) {
return injector.retrieveAllOfType(clazz);
}
throw new IllegalArgumentException(clazz + " not child of " + parentClass);
}
}
}

View File

@ -40,6 +40,10 @@ public class ServerListener implements Listener {
if ("Essentials".equalsIgnoreCase(pluginName)) { if ("Essentials".equalsIgnoreCase(pluginName)) {
pluginHookService.unhookEssentials(); pluginHookService.unhookEssentials();
ConsoleLogger.info("Essentials has been disabled: unhooking"); ConsoleLogger.info("Essentials has been disabled: unhooking");
} else if ("CMI".equalsIgnoreCase(pluginName)) {
pluginHookService.unhookCmi();
spawnLoader.unloadCmiSpawn();
ConsoleLogger.info("CMI has been disabled: unhooking");
} else if ("Multiverse-Core".equalsIgnoreCase(pluginName)) { } else if ("Multiverse-Core".equalsIgnoreCase(pluginName)) {
pluginHookService.unhookMultiverse(); pluginHookService.unhookMultiverse();
ConsoleLogger.info("Multiverse-Core has been disabled: unhooking"); ConsoleLogger.info("Multiverse-Core has been disabled: unhooking");
@ -70,6 +74,9 @@ public class ServerListener implements Listener {
pluginHookService.tryHookToMultiverse(); pluginHookService.tryHookToMultiverse();
} else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) { } else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) {
spawnLoader.loadEssentialsSpawn(); spawnLoader.loadEssentialsSpawn();
} else if ("CMI".equalsIgnoreCase(pluginName)) {
pluginHookService.tryHookToCmi();
spawnLoader.loadCmiSpawn();
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) { } else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
protocolLibService.setup(); protocolLibService.setup();
} }

View File

@ -50,6 +50,11 @@ public enum AdminPermission implements PermissionNode {
*/ */
GET_IP("authme.admin.getip"), GET_IP("authme.admin.getip"),
/**
* Administrator command to see the last recently logged in players.
*/
SEE_RECENT_PLAYERS("authme.admin.seerecent"),
/** /**
* Administrator command to teleport to the AuthMe spawn. * Administrator command to teleport to the AuthMe spawn.
*/ */

View File

@ -47,8 +47,8 @@ public class SyncProcessManager {
runTask(() -> processSyncPlayerLogout.processSyncLogout(player)); runTask(() -> processSyncPlayerLogout.processSyncLogout(player));
} }
public void processSyncPlayerLogin(Player player) { public void processSyncPlayerLogin(Player player, boolean isFirstLogin) {
runTask(() -> processSyncPlayerLogin.processPlayerLogin(player)); runTask(() -> processSyncPlayerLogin.processPlayerLogin(player, isFirstLogin));
} }
public void processSyncPlayerQuit(Player player, boolean wasLoggedIn) { public void processSyncPlayerQuit(Player player, boolean wasLoggedIn) {

View File

@ -220,6 +220,8 @@ public class AsynchronousLogin implements AsynchronousProcess {
*/ */
private void performLogin(Player player, PlayerAuth auth) { private void performLogin(Player player, PlayerAuth auth) {
if (player.isOnline()) { if (player.isOnline()) {
final boolean isFirstLogin = (auth.getLastLogin() == null);
// Update auth to reflect this new login // Update auth to reflect this new login
final String ip = PlayerUtils.getPlayerIp(player); final String ip = PlayerUtils.getPlayerIp(player);
auth.setRealName(player.getName()); auth.setRealName(player.getName());
@ -258,7 +260,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
// task, we schedule it in the end // task, we schedule it in the end
// so that we can be sure, and have not to care if it might be // so that we can be sure, and have not to care if it might be
// processed in other order. // processed in other order.
syncProcessManager.processSyncPlayerLogin(player); syncProcessManager.processSyncPlayerLogin(player, isFirstLogin);
} else { } else {
ConsoleLogger.warning("Player '" + player.getName() + "' wasn't online during login process, aborted..."); ConsoleLogger.warning("Player '" + player.getName() + "' wasn't online during login process, aborted...");
} }

View File

@ -62,7 +62,13 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
} }
} }
public void processPlayerLogin(Player player) { /**
* Performs operations in sync mode for a player that has just logged in.
*
* @param player the player that was logged in
* @param isFirstLogin true if this is the first time the player logged in
*/
public void processPlayerLogin(Player player, boolean isFirstLogin) {
final String name = player.getName().toLowerCase(); final String name = player.getName().toLowerCase();
final LimboPlayer limbo = limboService.getLimboPlayer(name); final LimboPlayer limbo = limboService.getLimboPlayer(name);
@ -93,6 +99,9 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
welcomeMessageConfiguration.sendWelcomeMessage(player); welcomeMessageConfiguration.sendWelcomeMessage(player);
// Login is now finished; we can force all commands // Login is now finished; we can force all commands
if (isFirstLogin) {
commandManager.runCommandsOnFirstLogin(player);
}
commandManager.runCommandsOnLogin(player); commandManager.runCommandsOnLogin(player);
// Send Bungee stuff. The service will check if it is enabled or not. // Send Bungee stuff. The service will check if it is enabled or not.

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.process.register; package fr.xephi.authme.process.register;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.factory.SingletonStore;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.AsynchronousProcess;

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.security; package fr.xephi.authme.security;
import ch.jalu.injector.factory.Factory;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.PasswordEncryptionEvent; import fr.xephi.authme.events.PasswordEncryptionEvent;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;

View File

@ -22,6 +22,7 @@ public class PluginHookService {
private final PluginManager pluginManager; private final PluginManager pluginManager;
private Essentials essentials; private Essentials essentials;
private Plugin cmi;
private MultiverseCore multiverse; private MultiverseCore multiverse;
/** /**
@ -33,6 +34,7 @@ public class PluginHookService {
public PluginHookService(PluginManager pluginManager) { public PluginHookService(PluginManager pluginManager) {
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
tryHookToEssentials(); tryHookToEssentials();
tryHookToCmi();
tryHookToMultiverse(); tryHookToMultiverse();
} }
@ -60,6 +62,19 @@ public class PluginHookService {
return null; return null;
} }
/**
* If CMI is hooked into, return CMI' data folder.
*
* @return The CMI data folder, or null if unavailable
*/
public File getCmiDataFolder() {
Plugin plugin = pluginManager.getPlugin("CMI");
if(plugin == null) {
return null;
}
return plugin.getDataFolder();
}
/** /**
* Return the spawn of the given world as defined by Multiverse (if available). * Return the spawn of the given world as defined by Multiverse (if available).
* *
@ -76,10 +91,10 @@ public class PluginHookService {
return null; return null;
} }
// ------ // ------
// "Is plugin available" methods // "Is plugin available" methods
// ------ // ------
/** /**
* @return true if we have a hook to Essentials, false otherwise * @return true if we have a hook to Essentials, false otherwise
*/ */
@ -87,6 +102,13 @@ public class PluginHookService {
return essentials != null; return essentials != null;
} }
/**
* @return true if we have a hook to CMI, false otherwise
*/
public boolean isCmiAvailable() {
return cmi != null;
}
/** /**
* @return true if we have a hook to Multiverse, false otherwise * @return true if we have a hook to Multiverse, false otherwise
*/ */
@ -109,6 +131,17 @@ public class PluginHookService {
} }
} }
/**
* Attempts to create a hook into CMI.
*/
public void tryHookToCmi() {
try {
cmi = getPlugin(pluginManager, "CMI", Plugin.class);
} catch (Exception | NoClassDefFoundError ignored) {
cmi = null;
}
}
/** /**
* Attempts to create a hook into Multiverse. * Attempts to create a hook into Multiverse.
*/ */
@ -123,6 +156,7 @@ public class PluginHookService {
// ------ // ------
// Unhook methods // Unhook methods
// ------ // ------
/** /**
* Unhooks from Essentials. * Unhooks from Essentials.
*/ */
@ -130,6 +164,13 @@ public class PluginHookService {
essentials = null; essentials = null;
} }
/**
* Unhooks from CMI.
*/
public void unhookCmi() {
cmi = null;
}
/** /**
* Unhooks from Multiverse. * Unhooks from Multiverse.
*/ */
@ -140,6 +181,7 @@ public class PluginHookService {
// ------ // ------
// Helpers // Helpers
// ------ // ------
private static <T extends Plugin> T getPlugin(PluginManager pluginManager, String name, Class<T> clazz) private static <T extends Plugin> T getPlugin(PluginManager pluginManager, String name, Class<T> clazz)
throws Exception, NoClassDefFoundError { throws Exception, NoClassDefFoundError {
if (pluginManager.isPluginEnabled(name)) { if (pluginManager.isPluginEnabled(name)) {
@ -150,5 +192,4 @@ public class PluginHookService {
return null; return null;
} }
} }

View File

@ -35,6 +35,7 @@ public class SpawnLoader implements Reloadable {
private FileConfiguration authMeConfiguration; private FileConfiguration authMeConfiguration;
private String[] spawnPriority; private String[] spawnPriority;
private Location essentialsSpawn; private Location essentialsSpawn;
private Location cmiSpawn;
/** /**
* Constructor. * Constructor.
@ -130,6 +131,31 @@ public class SpawnLoader implements Reloadable {
essentialsSpawn = null; essentialsSpawn = null;
} }
/**
* Load the spawn point defined in CMI.
*/
public void loadCmiSpawn() {
File cmiFolder = pluginHookService.getCmiDataFolder();
if (cmiFolder == null) {
return;
}
File cmiConfig = new File(cmiFolder, "config.yml");
if (cmiConfig.exists()) {
cmiSpawn = getLocationFromCmiConfiguration(YamlConfiguration.loadConfiguration(cmiConfig));
} else {
cmiSpawn = null;
ConsoleLogger.info("CMI config file not found: '" + cmiConfig.getAbsolutePath() + "'");
}
}
/**
* Unset the spawn point defined in CMI.
*/
public void unloadCmiSpawn() {
cmiSpawn = null;
}
/** /**
* Return the spawn location for the given player. The source of the spawn location varies * Return the spawn location for the given player. The source of the spawn location varies
* depending on the spawn priority setting. * depending on the spawn priority setting.
@ -162,6 +188,9 @@ public class SpawnLoader implements Reloadable {
case "essentials": case "essentials":
spawnLoc = essentialsSpawn; spawnLoc = essentialsSpawn;
break; break;
case "cmi":
spawnLoc = cmiSpawn;
break;
case "authme": case "authme":
spawnLoc = getSpawn(); spawnLoc = getSpawn();
break; break;
@ -242,6 +271,28 @@ public class SpawnLoader implements Reloadable {
return null; return null;
} }
/**
* Build a {@link Location} object based on the CMI configuration.
*
* @param configuration The CMI file configuration to read from
*
* @return Location corresponding to the values in the path
*/
private static Location getLocationFromCmiConfiguration(FileConfiguration configuration) {
final String pathPrefix = "Spawn.Main";
if (isLocationCompleteInCmiConfig(configuration, pathPrefix)) {
String prefix = pathPrefix + ".";
String worldName = configuration.getString(prefix + "World");
World world = Bukkit.getWorld(worldName);
if (!StringUtils.isEmpty(worldName) && world != null) {
return new Location(world, configuration.getDouble(prefix + "X"),
configuration.getDouble(prefix + "Y"), configuration.getDouble(prefix + "Z"),
getFloat(configuration, prefix + "Yaw"), getFloat(configuration, prefix + "Pitch"));
}
}
return null;
}
/** /**
* Return whether the file configuration contains all fields necessary to define a spawn * Return whether the file configuration contains all fields necessary to define a spawn
* under the given path. * under the given path.
@ -261,6 +312,24 @@ public class SpawnLoader implements Reloadable {
return true; return true;
} }
/**
* Return whether the CMI file configuration contains all spawn fields under the given path.
*
* @param cmiConfiguration The file configuration from CMI
* @param pathPrefix The path to verify
*
* @return True if all spawn fields are present, false otherwise
*/
private static boolean isLocationCompleteInCmiConfig(FileConfiguration cmiConfiguration, String pathPrefix) {
String[] fields = {"World", "X", "Y", "Z", "Yaw", "Pitch"};
for (String field : fields) {
if (!cmiConfiguration.contains(pathPrefix + "." + field)) {
return false;
}
}
return true;
}
/** /**
* Retrieve a property as a float from the given file configuration. * Retrieve a property as a float from the given file configuration.
* *
@ -274,4 +343,5 @@ public class SpawnLoader implements Reloadable {
// This behavior is consistent with FileConfiguration#getDouble // This behavior is consistent with FileConfiguration#getDouble
return (value instanceof Number) ? ((Number) value).floatValue() : 0; return (value instanceof Number) ? ((Number) value).floatValue() : 0;
} }
} }

View File

@ -13,6 +13,7 @@ public class CommandConfig {
private Map<String, Command> onJoin = new LinkedHashMap<>(); private Map<String, Command> onJoin = new LinkedHashMap<>();
private Map<String, Command> onLogin = new LinkedHashMap<>(); private Map<String, Command> onLogin = new LinkedHashMap<>();
private Map<String, Command> onSessionLogin = new LinkedHashMap<>(); private Map<String, Command> onSessionLogin = new LinkedHashMap<>();
private Map<String, Command> onFirstLogin = new LinkedHashMap<>();
private Map<String, Command> onRegister = new LinkedHashMap<>(); private Map<String, Command> onRegister = new LinkedHashMap<>();
private Map<String, Command> onUnregister = new LinkedHashMap<>(); private Map<String, Command> onUnregister = new LinkedHashMap<>();
private Map<String, Command> onLogout = new LinkedHashMap<>(); private Map<String, Command> onLogout = new LinkedHashMap<>();
@ -41,6 +42,14 @@ public class CommandConfig {
this.onSessionLogin = onSessionLogin; this.onSessionLogin = onSessionLogin;
} }
public Map<String, Command> getOnFirstLogin() {
return onFirstLogin;
}
public void setOnFirstLogin(Map<String, Command> onFirstLogin) {
this.onFirstLogin = onFirstLogin;
}
public Map<String, Command> getOnRegister() { public Map<String, Command> getOnRegister() {
return onRegister; return onRegister;
} }

View File

@ -34,6 +34,7 @@ public class CommandManager implements Reloadable {
private WrappedTagReplacer<Command, Player> onJoinCommands; private WrappedTagReplacer<Command, Player> onJoinCommands;
private WrappedTagReplacer<Command, Player> onLoginCommands; private WrappedTagReplacer<Command, Player> onLoginCommands;
private WrappedTagReplacer<Command, Player> onSessionLoginCommands; private WrappedTagReplacer<Command, Player> onSessionLoginCommands;
private WrappedTagReplacer<Command, Player> onFirstLoginCommands;
private WrappedTagReplacer<Command, Player> onRegisterCommands; private WrappedTagReplacer<Command, Player> onRegisterCommands;
private WrappedTagReplacer<Command, Player> onUnregisterCommands; private WrappedTagReplacer<Command, Player> onUnregisterCommands;
private WrappedTagReplacer<Command, Player> onLogoutCommands; private WrappedTagReplacer<Command, Player> onLogoutCommands;
@ -75,7 +76,6 @@ public class CommandManager implements Reloadable {
executeCommands(player, onLoginCommands.getAdaptedItems(player)); executeCommands(player, onLoginCommands.getAdaptedItems(player));
} }
/** /**
* Runs the configured commands for when a player has logged in successfully due to session. * Runs the configured commands for when a player has logged in successfully due to session.
* *
@ -85,6 +85,15 @@ public class CommandManager implements Reloadable {
executeCommands(player, onSessionLoginCommands.getAdaptedItems(player)); executeCommands(player, onSessionLoginCommands.getAdaptedItems(player));
} }
/**
* Runs the configured commands for when a player logs in the first time.
*
* @param player the player that has logged in for the first time
*/
public void runCommandsOnFirstLogin(Player player) {
executeCommands(player, onFirstLoginCommands.getAdaptedItems(player));
}
/** /**
* Runs the configured commands for when a player has been unregistered. * Runs the configured commands for when a player has been unregistered.
* *
@ -124,6 +133,7 @@ public class CommandManager implements Reloadable {
CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS); CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS);
onJoinCommands = newReplacer(commandConfig.getOnJoin()); onJoinCommands = newReplacer(commandConfig.getOnJoin());
onLoginCommands = newReplacer(commandConfig.getOnLogin()); onLoginCommands = newReplacer(commandConfig.getOnLogin());
onFirstLoginCommands = newReplacer(commandConfig.getOnFirstLogin());
onSessionLoginCommands = newReplacer(commandConfig.getOnSessionLogin()); onSessionLoginCommands = newReplacer(commandConfig.getOnSessionLogin());
onRegisterCommands = newReplacer(commandConfig.getOnRegister()); onRegisterCommands = newReplacer(commandConfig.getOnRegister());
onUnregisterCommands = newReplacer(commandConfig.getOnUnregister()); onUnregisterCommands = newReplacer(commandConfig.getOnUnregister());

View File

@ -23,7 +23,7 @@ class CommandMigrationService implements MigrationService {
/** List of all properties in {@link CommandConfig}. */ /** List of all properties in {@link CommandConfig}. */
@VisibleForTesting @VisibleForTesting
static final List<String> COMMAND_CONFIG_PROPERTIES = ImmutableList.of( static final List<String> COMMAND_CONFIG_PROPERTIES = ImmutableList.of(
"onJoin", "onLogin", "onSessionLogin", "onRegister", "onUnregister", "onLogout"); "onJoin", "onLogin", "onSessionLogin", "onFirstLogin", "onRegister", "onUnregister", "onLogout");
@Inject @Inject
private SettingsMigrationService settingsMigrationService; private SettingsMigrationService settingsMigrationService;

View File

@ -22,7 +22,7 @@ public final class CommandSettingsHolder implements SettingsHolder {
@SectionComments @SectionComments
public static Map<String, String[]> sectionComments() { public static Map<String, String[]> sectionComments() {
String[] comments = { String[] rootComments = {
"This configuration file allows you to execute commands on various events.", "This configuration file allows you to execute commands on various events.",
"Supported placeholders in commands:", "Supported placeholders in commands:",
" %p is replaced with the player name.", " %p is replaced with the player name.",
@ -48,10 +48,14 @@ public final class CommandSettingsHolder implements SettingsHolder {
" command: 'broadcast %p has joined, welcome back!'", " command: 'broadcast %p has joined, welcome back!'",
" executor: CONSOLE", " executor: CONSOLE",
"", "",
"Supported command events: onLogin, onSessionLogin, onJoin, onLogout, onRegister, onUnregister" "Supported command events: onLogin, onSessionLogin, onFirstLogin, onJoin, onLogout, onRegister, "
+ "onUnregister"
}; };
Map<String, String[]> commentMap = new HashMap<>(); Map<String, String[]> commentMap = new HashMap<>();
commentMap.put("", comments); commentMap.put("", rootComments);
commentMap.put("onFirstLogin", new String[]{
"Commands to run for players logging in whose 'last login date' was empty"
});
commentMap.put("onUnregister", new String[]{ commentMap.put("onUnregister", new String[]{
"Commands to run whenever a player is unregistered (by himself, or by an admin)" "Commands to run whenever a player is unregistered (by himself, or by an admin)"
}); });

View File

@ -140,9 +140,9 @@ public final class RestrictionSettings implements SettingsHolder {
public static final Property<Boolean> DISPLAY_OTHER_ACCOUNTS = public static final Property<Boolean> DISPLAY_OTHER_ACCOUNTS =
newProperty("settings.restrictions.displayOtherAccounts", true); newProperty("settings.restrictions.displayOtherAccounts", true);
@Comment("Spawn priority; values: authme, essentials, multiverse, default") @Comment("Spawn priority; values: authme, essentials, cmi, multiverse, default")
public static final Property<String> SPAWN_PRIORITY = public static final Property<String> SPAWN_PRIORITY =
newProperty("settings.restrictions.spawnPriority", "authme,essentials,multiverse,default"); newProperty("settings.restrictions.spawnPriority", "authme,essentials,cmi,multiverse,default");
@Comment("Maximum Login authorized by IP") @Comment("Maximum Login authorized by IP")
public static final Property<Integer> MAX_LOGIN_PER_IP = public static final Property<Integer> MAX_LOGIN_PER_IP =

View File

@ -1,7 +1,7 @@
package fr.xephi.authme.task; package fr.xephi.authme.task;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.factory.SingletonStore;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import javax.inject.Inject; import javax.inject.Inject;

View File

@ -24,7 +24,9 @@
# command: 'broadcast %p has joined, welcome back!' # command: 'broadcast %p has joined, welcome back!'
# executor: CONSOLE # executor: CONSOLE
# #
# Supported command events: onLogin, onSessionLogin, onJoin, onLogout, onRegister, onUnregister # Supported command events: onLogin, onSessionLogin, onFirstLogin, onJoin, onLogout, onRegister, onUnregister
# Commands to run for players logging in whose 'last login date' was empty
onFirstLogin: {}
onJoin: {} onJoin: {}
onLogin: {} onLogin: {}
# These commands are called whenever a logged in player uses /logout or quits. # These commands are called whenever a logged in player uses /logout or quits.

View File

@ -17,7 +17,7 @@ softdepend:
commands: commands:
authme: authme:
description: AuthMe op commands description: AuthMe op commands
usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|debug usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|purgeplayer|backup|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages|recent|debug
email: email:
description: Add email or recover password description: Add email or recover password
usage: /email show|add|change|recover|code|setpassword usage: /email show|add|change|recover|code|setpassword
@ -74,6 +74,7 @@ permissions:
authme.admin.register: true authme.admin.register: true
authme.admin.reload: true authme.admin.reload: true
authme.admin.seeotheraccounts: true authme.admin.seeotheraccounts: true
authme.admin.seerecent: true
authme.admin.setfirstspawn: true authme.admin.setfirstspawn: true
authme.admin.setspawn: true authme.admin.setspawn: true
authme.admin.spawn: true authme.admin.spawn: true
@ -134,6 +135,9 @@ permissions:
authme.admin.seeotheraccounts: authme.admin.seeotheraccounts:
description: Permission to see the other accounts of the players that log in. description: Permission to see the other accounts of the players that log in.
default: op default: op
authme.admin.seerecent:
description: Administrator command to see the last recently logged in players.
default: op
authme.admin.setfirstspawn: authme.admin.setfirstspawn:
description: Administrator command to set the first AuthMe spawn. description: Administrator command to set the first AuthMe spawn.
default: op default: op

View File

@ -8,8 +8,6 @@ import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.command.CommandHandler; import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.factory.FactoryDependencyHandler;
import fr.xephi.authme.initialization.factory.SingletonStoreDependencyHandler;
import fr.xephi.authme.listener.BlockListener; import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
@ -96,7 +94,6 @@ public class AuthMeInitializationTest {
new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData()); new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData());
Injector injector = new InjectorBuilder() Injector injector = new InjectorBuilder()
.addHandlers(new FactoryDependencyHandler(), new SingletonStoreDependencyHandler())
.addDefaultHandlers("fr.xephi.authme") .addDefaultHandlers("fr.xephi.authme")
.create(); .create();
injector.provide(DataFolder.class, dataFolder); injector.provide(DataFolder.class, dataFolder);

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.command; package fr.xephi.authme.command;
import ch.jalu.injector.Injector; import ch.jalu.injector.Injector;
import ch.jalu.injector.factory.Factory;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand; import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand;
import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand; import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand;
import fr.xephi.authme.command.TestCommandsUtil.TestUnregisterCommand; import fr.xephi.authme.command.TestCommandsUtil.TestUnregisterCommand;
import fr.xephi.authme.command.help.HelpProvider; import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages; import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;

View File

@ -1,8 +1,8 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.factory.Factory;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.datasource.converter.Converter; import fr.xephi.authme.datasource.converter.Converter;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;

View File

@ -0,0 +1,61 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import org.bukkit.command.CommandSender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
/**
* Test for {@link RecentPlayersCommand}.
*/
@RunWith(MockitoJUnitRunner.class)
public class RecentPlayersCommandTest {
@InjectMocks
@Spy
private RecentPlayersCommand command;
@Mock
private DataSource dataSource;
@Test
public void shouldShowRecentPlayers() {
// given
PlayerAuth auth1 = PlayerAuth.builder()
.name("hannah").realName("Hannah").lastIp("11.11.11.11")
.lastLogin(1510387755000L) // 11/11/2017 @ 8:09am
.build();
PlayerAuth auth2 = PlayerAuth.builder()
.name("matt").realName("MATT").lastIp("22.11.22.33")
.lastLogin(1510269301000L) // 11/09/2017 @ 11:15pm
.build();
doReturn(ZoneId.of("UTC")).when(command).getZoneId();
given(dataSource.getRecentlyLoggedInPlayers()).willReturn(Arrays.asList(auth1, auth2));
CommandSender sender = mock(CommandSender.class);
// when
command.executeCommand(sender, Collections.emptyList());
// then
verify(sender).sendMessage(argThat(containsString("Recently logged in players")));
verify(sender).sendMessage("- Hannah (08:09 AM, 11 Nov with IP 11.11.11.11)");
verify(sender).sendMessage("- MATT (11:15 PM, 09 Nov with IP 22.11.22.33)");
}
}

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType; import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.initialization.factory.SingletonStore;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.output.LogLevel; import fr.xephi.authme.output.LogLevel;
import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.CommonService;

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.command.executable.authme.debug; package fr.xephi.authme.command.executable.authme.debug;
import ch.jalu.injector.factory.SingletonStore;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
@ -11,7 +12,6 @@ import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.Reloadable; import fr.xephi.authme.initialization.Reloadable;
import fr.xephi.authme.initialization.SettingsDependent; import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.initialization.factory.SingletonStore;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.junit.Before; import org.junit.Before;

View File

@ -1,6 +1,6 @@
package fr.xephi.authme.command.executable.authme.debug; package fr.xephi.authme.command.executable.authme.debug;
import fr.xephi.authme.initialization.factory.Factory; import ch.jalu.injector.factory.Factory;
import fr.xephi.authme.permission.DebugSectionPermissions; import fr.xephi.authme.permission.DebugSectionPermissions;
import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;

View File

@ -1,12 +1,12 @@
package fr.xephi.authme.data.limbo.persistence; package fr.xephi.authme.data.limbo.persistence;
import ch.jalu.injector.factory.Factory;
import ch.jalu.injector.testing.BeforeInjecting; import ch.jalu.injector.testing.BeforeInjecting;
import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.DelayedInjectionRunner;
import ch.jalu.injector.testing.InjectDelayed; import ch.jalu.injector.testing.InjectDelayed;
import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.limbo.LimboPlayer; import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.LimboSettings; import fr.xephi.authme.settings.properties.LimboSettings;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.google.common.collect.Lists;
import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import org.junit.Test; import org.junit.Test;
@ -467,4 +468,30 @@ public abstract class AbstractDataSourceIntegrationTest {
assertThat(dataSource.hasSession("user"), equalTo(true)); assertThat(dataSource.hasSession("user"), equalTo(true));
assertThat(dataSource.hasSession("nonExistentName"), equalTo(false)); assertThat(dataSource.hasSession("nonExistentName"), equalTo(false));
} }
@Test
public void shouldGetRecentlyLoggedInPlayers() {
// given
DataSource dataSource = getDataSource();
String[] names = {"user3", "user8", "user2", "user4", "user7",
"user11", "user14", "user12", "user18", "user16",
"user28", "user29", "user22", "user20", "user24"};
long timestamp = 1461024000; // 2016-04-19 00:00:00
for (int i = 0; i < names.length; ++i) {
PlayerAuth auth = PlayerAuth.builder().name(names[i])
.registrationDate(1234567)
.lastLogin(timestamp + i * 3600)
.build();
dataSource.saveAuth(auth);
dataSource.updateSession(auth);
}
// when
List<PlayerAuth> recentPlayers = dataSource.getRecentlyLoggedInPlayers();
// then
assertThat(Lists.transform(recentPlayers, PlayerAuth::getNickname),
contains("user24", "user20", "user22", "user29", "user28",
"user16", "user18", "user12", "user14", "user11"));
}
} }

View File

@ -31,6 +31,7 @@ public class ServerListenerTest {
private static final String ESSENTIALS = "Essentials"; private static final String ESSENTIALS = "Essentials";
private static final String ESSENTIALS_SPAWN = "EssentialsSpawn"; private static final String ESSENTIALS_SPAWN = "EssentialsSpawn";
private static final String CMI = "CMI";
private static final String MULTIVERSE = "Multiverse-Core"; private static final String MULTIVERSE = "Multiverse-Core";
private static final String PROTOCOL_LIB = "ProtocolLib"; private static final String PROTOCOL_LIB = "ProtocolLib";
@ -58,6 +59,10 @@ public class ServerListenerTest {
public void shouldForwardPluginNameOnEnable() { public void shouldForwardPluginNameOnEnable() {
checkEnableHandling(ESSENTIALS, () -> verify(pluginHookService).tryHookToEssentials()); checkEnableHandling(ESSENTIALS, () -> verify(pluginHookService).tryHookToEssentials());
checkEnableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).loadEssentialsSpawn()); checkEnableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).loadEssentialsSpawn());
checkEnableHandling(CMI, () -> {
verify(pluginHookService).tryHookToCmi();
verify(spawnLoader).loadCmiSpawn();
});
checkEnableHandling(MULTIVERSE, () -> verify(pluginHookService).tryHookToMultiverse()); checkEnableHandling(MULTIVERSE, () -> verify(pluginHookService).tryHookToMultiverse());
checkEnableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).setup()); checkEnableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).setup());
checkEnableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader)); checkEnableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader));
@ -67,6 +72,10 @@ public class ServerListenerTest {
public void shouldForwardPluginNameOnDisable() { public void shouldForwardPluginNameOnDisable() {
checkDisableHandling(ESSENTIALS, () -> verify(pluginHookService).unhookEssentials()); checkDisableHandling(ESSENTIALS, () -> verify(pluginHookService).unhookEssentials());
checkDisableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).unloadEssentialsSpawn()); checkDisableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).unloadEssentialsSpawn());
checkDisableHandling(CMI, () -> {
verify(pluginHookService).unhookCmi();
verify(spawnLoader).unloadCmiSpawn();
});
checkDisableHandling(MULTIVERSE, () -> verify(pluginHookService).unhookMultiverse()); checkDisableHandling(MULTIVERSE, () -> verify(pluginHookService).unhookMultiverse());
checkDisableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).disable()); checkDisableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).disable());
checkDisableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader)); checkDisableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader));

View File

@ -1,9 +1,9 @@
package fr.xephi.authme.process.register; package fr.xephi.authme.process.register;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.factory.SingletonStore;
import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.register.executors.PasswordRegisterParams; import fr.xephi.authme.process.register.executors.PasswordRegisterParams;

View File

@ -2,6 +2,7 @@ package fr.xephi.authme.security;
import ch.jalu.injector.Injector; import ch.jalu.injector.Injector;
import ch.jalu.injector.InjectorBuilder; import ch.jalu.injector.InjectorBuilder;
import ch.jalu.injector.factory.Factory;
import ch.jalu.injector.testing.BeforeInjecting; import ch.jalu.injector.testing.BeforeInjecting;
import ch.jalu.injector.testing.DelayedInjectionRunner; import ch.jalu.injector.testing.DelayedInjectionRunner;
import ch.jalu.injector.testing.InjectDelayed; import ch.jalu.injector.testing.InjectDelayed;
@ -9,7 +10,6 @@ import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper; import fr.xephi.authme.TestHelper;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.events.PasswordEncryptionEvent; import fr.xephi.authme.events.PasswordEncryptionEvent;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.security.crypts.Joomla; import fr.xephi.authme.security.crypts.Joomla;

View File

@ -35,6 +35,8 @@ public class PluginHookServiceTest {
/** The plugin name of Essentials. */ /** The plugin name of Essentials. */
private static final String ESSENTIALS = "Essentials"; private static final String ESSENTIALS = "Essentials";
/** The plugin name of CMI. */
private static final String CMI = "CMI";
/** The plugin name of Multiverse-Core. */ /** The plugin name of Multiverse-Core. */
private static final String MULTIVERSE = "Multiverse-Core"; private static final String MULTIVERSE = "Multiverse-Core";
@ -71,6 +73,19 @@ public class PluginHookServiceTest {
assertThat(pluginHookService.isEssentialsAvailable(), equalTo(true)); assertThat(pluginHookService.isEssentialsAvailable(), equalTo(true));
} }
@Test
public void shouldHookIntoCmiAtInitialization() {
// given
PluginManager pluginManager = mock(PluginManager.class);
setPluginAvailable(pluginManager, CMI, Plugin.class);
// when
PluginHookService pluginHookService = new PluginHookService(pluginManager);
// then
assertThat(pluginHookService.isCmiAvailable(), equalTo(true));
}
@Test @Test
public void shouldHookIntoMultiverseAtInitialization() { public void shouldHookIntoMultiverseAtInitialization() {
// given // given
@ -175,6 +190,7 @@ public class PluginHookServiceTest {
// then // then
assertThat(pluginHookService.isEssentialsAvailable(), equalTo(false)); assertThat(pluginHookService.isEssentialsAvailable(), equalTo(false));
assertThat(pluginHookService.isCmiAvailable(), equalTo(false));
assertThat(pluginHookService.isMultiverseAvailable(), equalTo(false)); assertThat(pluginHookService.isMultiverseAvailable(), equalTo(false));
} }

View File

@ -107,6 +107,21 @@ public class CommandManagerTest {
verifyZeroInteractions(geoIpService); verifyZeroInteractions(geoIpService);
} }
@Test
public void shouldExecuteCommandsOnFirstLogin() {
// given
copyJarFileAsCommandsYml(TEST_FILES_FOLDER + "commands.complete.yml");
initManager();
// when
manager.runCommandsOnFirstLogin(player);
// then
verify(bukkitService).dispatchConsoleCommand("pay Bobby 30");
verifyNoMoreInteractions(bukkitService);
verifyZeroInteractions(geoIpService);
}
@Test @Test
public void shouldExecuteCommandsOnJoin() { public void shouldExecuteCommandsOnJoin() {
// given // given

View File

@ -1,7 +1,7 @@
package fr.xephi.authme.task; package fr.xephi.authme.task;
import ch.jalu.injector.factory.SingletonStore;
import fr.xephi.authme.initialization.HasCleanup; import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.factory.SingletonStore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;

View File

@ -1,7 +1,9 @@
package tools.dependencygraph; package tools.dependencygraph;
import ch.jalu.injector.handlers.instantiation.DependencyDescription; import ch.jalu.injector.context.ObjectIdentifier;
import ch.jalu.injector.handlers.instantiation.Instantiation; import ch.jalu.injector.factory.Factory;
import ch.jalu.injector.factory.SingletonStore;
import ch.jalu.injector.handlers.instantiation.Resolution;
import ch.jalu.injector.handlers.instantiation.StandardInjectionProvider; import ch.jalu.injector.handlers.instantiation.StandardInjectionProvider;
import ch.jalu.injector.utils.ReflectionUtils; import ch.jalu.injector.utils.ReflectionUtils;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
@ -14,13 +16,12 @@ import fr.xephi.authme.command.executable.authme.debug.DebugCommand;
import fr.xephi.authme.data.limbo.persistence.LimboPersistence; import fr.xephi.authme.data.limbo.persistence.LimboPersistence;
import fr.xephi.authme.datasource.converter.Converter; import fr.xephi.authme.datasource.converter.Converter;
import fr.xephi.authme.initialization.DataFolder; import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.factory.Factory;
import fr.xephi.authme.initialization.factory.SingletonStore;
import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.process.AsynchronousProcess;
import fr.xephi.authme.process.SynchronousProcess; import fr.xephi.authme.process.SynchronousProcess;
import fr.xephi.authme.process.register.executors.RegistrationExecutor; import fr.xephi.authme.process.register.executors.RegistrationExecutor;
import fr.xephi.authme.security.crypts.EncryptionMethod; import fr.xephi.authme.security.crypts.EncryptionMethod;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import tools.utils.InjectorUtils;
import tools.utils.ToolTask; import tools.utils.ToolTask;
import tools.utils.ToolsConstants; import tools.utils.ToolsConstants;
@ -124,21 +125,20 @@ public class DrawDependency implements ToolTask {
* This is interesting so that a dependency in a class to {@code Factory<Foo>} is * This is interesting so that a dependency in a class to {@code Factory<Foo>} is
* rendered as a dependency to {@code Foo}, not to {@code Factory}. * rendered as a dependency to {@code Foo}, not to {@code Factory}.
* *
* @param clazz class of the dependency
* @param genericType generic type of the dependency * @param genericType generic type of the dependency
* @return the class to use to render the dependency * @return the class to use to render the dependency
*/ */
private Class<?> unwrapGenericClass(Class<?> clazz, Type genericType) { private Class<?> unwrapGenericClass(Type genericType) {
if (clazz == Factory.class || clazz == 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 '" + clazz + "' should be a concrete class"); Objects.requireNonNull(parameterType, "Parameter type for '" + genericType + "' should be a concrete class");
return parameterType; return parameterType;
} }
return clazz; return InjectorUtils.convertToClass(genericType);
} }
private List<String> getDependencies(Class<?> clazz) { private List<String> getDependencies(Class<?> clazz) {
Instantiation<?> instantiation = new StandardInjectionProvider().safeGet(clazz); Resolution<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
return instantiation == null ? null : formatInjectionDependencies(instantiation); return instantiation == null ? null : formatInjectionDependencies(instantiation);
} }
@ -150,22 +150,22 @@ public class DrawDependency implements ToolTask {
* @param injection the injection whose dependencies should be formatted * @param injection the injection whose dependencies should be formatted
* @return list of dependencies in a friendly format * @return list of dependencies in a friendly format
*/ */
private List<String> formatInjectionDependencies(Instantiation<?> injection) { private List<String> formatInjectionDependencies(Resolution<?> injection) {
List<DependencyDescription> descriptions = injection.getDependencies(); List<ObjectIdentifier> dependencies = injection.getDependencies();
List<String> result = new ArrayList<>(descriptions.size()); List<String> result = new ArrayList<>(dependencies.size());
for (DependencyDescription dependency : descriptions) { for (ObjectIdentifier dependency : dependencies) {
Class<?> annotation = getRelevantAnnotationClass(dependency.getAnnotations()); Class<?> annotation = getRelevantAnnotationClass(dependency.getAnnotations());
if (annotation != null) { if (annotation != null) {
result.add("@" + annotation.getSimpleName()); result.add("@" + annotation.getSimpleName());
} else { } else {
Class<?> clazz = unwrapGenericClass(dependency.getType(), dependency.getGenericType()); Class<?> clazz = unwrapGenericClass(dependency.getType());
result.add(mapToSuper(clazz).getSimpleName()); result.add(mapToSuper(clazz).getSimpleName());
} }
} }
return result; return result;
} }
private static Class<? extends Annotation> getRelevantAnnotationClass(Annotation[] annotations) { private static Class<? extends Annotation> getRelevantAnnotationClass(List<Annotation> annotations) {
for (Annotation annotation : annotations) { for (Annotation annotation : annotations) {
if (ANNOTATION_TYPES.contains(annotation.annotationType())) { if (ANNOTATION_TYPES.contains(annotation.annotationType())) {
return annotation.annotationType(); return annotation.annotationType();

View File

@ -1,9 +1,11 @@
package tools.utils; package tools.utils;
import ch.jalu.injector.handlers.instantiation.DependencyDescription; import ch.jalu.injector.context.ObjectIdentifier;
import ch.jalu.injector.handlers.instantiation.Instantiation; import ch.jalu.injector.handlers.instantiation.Resolution;
import ch.jalu.injector.handlers.instantiation.StandardInjectionProvider; import ch.jalu.injector.handlers.instantiation.StandardInjectionProvider;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -22,15 +24,37 @@ public final class InjectorUtils {
* @return the class' dependencies, or null if no instantiation method found * @return the class' dependencies, or null if no instantiation method found
*/ */
public static Set<Class<?>> getDependencies(Class<?> clazz) { public static Set<Class<?>> getDependencies(Class<?> clazz) {
Instantiation<?> instantiation = new StandardInjectionProvider().safeGet(clazz); Resolution<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
if (instantiation == null) { if (instantiation == null) {
return null; return null;
} }
Set<Class<?>> dependencies = new HashSet<>(); Set<Class<?>> dependencies = new HashSet<>();
for (DependencyDescription description : instantiation.getDependencies()) { for (ObjectIdentifier description : instantiation.getDependencies()) {
dependencies.add(description.getType()); dependencies.add(convertToClass(description.getType()));
} }
return dependencies; return dependencies;
} }
/**
* Returns the given type as a {@link Class}.
*
* @param type the type to convert
* @return class corresponding to the provided type
*/
public static Class<?> convertToClass(Type type) {
if (type instanceof Class<?>) {
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
if (rawType instanceof Class<?>) {
return (Class<?>) rawType;
} else {
throw new IllegalStateException("Got raw type '" + rawType + "' of type '"
+ rawType.getClass() + "' for genericType '" + type + "'");
}
}
Class<?> typeClass = type == null ? null : type.getClass();
throw new IllegalStateException("Unknown type implementation '" + typeClass + "' for '" + type + "'");
}
} }

View File

@ -25,6 +25,10 @@ onSessionLogin:
welcome: welcome:
command: 'msg %p Session login!' command: 'msg %p Session login!'
executor: CONSOLE executor: CONSOLE
onFirstLogin:
give_money:
command: 'pay %p 30'
executor: CONSOLE
onUnregister: {} onUnregister: {}
onLogout: onLogout:
announce: announce: