mirror of
https://github.com/AuthMe/AuthMeReloaded.git
synced 2024-08-31 01:29:37 +02:00
commit
c150adb665
|
@ -1,8 +1,8 @@
|
|||
<!-- 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
|
||||
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
|
||||
the generated config.yml file.
|
||||
|
||||
|
@ -131,7 +131,7 @@ settings:
|
|||
# Hide the chat log from players who are not authenticated?
|
||||
hideChat: false
|
||||
# Allowed commands for unauthenticated players
|
||||
allowCommands:
|
||||
allowCommands:
|
||||
- '/login'
|
||||
- '/register'
|
||||
- '/l'
|
||||
|
@ -158,7 +158,7 @@ settings:
|
|||
enabled: false
|
||||
# WorldNames where we need to force the spawn location
|
||||
# Case-sensitive!
|
||||
worlds:
|
||||
worlds:
|
||||
- 'world'
|
||||
- 'world_nether'
|
||||
- 'world_the_end'
|
||||
|
@ -202,8 +202,8 @@ settings:
|
|||
# Should we display all other accounts from a player when he joins?
|
||||
# permission: /authme.admin.accounts
|
||||
displayOtherAccounts: true
|
||||
# Spawn priority; values: authme, essentials, multiverse, default
|
||||
spawnPriority: 'authme,essentials,multiverse,default'
|
||||
# Spawn priority; values: authme, essentials, cmi, multiverse, default
|
||||
spawnPriority: 'authme,essentials,cmi,multiverse,default'
|
||||
# Maximum Login authorized by IP
|
||||
maxLoginPerIp: 0
|
||||
# Maximum Join authorized by IP
|
||||
|
@ -258,7 +258,7 @@ settings:
|
|||
# - '123456'
|
||||
# - 'password'
|
||||
# - 'help'
|
||||
unsafePasswords:
|
||||
unsafePasswords:
|
||||
- '123456'
|
||||
- 'password'
|
||||
- 'qwerty'
|
||||
|
@ -364,7 +364,7 @@ Email:
|
|||
# Delay in minute for the recall scheduler
|
||||
delayRecall: 5
|
||||
# Blacklist these domains for emails
|
||||
emailBlacklisted:
|
||||
emailBlacklisted:
|
||||
- '10minutemail.com'
|
||||
# Whitelist ONLY these domains for emails
|
||||
emailWhitelisted: []
|
||||
|
@ -391,12 +391,12 @@ Protection:
|
|||
# Countries allowed to join the server and register. For country codes, see
|
||||
# http://dev.maxmind.com/geoip/legacy/codes/iso3166/
|
||||
# PLEASE USE QUOTES!
|
||||
countries:
|
||||
countries:
|
||||
- 'US'
|
||||
- 'GB'
|
||||
# Countries not allowed to join the server and register
|
||||
# PLEASE USE QUOTES!
|
||||
countriesBlacklist:
|
||||
countriesBlacklist:
|
||||
- 'A1'
|
||||
# Do we need to enable automatic antibot system?
|
||||
enableAntiBot: true
|
||||
|
@ -555,9 +555,9 @@ Converter:
|
|||
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`.
|
||||
|
||||
---
|
||||
|
||||
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
|
||||
|
|
4
pom.xml
4
pom.xml
|
@ -6,7 +6,7 @@
|
|||
|
||||
<groupId>fr.xephi</groupId>
|
||||
<artifactId>authme</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<version>5.5.0-SNAPSHOT</version>
|
||||
|
||||
<name>AuthMeReloaded</name>
|
||||
<description>The first authentication plugin for the Bukkit API!</description>
|
||||
|
@ -302,7 +302,7 @@
|
|||
<dependency>
|
||||
<groupId>ch.jalu</groupId>
|
||||
<artifactId>injector</artifactId>
|
||||
<version>0.4.1</version>
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
|
|
@ -12,8 +12,6 @@ import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
|
|||
import fr.xephi.authme.initialization.OnStartupTasks;
|
||||
import fr.xephi.authme.initialization.SettingsProvider;
|
||||
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.EntityListener;
|
||||
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
|
||||
injector = new InjectorBuilder()
|
||||
.addHandlers(new FactoryDependencyHandler(), new SingletonStoreDependencyHandler())
|
||||
.addDefaultHandlers("fr.xephi.authme")
|
||||
.create();
|
||||
injector.register(AuthMe.class, this);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package fr.xephi.authme.command;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
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.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
|
|
|
@ -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.PurgeLastPositionCommand;
|
||||
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.ReloadCommand;
|
||||
import fr.xephi.authme.command.executable.authme.SetEmailCommand;
|
||||
|
@ -433,6 +434,15 @@ public class CommandInitializer {
|
|||
.executableCommand(MessagesCommand.class)
|
||||
.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()
|
||||
.parent(authmeBase)
|
||||
.labels("debug", "dbg")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
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.VAuthConverter;
|
||||
import fr.xephi.authme.datasource.converter.XAuthConverter;
|
||||
import fr.xephi.authme.initialization.factory.Factory;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
|
|
|
@ -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() + ")";
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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.limbo.LimboService;
|
||||
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.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import fr.xephi.authme.permission.DebugSectionPermissions;
|
||||
import fr.xephi.authme.permission.PermissionNode;
|
||||
import org.bukkit.ChatColor;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import fr.xephi.authme.command.ExecutableCommand;
|
||||
import fr.xephi.authme.initialization.factory.Factory;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package fr.xephi.authme.data.limbo.persistence;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import fr.xephi.authme.ConsoleLogger;
|
||||
import fr.xephi.authme.data.limbo.LimboPlayer;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.initialization.factory.Factory;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
import fr.xephi.authme.settings.properties.LimboSettings;
|
||||
import org.bukkit.entity.Player;
|
||||
|
|
|
@ -263,6 +263,11 @@ public class CacheDataSource implements DataSource {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PlayerAuth> getRecentlyLoggedInPlayers() {
|
||||
return source.getRecentlyLoggedInPlayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCache(String playerName) {
|
||||
cachedAuths.invalidate(playerName);
|
||||
|
|
|
@ -225,6 +225,13 @@ public interface DataSource extends Reloadable {
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -393,6 +393,11 @@ public class FlatFile implements DataSource {
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -716,6 +716,22 @@ public class MySQL implements DataSource {
|
|||
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 {
|
||||
String salt = col.SALT.isEmpty() ? null : row.getString(col.SALT);
|
||||
int group = col.GROUP.isEmpty() ? -1 : row.getInt(col.GROUP);
|
||||
|
|
|
@ -639,6 +639,20 @@ public class SQLite implements DataSource {
|
|||
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 {
|
||||
String salt = !col.SALT.isEmpty() ? row.getString(col.SALT) : null;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,6 +40,10 @@ public class ServerListener implements Listener {
|
|||
if ("Essentials".equalsIgnoreCase(pluginName)) {
|
||||
pluginHookService.unhookEssentials();
|
||||
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)) {
|
||||
pluginHookService.unhookMultiverse();
|
||||
ConsoleLogger.info("Multiverse-Core has been disabled: unhooking");
|
||||
|
@ -70,6 +74,9 @@ public class ServerListener implements Listener {
|
|||
pluginHookService.tryHookToMultiverse();
|
||||
} else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) {
|
||||
spawnLoader.loadEssentialsSpawn();
|
||||
} else if ("CMI".equalsIgnoreCase(pluginName)) {
|
||||
pluginHookService.tryHookToCmi();
|
||||
spawnLoader.loadCmiSpawn();
|
||||
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
|
||||
protocolLibService.setup();
|
||||
}
|
||||
|
|
|
@ -50,6 +50,11 @@ public enum AdminPermission implements PermissionNode {
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
|
@ -47,8 +47,8 @@ public class SyncProcessManager {
|
|||
runTask(() -> processSyncPlayerLogout.processSyncLogout(player));
|
||||
}
|
||||
|
||||
public void processSyncPlayerLogin(Player player) {
|
||||
runTask(() -> processSyncPlayerLogin.processPlayerLogin(player));
|
||||
public void processSyncPlayerLogin(Player player, boolean isFirstLogin) {
|
||||
runTask(() -> processSyncPlayerLogin.processPlayerLogin(player, isFirstLogin));
|
||||
}
|
||||
|
||||
public void processSyncPlayerQuit(Player player, boolean wasLoggedIn) {
|
||||
|
|
|
@ -220,6 +220,8 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
|||
*/
|
||||
private void performLogin(Player player, PlayerAuth auth) {
|
||||
if (player.isOnline()) {
|
||||
final boolean isFirstLogin = (auth.getLastLogin() == null);
|
||||
|
||||
// Update auth to reflect this new login
|
||||
final String ip = PlayerUtils.getPlayerIp(player);
|
||||
auth.setRealName(player.getName());
|
||||
|
@ -258,7 +260,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
|
|||
// task, we schedule it in the end
|
||||
// so that we can be sure, and have not to care if it might be
|
||||
// processed in other order.
|
||||
syncProcessManager.processSyncPlayerLogin(player);
|
||||
syncProcessManager.processSyncPlayerLogin(player, isFirstLogin);
|
||||
} else {
|
||||
ConsoleLogger.warning("Player '" + player.getName() + "' wasn't online during login process, aborted...");
|
||||
}
|
||||
|
|
|
@ -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 LimboPlayer limbo = limboService.getLimboPlayer(name);
|
||||
|
||||
|
@ -93,6 +99,9 @@ public class ProcessSyncPlayerLogin implements SynchronousProcess {
|
|||
welcomeMessageConfiguration.sendWelcomeMessage(player);
|
||||
|
||||
// Login is now finished; we can force all commands
|
||||
if (isFirstLogin) {
|
||||
commandManager.runCommandsOnFirstLogin(player);
|
||||
}
|
||||
commandManager.runCommandsOnLogin(player);
|
||||
|
||||
// Send Bungee stuff. The service will check if it is enabled or not.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
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.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.process.AsynchronousProcess;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package fr.xephi.authme.security;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.events.PasswordEncryptionEvent;
|
||||
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.HashedPassword;
|
||||
import fr.xephi.authme.settings.Settings;
|
||||
|
|
|
@ -22,6 +22,7 @@ public class PluginHookService {
|
|||
|
||||
private final PluginManager pluginManager;
|
||||
private Essentials essentials;
|
||||
private Plugin cmi;
|
||||
private MultiverseCore multiverse;
|
||||
|
||||
/**
|
||||
|
@ -33,6 +34,7 @@ public class PluginHookService {
|
|||
public PluginHookService(PluginManager pluginManager) {
|
||||
this.pluginManager = pluginManager;
|
||||
tryHookToEssentials();
|
||||
tryHookToCmi();
|
||||
tryHookToMultiverse();
|
||||
}
|
||||
|
||||
|
@ -60,6 +62,19 @@ public class PluginHookService {
|
|||
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).
|
||||
*
|
||||
|
@ -76,10 +91,10 @@ public class PluginHookService {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
// ------
|
||||
// "Is plugin available" methods
|
||||
// ------
|
||||
|
||||
/**
|
||||
* @return true if we have a hook to Essentials, false otherwise
|
||||
*/
|
||||
|
@ -87,6 +102,13 @@ public class PluginHookService {
|
|||
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
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -123,6 +156,7 @@ public class PluginHookService {
|
|||
// ------
|
||||
// Unhook methods
|
||||
// ------
|
||||
|
||||
/**
|
||||
* Unhooks from Essentials.
|
||||
*/
|
||||
|
@ -130,6 +164,13 @@ public class PluginHookService {
|
|||
essentials = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhooks from CMI.
|
||||
*/
|
||||
public void unhookCmi() {
|
||||
cmi = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unhooks from Multiverse.
|
||||
*/
|
||||
|
@ -140,6 +181,7 @@ public class PluginHookService {
|
|||
// ------
|
||||
// Helpers
|
||||
// ------
|
||||
|
||||
private static <T extends Plugin> T getPlugin(PluginManager pluginManager, String name, Class<T> clazz)
|
||||
throws Exception, NoClassDefFoundError {
|
||||
if (pluginManager.isPluginEnabled(name)) {
|
||||
|
@ -150,5 +192,4 @@ public class PluginHookService {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ public class SpawnLoader implements Reloadable {
|
|||
private FileConfiguration authMeConfiguration;
|
||||
private String[] spawnPriority;
|
||||
private Location essentialsSpawn;
|
||||
private Location cmiSpawn;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -130,6 +131,31 @@ public class SpawnLoader implements Reloadable {
|
|||
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
|
||||
* depending on the spawn priority setting.
|
||||
|
@ -162,6 +188,9 @@ public class SpawnLoader implements Reloadable {
|
|||
case "essentials":
|
||||
spawnLoc = essentialsSpawn;
|
||||
break;
|
||||
case "cmi":
|
||||
spawnLoc = cmiSpawn;
|
||||
break;
|
||||
case "authme":
|
||||
spawnLoc = getSpawn();
|
||||
break;
|
||||
|
@ -242,6 +271,28 @@ public class SpawnLoader implements Reloadable {
|
|||
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
|
||||
* under the given path.
|
||||
|
@ -261,6 +312,24 @@ public class SpawnLoader implements Reloadable {
|
|||
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.
|
||||
*
|
||||
|
@ -274,4 +343,5 @@ public class SpawnLoader implements Reloadable {
|
|||
// This behavior is consistent with FileConfiguration#getDouble
|
||||
return (value instanceof Number) ? ((Number) value).floatValue() : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ public class CommandConfig {
|
|||
private Map<String, Command> onJoin = new LinkedHashMap<>();
|
||||
private Map<String, Command> onLogin = 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> onUnregister = new LinkedHashMap<>();
|
||||
private Map<String, Command> onLogout = new LinkedHashMap<>();
|
||||
|
@ -41,6 +42,14 @@ public class CommandConfig {
|
|||
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() {
|
||||
return onRegister;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ public class CommandManager implements Reloadable {
|
|||
private WrappedTagReplacer<Command, Player> onJoinCommands;
|
||||
private WrappedTagReplacer<Command, Player> onLoginCommands;
|
||||
private WrappedTagReplacer<Command, Player> onSessionLoginCommands;
|
||||
private WrappedTagReplacer<Command, Player> onFirstLoginCommands;
|
||||
private WrappedTagReplacer<Command, Player> onRegisterCommands;
|
||||
private WrappedTagReplacer<Command, Player> onUnregisterCommands;
|
||||
private WrappedTagReplacer<Command, Player> onLogoutCommands;
|
||||
|
@ -75,7 +76,6 @@ public class CommandManager implements Reloadable {
|
|||
executeCommands(player, onLoginCommands.getAdaptedItems(player));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -124,6 +133,7 @@ public class CommandManager implements Reloadable {
|
|||
CommandConfig commandConfig = settingsManager.getProperty(CommandSettingsHolder.COMMANDS);
|
||||
onJoinCommands = newReplacer(commandConfig.getOnJoin());
|
||||
onLoginCommands = newReplacer(commandConfig.getOnLogin());
|
||||
onFirstLoginCommands = newReplacer(commandConfig.getOnFirstLogin());
|
||||
onSessionLoginCommands = newReplacer(commandConfig.getOnSessionLogin());
|
||||
onRegisterCommands = newReplacer(commandConfig.getOnRegister());
|
||||
onUnregisterCommands = newReplacer(commandConfig.getOnUnregister());
|
||||
|
|
|
@ -23,7 +23,7 @@ class CommandMigrationService implements MigrationService {
|
|||
/** List of all properties in {@link CommandConfig}. */
|
||||
@VisibleForTesting
|
||||
static final List<String> COMMAND_CONFIG_PROPERTIES = ImmutableList.of(
|
||||
"onJoin", "onLogin", "onSessionLogin", "onRegister", "onUnregister", "onLogout");
|
||||
"onJoin", "onLogin", "onSessionLogin", "onFirstLogin", "onRegister", "onUnregister", "onLogout");
|
||||
|
||||
@Inject
|
||||
private SettingsMigrationService settingsMigrationService;
|
||||
|
|
|
@ -22,7 +22,7 @@ public final class CommandSettingsHolder implements SettingsHolder {
|
|||
|
||||
@SectionComments
|
||||
public static Map<String, String[]> sectionComments() {
|
||||
String[] comments = {
|
||||
String[] rootComments = {
|
||||
"This configuration file allows you to execute commands on various events.",
|
||||
"Supported placeholders in commands:",
|
||||
" %p is replaced with the player name.",
|
||||
|
@ -48,10 +48,14 @@ public final class CommandSettingsHolder implements SettingsHolder {
|
|||
" command: 'broadcast %p has joined, welcome back!'",
|
||||
" 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<>();
|
||||
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[]{
|
||||
"Commands to run whenever a player is unregistered (by himself, or by an admin)"
|
||||
});
|
||||
|
|
|
@ -140,9 +140,9 @@ public final class RestrictionSettings implements SettingsHolder {
|
|||
public static final Property<Boolean> DISPLAY_OTHER_ACCOUNTS =
|
||||
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 =
|
||||
newProperty("settings.restrictions.spawnPriority", "authme,essentials,multiverse,default");
|
||||
newProperty("settings.restrictions.spawnPriority", "authme,essentials,cmi,multiverse,default");
|
||||
|
||||
@Comment("Maximum Login authorized by IP")
|
||||
public static final Property<Integer> MAX_LOGIN_PER_IP =
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package fr.xephi.authme.task;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
# command: 'broadcast %p has joined, welcome back!'
|
||||
# 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: {}
|
||||
onLogin: {}
|
||||
# These commands are called whenever a logged in player uses /logout or quits.
|
||||
|
|
|
@ -17,7 +17,7 @@ softdepend:
|
|||
commands:
|
||||
authme:
|
||||
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:
|
||||
description: Add email or recover password
|
||||
usage: /email show|add|change|recover|code|setpassword
|
||||
|
@ -74,6 +74,7 @@ permissions:
|
|||
authme.admin.register: true
|
||||
authme.admin.reload: true
|
||||
authme.admin.seeotheraccounts: true
|
||||
authme.admin.seerecent: true
|
||||
authme.admin.setfirstspawn: true
|
||||
authme.admin.setspawn: true
|
||||
authme.admin.spawn: true
|
||||
|
@ -134,6 +135,9 @@ permissions:
|
|||
authme.admin.seeotheraccounts:
|
||||
description: Permission to see the other accounts of the players that log in.
|
||||
default: op
|
||||
authme.admin.seerecent:
|
||||
description: Administrator command to see the last recently logged in players.
|
||||
default: op
|
||||
authme.admin.setfirstspawn:
|
||||
description: Administrator command to set the first AuthMe spawn.
|
||||
default: op
|
||||
|
|
|
@ -8,8 +8,6 @@ import fr.xephi.authme.api.v3.AuthMeApi;
|
|||
import fr.xephi.authme.command.CommandHandler;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
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.permission.PermissionsManager;
|
||||
import fr.xephi.authme.process.Management;
|
||||
|
@ -96,7 +94,6 @@ public class AuthMeInitializationTest {
|
|||
new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData());
|
||||
|
||||
Injector injector = new InjectorBuilder()
|
||||
.addHandlers(new FactoryDependencyHandler(), new SingletonStoreDependencyHandler())
|
||||
.addDefaultHandlers("fr.xephi.authme")
|
||||
.create();
|
||||
injector.provide(DataFolder.class, dataFolder);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package fr.xephi.authme.command;
|
||||
|
||||
import ch.jalu.injector.Injector;
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import com.google.common.collect.Sets;
|
||||
import fr.xephi.authme.command.TestCommandsUtil.TestLoginCommand;
|
||||
import fr.xephi.authme.command.TestCommandsUtil.TestRegisterCommand;
|
||||
import fr.xephi.authme.command.TestCommandsUtil.TestUnregisterCommand;
|
||||
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.Messages;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
import fr.xephi.authme.datasource.converter.Converter;
|
||||
import fr.xephi.authme.initialization.factory.Factory;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.service.BukkitService;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
|
|
|
@ -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)");
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
package fr.xephi.authme.command.executable.authme;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.AuthMe;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.datasource.DataSourceType;
|
||||
import fr.xephi.authme.initialization.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.output.LogLevel;
|
||||
import fr.xephi.authme.service.CommonService;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package fr.xephi.authme.command.executable.authme.debug;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import fr.xephi.authme.ReflectionTestUtils;
|
||||
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.Reloadable;
|
||||
import fr.xephi.authme.initialization.SettingsDependent;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.junit.Before;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
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.PermissionNode;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package fr.xephi.authme.data.limbo.persistence;
|
||||
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import ch.jalu.injector.testing.BeforeInjecting;
|
||||
import ch.jalu.injector.testing.DelayedInjectionRunner;
|
||||
import ch.jalu.injector.testing.InjectDelayed;
|
||||
import fr.xephi.authme.ReflectionTestUtils;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
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.properties.LimboSettings;
|
||||
import org.bukkit.entity.Player;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package fr.xephi.authme.datasource;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import fr.xephi.authme.data.auth.PlayerAuth;
|
||||
import fr.xephi.authme.security.crypts.HashedPassword;
|
||||
import org.junit.Test;
|
||||
|
@ -467,4 +468,30 @@ public abstract class AbstractDataSourceIntegrationTest {
|
|||
assertThat(dataSource.hasSession("user"), equalTo(true));
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ public class ServerListenerTest {
|
|||
|
||||
private static final String ESSENTIALS = "Essentials";
|
||||
private static final String ESSENTIALS_SPAWN = "EssentialsSpawn";
|
||||
private static final String CMI = "CMI";
|
||||
private static final String MULTIVERSE = "Multiverse-Core";
|
||||
private static final String PROTOCOL_LIB = "ProtocolLib";
|
||||
|
||||
|
@ -58,6 +59,10 @@ public class ServerListenerTest {
|
|||
public void shouldForwardPluginNameOnEnable() {
|
||||
checkEnableHandling(ESSENTIALS, () -> verify(pluginHookService).tryHookToEssentials());
|
||||
checkEnableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).loadEssentialsSpawn());
|
||||
checkEnableHandling(CMI, () -> {
|
||||
verify(pluginHookService).tryHookToCmi();
|
||||
verify(spawnLoader).loadCmiSpawn();
|
||||
});
|
||||
checkEnableHandling(MULTIVERSE, () -> verify(pluginHookService).tryHookToMultiverse());
|
||||
checkEnableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).setup());
|
||||
checkEnableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader));
|
||||
|
@ -67,6 +72,10 @@ public class ServerListenerTest {
|
|||
public void shouldForwardPluginNameOnDisable() {
|
||||
checkDisableHandling(ESSENTIALS, () -> verify(pluginHookService).unhookEssentials());
|
||||
checkDisableHandling(ESSENTIALS_SPAWN, () -> verify(spawnLoader).unloadEssentialsSpawn());
|
||||
checkDisableHandling(CMI, () -> {
|
||||
verify(pluginHookService).unhookCmi();
|
||||
verify(spawnLoader).unloadCmiSpawn();
|
||||
});
|
||||
checkDisableHandling(MULTIVERSE, () -> verify(pluginHookService).unhookMultiverse());
|
||||
checkDisableHandling(PROTOCOL_LIB, () -> verify(protocolLibService).disable());
|
||||
checkDisableHandling("UnknownPlugin", () -> verifyZeroInteractions(pluginHookService, spawnLoader));
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package fr.xephi.authme.process.register;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.TestHelper;
|
||||
import fr.xephi.authme.data.auth.PlayerCache;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import fr.xephi.authme.message.MessageKey;
|
||||
import fr.xephi.authme.permission.PermissionsManager;
|
||||
import fr.xephi.authme.process.register.executors.PasswordRegisterParams;
|
||||
|
|
|
@ -2,6 +2,7 @@ package fr.xephi.authme.security;
|
|||
|
||||
import ch.jalu.injector.Injector;
|
||||
import ch.jalu.injector.InjectorBuilder;
|
||||
import ch.jalu.injector.factory.Factory;
|
||||
import ch.jalu.injector.testing.BeforeInjecting;
|
||||
import ch.jalu.injector.testing.DelayedInjectionRunner;
|
||||
import ch.jalu.injector.testing.InjectDelayed;
|
||||
|
@ -9,7 +10,6 @@ import fr.xephi.authme.ReflectionTestUtils;
|
|||
import fr.xephi.authme.TestHelper;
|
||||
import fr.xephi.authme.datasource.DataSource;
|
||||
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.HashedPassword;
|
||||
import fr.xephi.authme.security.crypts.Joomla;
|
||||
|
|
|
@ -35,6 +35,8 @@ public class PluginHookServiceTest {
|
|||
|
||||
/** The plugin name of Essentials. */
|
||||
private static final String ESSENTIALS = "Essentials";
|
||||
/** The plugin name of CMI. */
|
||||
private static final String CMI = "CMI";
|
||||
/** The plugin name of Multiverse-Core. */
|
||||
private static final String MULTIVERSE = "Multiverse-Core";
|
||||
|
||||
|
@ -71,6 +73,19 @@ public class PluginHookServiceTest {
|
|||
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
|
||||
public void shouldHookIntoMultiverseAtInitialization() {
|
||||
// given
|
||||
|
@ -175,6 +190,7 @@ public class PluginHookServiceTest {
|
|||
|
||||
// then
|
||||
assertThat(pluginHookService.isEssentialsAvailable(), equalTo(false));
|
||||
assertThat(pluginHookService.isCmiAvailable(), equalTo(false));
|
||||
assertThat(pluginHookService.isMultiverseAvailable(), equalTo(false));
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,21 @@ public class CommandManagerTest {
|
|||
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
|
||||
public void shouldExecuteCommandsOnJoin() {
|
||||
// given
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package fr.xephi.authme.task;
|
||||
|
||||
import ch.jalu.injector.factory.SingletonStore;
|
||||
import fr.xephi.authme.initialization.HasCleanup;
|
||||
import fr.xephi.authme.initialization.factory.SingletonStore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package tools.dependencygraph;
|
||||
|
||||
import ch.jalu.injector.handlers.instantiation.DependencyDescription;
|
||||
import ch.jalu.injector.handlers.instantiation.Instantiation;
|
||||
import ch.jalu.injector.context.ObjectIdentifier;
|
||||
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.utils.ReflectionUtils;
|
||||
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.datasource.converter.Converter;
|
||||
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.SynchronousProcess;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationExecutor;
|
||||
import fr.xephi.authme.security.crypts.EncryptionMethod;
|
||||
import org.bukkit.event.Listener;
|
||||
import tools.utils.InjectorUtils;
|
||||
import tools.utils.ToolTask;
|
||||
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
|
||||
* rendered as a dependency to {@code Foo}, not to {@code Factory}.
|
||||
*
|
||||
* @param clazz class of the dependency
|
||||
* @param genericType generic type of the dependency
|
||||
* @return the class to use to render the dependency
|
||||
*/
|
||||
private Class<?> unwrapGenericClass(Class<?> clazz, Type genericType) {
|
||||
if (clazz == Factory.class || clazz == SingletonStore.class) {
|
||||
private Class<?> unwrapGenericClass(Type genericType) {
|
||||
if (genericType == Factory.class || genericType == SingletonStore.class) {
|
||||
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 clazz;
|
||||
return InjectorUtils.convertToClass(genericType);
|
||||
}
|
||||
|
||||
private List<String> getDependencies(Class<?> clazz) {
|
||||
Instantiation<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
|
||||
Resolution<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
|
||||
return instantiation == null ? null : formatInjectionDependencies(instantiation);
|
||||
}
|
||||
|
||||
|
@ -150,22 +150,22 @@ public class DrawDependency implements ToolTask {
|
|||
* @param injection the injection whose dependencies should be formatted
|
||||
* @return list of dependencies in a friendly format
|
||||
*/
|
||||
private List<String> formatInjectionDependencies(Instantiation<?> injection) {
|
||||
List<DependencyDescription> descriptions = injection.getDependencies();
|
||||
List<String> result = new ArrayList<>(descriptions.size());
|
||||
for (DependencyDescription dependency : descriptions) {
|
||||
private List<String> formatInjectionDependencies(Resolution<?> injection) {
|
||||
List<ObjectIdentifier> dependencies = injection.getDependencies();
|
||||
List<String> result = new ArrayList<>(dependencies.size());
|
||||
for (ObjectIdentifier dependency : dependencies) {
|
||||
Class<?> annotation = getRelevantAnnotationClass(dependency.getAnnotations());
|
||||
if (annotation != null) {
|
||||
result.add("@" + annotation.getSimpleName());
|
||||
} else {
|
||||
Class<?> clazz = unwrapGenericClass(dependency.getType(), dependency.getGenericType());
|
||||
Class<?> clazz = unwrapGenericClass(dependency.getType());
|
||||
result.add(mapToSuper(clazz).getSimpleName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Class<? extends Annotation> getRelevantAnnotationClass(Annotation[] annotations) {
|
||||
private static Class<? extends Annotation> getRelevantAnnotationClass(List<Annotation> annotations) {
|
||||
for (Annotation annotation : annotations) {
|
||||
if (ANNOTATION_TYPES.contains(annotation.annotationType())) {
|
||||
return annotation.annotationType();
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package tools.utils;
|
||||
|
||||
import ch.jalu.injector.handlers.instantiation.DependencyDescription;
|
||||
import ch.jalu.injector.handlers.instantiation.Instantiation;
|
||||
import ch.jalu.injector.context.ObjectIdentifier;
|
||||
import ch.jalu.injector.handlers.instantiation.Resolution;
|
||||
import ch.jalu.injector.handlers.instantiation.StandardInjectionProvider;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -22,15 +24,37 @@ public final class InjectorUtils {
|
|||
* @return the class' dependencies, or null if no instantiation method found
|
||||
*/
|
||||
public static Set<Class<?>> getDependencies(Class<?> clazz) {
|
||||
Instantiation<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
|
||||
Resolution<?> instantiation = new StandardInjectionProvider().safeGet(clazz);
|
||||
if (instantiation == null) {
|
||||
return null;
|
||||
}
|
||||
Set<Class<?>> dependencies = new HashSet<>();
|
||||
for (DependencyDescription description : instantiation.getDependencies()) {
|
||||
dependencies.add(description.getType());
|
||||
for (ObjectIdentifier description : instantiation.getDependencies()) {
|
||||
dependencies.add(convertToClass(description.getType()));
|
||||
}
|
||||
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 + "'");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ onSessionLogin:
|
|||
welcome:
|
||||
command: 'msg %p Session login!'
|
||||
executor: CONSOLE
|
||||
onFirstLogin:
|
||||
give_money:
|
||||
command: 'pay %p 30'
|
||||
executor: CONSOLE
|
||||
onUnregister: {}
|
||||
onLogout:
|
||||
announce:
|
||||
|
|
Loading…
Reference in New Issue
Block a user