diff --git a/Plugins/Mineplex.Core/pom.xml b/Plugins/Mineplex.Core/pom.xml
index 06ecb566d..a2d2f6e3e 100644
--- a/Plugins/Mineplex.Core/pom.xml
+++ b/Plugins/Mineplex.Core/pom.xml
@@ -50,6 +50,16 @@
xz
1.5
+
+ com.warrenstrange
+ googleauth
+ 1.1.1
+
+
+ com.google.zxing
+ core
+ 3.3.0
+
diff --git a/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorAuth.java b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorAuth.java
new file mode 100644
index 000000000..a55271856
--- /dev/null
+++ b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorAuth.java
@@ -0,0 +1,320 @@
+package mineplex.core.twofactor;
+
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryDragEvent;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.player.PlayerDropItemEvent;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerItemHeldEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.map.MapRenderer;
+import org.bukkit.map.MapView;
+import org.bukkit.metadata.FixedMetadataValue;
+
+import com.warrenstrange.googleauth.GoogleAuthenticator;
+
+import mineplex.core.Managers;
+import mineplex.core.MiniClientPlugin;
+import mineplex.core.ReflectivelyCreateMiniPlugin;
+import mineplex.core.account.CoreClientManager;
+import mineplex.core.command.CommandBase;
+import mineplex.core.common.Rank;
+import mineplex.core.common.util.BukkitFuture;
+import mineplex.core.common.util.F;
+import mineplex.core.recharge.Recharge;
+import mineplex.serverdata.database.DBPool;
+
+@ReflectivelyCreateMiniPlugin
+public class TwoFactorAuth extends MiniClientPlugin
+{
+ private static final String TFA_SETUP = "twofactor:setup";
+ private static final String TFA_AUTHENTICATING = "twofactor:authenticating";
+
+ private static final GoogleAuthenticator authenticator = new GoogleAuthenticator();
+ private final CoreClientManager _clientManager = Managers.require(CoreClientManager.class);
+ private final TwoFactorRepository _repository = new TwoFactorRepository(DBPool.getAccount());
+
+ public TwoFactorAuth()
+ {
+ super("Two-factor Authentication");
+ _clientManager.addStoredProcedureLoginProcessor(
+ _repository.buildSecretKeyLoginProcessor((uuid, secretKey) -> Get(uuid).setSecretKey(secretKey))
+ );
+ _clientManager.addStoredProcedureLoginProcessor(
+ _repository.buildLastIpLoginProcessor((uuid, ip) -> Get(uuid).setLastLoginIp(ip))
+ );
+ }
+
+ @Override
+ public void addCommands()
+ {
+ // TODO: remove this when we start enforcing 2FA
+ addCommand(new CommandBase(this, Rank.MAPDEV, "2fa", "tfa")
+ {
+ @Override
+ public void Execute(Player caller, String[] args)
+ {
+ TwoFactorData data = Get(caller);
+ if (data.getSecretKey().isPresent())
+ {
+ caller.sendMessage(F.main("2FA", "You already have 2FA enabled."));
+ return;
+ }
+
+ setup2FA(caller);
+ }
+ });
+ }
+
+ private void setup2FA(Player player)
+ {
+ String secret = authenticator.createCredentials().getKey();
+
+ MapView view = Bukkit.createMap(player.getWorld());
+ for (MapRenderer renderer : view.getRenderers())
+ {
+ view.removeRenderer(renderer);
+ }
+ view.addRenderer(new TwoFactorMapRenderer(player, secret));
+
+ ItemStack stack = new ItemStack(Material.MAP);
+ stack.setDurability(view.getId());
+
+ // Find first free hotbar slot
+ int slot = 0;
+ for (int i = 0; i < 9; i++)
+ {
+ if (player.getInventory().getItem(i) == null)
+ {
+ slot = i;
+ break;
+ }
+ }
+
+ player.getInventory().setHeldItemSlot(slot);
+ player.getInventory().setItemInHand(stack);
+
+ player.setMetadata(TFA_SETUP, new FixedMetadataValue(_plugin, secret));
+ player.sendMessage(F.main("2FA", "Setting up two-factor authentication."));
+ }
+
+ @EventHandler
+ public void onJoin(PlayerJoinEvent event)
+ {
+ Player player = event.getPlayer();
+
+ TwoFactorData data = Get(player);
+
+ if (data.getLastLoginIp().isPresent() && player.getAddress().getAddress().toString().substring(1).equals(data.getLastLoginIp().get()))
+ {
+ player.sendMessage(F.main("2FA", "Authenticated"));
+ return;
+ }
+
+ if (data.getSecretKey().isPresent())
+ {
+ // Hooray 2FA
+ player.sendMessage(F.main("2FA", "Please enter your two-factor auth code"));
+ player.setMetadata(TFA_AUTHENTICATING, new FixedMetadataValue(_plugin, true));
+ }
+ else
+ {
+ // 2FA not set up yet. TODO: enforce this at a later date.
+ }
+ }
+
+ @EventHandler
+ public void onQuit(PlayerQuitEvent event)
+ {
+ Player player = event.getPlayer();
+ if (player.hasMetadata(TFA_SETUP))
+ {
+ player.setItemInHand(null);
+ player.removeMetadata(TFA_SETUP, _plugin);
+ }
+ player.removeMetadata(TFA_AUTHENTICATING, _plugin);
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onChat(AsyncPlayerChatEvent event)
+ {
+ Player player = event.getPlayer();
+
+ String secret = null;
+ if (player.hasMetadata(TFA_SETUP))
+ {
+ secret = player.getMetadata(TFA_SETUP).get(0).asString();
+ }
+ else if (player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ secret = Get(player).getSecretKey().get();
+ }
+
+ if (secret == null)
+ {
+ return;
+ }
+
+ // Hooray 2FA - let's see if their message matches their auth code
+
+ event.setCancelled(true);
+
+ int code;
+ try
+ {
+ code = Integer.parseInt(event.getMessage());
+ }
+ catch (NumberFormatException e)
+ {
+ player.sendMessage(F.main("2FA", "Invalid authentication code (not a number)."));
+ return;
+ }
+
+ if (!authenticator.authorize(secret, code))
+ {
+ player.sendMessage(F.main("2FA", "Invalid authentication code."));
+ return;
+ }
+
+ // Success!
+
+ player.sendMessage(F.main("2FA", "Authorized for 24 hours."));
+
+ if (player.hasMetadata(TFA_SETUP))
+ {
+ // Remove setup map + save secret
+ player.setItemInHand(null);
+ Get(player).setSecretKey(secret);
+
+ player.sendMessage(F.main("2FA", "Saving secret.."));
+ _repository.saveSecret(player, secret).whenComplete(BukkitFuture.complete((v, throwable) ->
+ {
+ if (!player.isOnline())
+ {
+ return;
+ }
+
+ if (throwable != null)
+ {
+ Get(player).setSecretKey(null);
+ player.sendMessage(F.main("2FA", "Something went wrong. Please try again in a moment."));
+ }
+ else
+ {
+ player.sendMessage(F.main("2FA", "Secret key saved."));
+ }
+ }));
+ }
+
+ _repository.saveLogin(player, player.getAddress().getAddress().toString().substring(1));
+
+ player.removeMetadata(TFA_SETUP, _plugin);
+ player.removeMetadata(TFA_AUTHENTICATING, _plugin);
+ }
+
+ // Cancel relevant events
+
+ @EventHandler(ignoreCancelled = true)
+ public void onChangeHeldItem(PlayerItemHeldEvent event)
+ {
+ Player player = event.getPlayer();
+ if (player.hasMetadata(TFA_SETUP))
+ {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onClick(InventoryClickEvent event)
+ {
+ Player player = (Player) event.getWhoClicked();
+ if (player.hasMetadata(TFA_SETUP) || player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onDrag(InventoryDragEvent event)
+ {
+ Player player = (Player) event.getWhoClicked();
+ if (player.hasMetadata(TFA_SETUP) || player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onDrop(PlayerDropItemEvent event)
+ {
+ Player player = event.getPlayer();
+ if (player.hasMetadata(TFA_SETUP) || player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onInteract(PlayerInteractEvent event)
+ {
+ Player player = event.getPlayer();
+ if (player.hasMetadata(TFA_SETUP) || player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onInteractWithEntity(PlayerInteractEntityEvent event)
+ {
+ Player player = event.getPlayer();
+ if (player.hasMetadata(TFA_SETUP) || player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onCommand(PlayerCommandPreprocessEvent event)
+ {
+ Player player = event.getPlayer();
+ if (player.hasMetadata(TFA_SETUP) || player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ event.setMessage("/");
+ event.setCancelled(true);
+ }
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onMove(PlayerMoveEvent event)
+ {
+ Player player = event.getPlayer();
+ if (player.hasMetadata(TFA_SETUP) || player.hasMetadata(TFA_AUTHENTICATING))
+ {
+ if (Recharge.Instance.use(player, "two-factor message cooldown", 3000L, false, false))
+ {
+ player.sendMessage(F.main("2FA", "Please enter your two-factor auth code"));
+ }
+ event.getTo().setX(event.getFrom().getX());
+ event.getTo().setZ(event.getFrom().getZ());
+ }
+ }
+
+ @Override
+ protected TwoFactorData addPlayer(UUID uuid)
+ {
+ return new TwoFactorData();
+ }
+}
diff --git a/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorData.java b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorData.java
new file mode 100644
index 000000000..faff1adb3
--- /dev/null
+++ b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorData.java
@@ -0,0 +1,29 @@
+package mineplex.core.twofactor;
+
+import java.util.Optional;
+
+public class TwoFactorData
+{
+ private String _secretKey;
+ private String _lastIp;
+
+ public Optional getSecretKey()
+ {
+ return Optional.ofNullable(_secretKey);
+ }
+
+ public Optional getLastLoginIp()
+ {
+ return Optional.ofNullable(_lastIp);
+ }
+
+ public void setSecretKey(String secretKey)
+ {
+ _secretKey = secretKey;
+ }
+
+ public void setLastLoginIp(String lastIp)
+ {
+ _lastIp = lastIp;
+ }
+}
diff --git a/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorMapRenderer.java b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorMapRenderer.java
new file mode 100644
index 000000000..178ba775a
--- /dev/null
+++ b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorMapRenderer.java
@@ -0,0 +1,155 @@
+package mineplex.core.twofactor;
+
+import java.util.Arrays;
+
+import org.bukkit.entity.Player;
+import org.bukkit.map.MapCanvas;
+import org.bukkit.map.MapFont;
+import org.bukkit.map.MapRenderer;
+import org.bukkit.map.MapView;
+import org.bukkit.map.MinecraftFont;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+
+public class TwoFactorMapRenderer extends MapRenderer
+{
+ private static final String SETUP_TEXT =
+ "\u00A7100;Two-factor Auth Setup\n\n" +
+ "\u00A744;1. Use your device to\n" +
+ "scan the QR code, or\n" +
+ "enter your \u00A716;secret code:\n" +
+ "\u00A728;%s\n\n" +
+ "\u00A744;2. Type the\n" +
+ "code from\n" +
+ "the app\n" +
+ "in chat.";
+ private static final MapFont FONT = MinecraftFont.Font;
+ private static final byte QR_COLOR = (byte)116; // Black
+ private static final QRCodeWriter writer = new QRCodeWriter();
+
+ private final Player _player;
+ private final byte[][] contents = new byte[128][128];
+
+ public TwoFactorMapRenderer(Player player, String secret)
+ {
+ _player = player;
+
+ BitMatrix matrix;
+ try
+ {
+ matrix = writer.encode(String.format("otpauth://totp/%s?secret=%s&issuer=Mineplex", _player.getName(), secret), BarcodeFormat.QR_CODE, 32, 32, ImmutableMap.of(EncodeHintType.MARGIN, 0));
+
+ }
+ catch (WriterException e)
+ {
+ e.printStackTrace();
+ return;
+ }
+
+ // Set background color to white
+ for (byte[] column : contents)
+ {
+ Arrays.fill(column, (byte)32);
+ }
+
+ String spacedSecret = Joiner.on(' ').join(Splitter.fixedLength(4).split(secret));
+ renderText(contents, 2, 2, String.format(SETUP_TEXT, spacedSecret));
+ renderQR(contents, 62, 62, matrix, 2);
+
+ }
+
+ private static void renderText(byte[][] contents, int startX, int startY, String text)
+ {
+ int x = startX;
+ int y = startY;
+ byte color = (byte)44;
+
+ for (int i = 0; i < text.length(); i++)
+ {
+ char c = text.charAt(i);
+ if (c == '\n')
+ {
+ x = startX;
+ y += FONT.getHeight() + 1;
+ continue;
+ }
+
+ if (c == '\u00A7')
+ {
+ int end = text.indexOf(';', i);
+ if (end >= 0)
+ {
+ color = Byte.parseByte(text.substring(i+1, end));
+ i = end;
+ continue;
+ }
+ }
+
+ MapFont.CharacterSprite sprite = FONT.getChar(c);
+ for (int k = 0; k < sprite.getWidth(); k++)
+ {
+ for (int l = 0; l < FONT.getHeight(); l++)
+ {
+ if (sprite.get(l, k))
+ {
+ if (x+k >= 128 || y+l >= 128)
+ {
+ continue;
+ }
+ contents[x+k][y+l] = color;
+ }
+ }
+ }
+ x += sprite.getWidth() + 1;
+ }
+ }
+
+ private static void renderQR(byte[][] contents, int x, int y, BitMatrix matrix, int scale)
+ {
+ for (int matrixX = 0 ; matrixX < matrix.getWidth(); matrixX++)
+ {
+ for (int matrixY = 0; matrixY < matrix.getHeight(); matrixY++)
+ {
+
+ if (matrix.get(matrixX, matrixY))
+ {
+ for (int i = 0; i < scale; i++)
+ {
+ for (int k = 0; k < scale; k++)
+ {
+ if (x + (matrixX * scale) + i >= 128 || y + (matrixY * scale) + k >= 128)
+ {
+ continue;
+ }
+ contents[x + (matrixX * scale) + i][y + (matrixY * scale) + k] = QR_COLOR;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void render(MapView mapView, MapCanvas mapCanvas, Player player)
+ {
+ if (player != _player)
+ {
+ return;
+ }
+
+ for (int x = 0; x < 128; x++)
+ {
+ for (int y = 0; y < 128; y++)
+ {
+ mapCanvas.setPixel(x, y, contents[x][y]);
+ }
+ }
+ }
+}
diff --git a/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorRepository.java b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorRepository.java
new file mode 100644
index 000000000..20d5810b0
--- /dev/null
+++ b/Plugins/Mineplex.Core/src/mineplex/core/twofactor/TwoFactorRepository.java
@@ -0,0 +1,124 @@
+package mineplex.core.twofactor;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.function.BiConsumer;
+
+import org.bukkit.entity.Player;
+
+import mineplex.core.Managers;
+import mineplex.core.account.CoreClientManager;
+import mineplex.core.account.ILoginProcessor;
+
+public class TwoFactorRepository
+{
+ private static final String INSERT_SECRET_KEY = "INSERT INTO twofactor (accountId,secretKey) VALUES (?,?);";
+ private static final String INSERT_LOGIN = "INSERT INTO twofactor_history (accountId,ip,loginTime) VALUES (?,?,NOW());";
+ private final CoreClientManager _clientManager = Managers.require(CoreClientManager.class);
+ private final DataSource _dataSource;
+
+ public TwoFactorRepository(DataSource source)
+ {
+ _dataSource = source;
+ }
+
+ public ILoginProcessor buildSecretKeyLoginProcessor(BiConsumer consumer)
+ {
+ return new ILoginProcessor()
+ {
+ @Override
+ public String getName()
+ {
+ return "Two-factor auth secret key grabber";
+ }
+
+ @Override
+ public void processLoginResultSet(String playerName, UUID uuid, int accountId, ResultSet resultSet) throws SQLException
+ {
+ if (resultSet.next())
+ {
+ consumer.accept(uuid, resultSet.getString(1));
+ }
+ }
+
+ @Override
+ public String getQuery(int accountId, String uuid, String name)
+ {
+ return "SELECT secretKey FROM twofactor WHERE accountId=" + accountId + ";";
+ }
+ };
+ }
+
+ public ILoginProcessor buildLastIpLoginProcessor(BiConsumer consumer)
+ {
+ return new ILoginProcessor()
+ {
+ @Override
+ public String getName()
+ {
+ return "Two-factor auth last login grabber";
+ }
+
+ @Override
+ public void processLoginResultSet(String playerName, UUID uuid, int accountId, ResultSet resultSet) throws SQLException
+ {
+ if (resultSet.next())
+ {
+ consumer.accept(uuid, resultSet.getString(1));
+ }
+ }
+
+ @Override
+ public String getQuery(int accountId, String uuid, String name)
+ {
+ return "SELECT ip FROM twofactor_history WHERE accountId=" + accountId + " AND loginTime >= DATE_SUB(NOW(), INTERVAL 1 DAY) ORDER BY loginTime DESC;";
+ }
+ };
+ }
+
+ public CompletableFuture saveSecret(Player player, String secret)
+ {
+ int accountId = _clientManager.Get(player).getAccountId();
+
+ return CompletableFuture.runAsync(() ->
+ {
+ try (Connection connection = _dataSource.getConnection())
+ {
+ PreparedStatement statement = connection.prepareStatement(INSERT_SECRET_KEY);
+ statement.setInt(1, accountId);
+ statement.setString(2, secret);
+ statement.executeUpdate();
+ }
+ catch (SQLException e)
+ {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ public CompletableFuture saveLogin(Player player, String ip)
+ {
+ int accountId = _clientManager.Get(player).getAccountId();
+
+ return CompletableFuture.runAsync(() ->
+ {
+ try (Connection connection = _dataSource.getConnection())
+ {
+ PreparedStatement statement = connection.prepareStatement(INSERT_LOGIN);
+ statement.setInt(1, accountId);
+ statement.setString(2, ip);
+ statement.executeUpdate();
+ }
+ catch (SQLException e)
+ {
+ throw new CompletionException(e);
+ }
+ });
+ }
+}
diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java
index d865e5d6e..5c091c8a8 100644
--- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java
+++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Clans.java
@@ -1,5 +1,15 @@
package mineplex.game.clans;
+import net.minecraft.server.v1_8_R3.MinecraftServer;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.spigotmc.SpigotConfig;
+
import mineplex.core.CustomTagFix;
import mineplex.core.FoodDupeFix;
import mineplex.core.TimingsFix;
@@ -48,6 +58,7 @@ import mineplex.core.spawn.Spawn;
import mineplex.core.stats.StatsManager;
import mineplex.core.status.ServerStatusManager;
import mineplex.core.teleport.Teleport;
+import mineplex.core.twofactor.TwoFactorAuth;
import mineplex.core.updater.FileUpdater;
import mineplex.core.updater.Updater;
import mineplex.core.visibility.VisibilityManager;
@@ -60,14 +71,6 @@ import mineplex.game.clans.shop.mining.MiningShop;
import mineplex.game.clans.shop.pvp.PvpShop;
import mineplex.game.clans.spawn.travel.TravelShop;
import mineplex.game.clans.world.WorldManager;
-import net.minecraft.server.v1_8_R3.MinecraftServer;
-import org.bukkit.Bukkit;
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.World;
-import org.bukkit.craftbukkit.v1_8_R3.CraftWorld;
-import org.bukkit.plugin.java.JavaPlugin;
-import org.spigotmc.SpigotConfig;
import static mineplex.core.Managers.require;
@@ -261,6 +264,8 @@ public class Clans extends JavaPlugin
new MiningShop(_clansManager, _clientManager, _donationManager);
new WorldManager(this);
+ require(TwoFactorAuth.class);
+
// Disable spigot item merging
for (World world : getServer().getWorlds())
{
diff --git a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java
index 04cf7bd71..235791a86 100644
--- a/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java
+++ b/Plugins/Mineplex.Hub.Clans/src/mineplex/clanshub/ClansHub.java
@@ -1,5 +1,9 @@
package mineplex.clanshub;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.plugin.java.JavaPlugin;
+
import mineplex.core.CustomTagFix;
import mineplex.core.PacketsInteractionFix;
import mineplex.core.account.CoreClientManager;
@@ -39,6 +43,7 @@ import mineplex.core.preferences.PreferencesManager;
import mineplex.core.profileCache.ProfileCacheManager;
import mineplex.core.punish.Punish;
import mineplex.core.rankGiveaway.eternal.EternalGiveawayManager;
+import mineplex.core.rankGiveaway.titangiveaway.TitanGiveawayManager;
import mineplex.core.recharge.Recharge;
import mineplex.core.resourcepack.ResourcePackManager;
import mineplex.core.serverConfig.ServerConfiguration;
@@ -47,9 +52,9 @@ import mineplex.core.status.ServerStatusManager;
import mineplex.core.task.TaskManager;
import mineplex.core.teleport.Teleport;
import mineplex.core.thank.ThankManager;
-import mineplex.core.rankGiveaway.titangiveaway.TitanGiveawayManager;
import mineplex.core.titles.Titles;
import mineplex.core.titles.tracks.TrackManager;
+import mineplex.core.twofactor.TwoFactorAuth;
import mineplex.core.updater.FileUpdater;
import mineplex.core.updater.Updater;
import mineplex.core.velocity.VelocityFix;
@@ -57,9 +62,6 @@ import mineplex.core.visibility.VisibilityManager;
import mineplex.minecraft.game.core.combat.CombatManager;
import mineplex.minecraft.game.core.condition.ConditionManager;
import mineplex.minecraft.game.core.damage.DamageManager;
-import org.bukkit.Bukkit;
-import org.bukkit.Location;
-import org.bukkit.plugin.java.JavaPlugin;
import static mineplex.core.Managers.require;
@@ -175,6 +177,7 @@ public class ClansHub extends JavaPlugin
require(TrackManager.class);
require(Titles.class);
+ require(TwoFactorAuth.class);
}
@Override
diff --git a/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java b/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java
index cd87eaf8e..492f1fa1f 100644
--- a/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java
+++ b/Plugins/Mineplex.Hub/src/mineplex/hub/Hub.java
@@ -52,6 +52,7 @@ import mineplex.core.profileCache.ProfileCacheManager;
import mineplex.core.projectile.ProjectileManager;
import mineplex.core.punish.Punish;
import mineplex.core.rankGiveaway.eternal.EternalGiveawayManager;
+import mineplex.core.rankGiveaway.titangiveaway.TitanGiveawayManager;
import mineplex.core.recharge.Recharge;
import mineplex.core.report.ReportManager;
import mineplex.core.report.ReportPlugin;
@@ -63,7 +64,9 @@ import mineplex.core.status.ServerStatusManager;
import mineplex.core.task.TaskManager;
import mineplex.core.teleport.Teleport;
import mineplex.core.thank.ThankManager;
-import mineplex.core.rankGiveaway.titangiveaway.TitanGiveawayManager;
+import mineplex.core.titles.Titles;
+import mineplex.core.titles.tracks.TrackManager;
+import mineplex.core.twofactor.TwoFactorAuth;
import mineplex.core.updater.FileUpdater;
import mineplex.core.updater.Updater;
import mineplex.core.velocity.VelocityFix;
@@ -71,8 +74,6 @@ import mineplex.core.visibility.VisibilityManager;
import mineplex.hub.modules.BillboardManager;
import mineplex.hub.queue.QueueManager;
import mineplex.hub.server.ServerManager;
-import mineplex.core.titles.Titles;
-import mineplex.core.titles.tracks.TrackManager;
import mineplex.minecraft.game.classcombat.Class.ClassManager;
import mineplex.minecraft.game.classcombat.Condition.SkillConditionManager;
import mineplex.minecraft.game.classcombat.Skill.SkillFactory;
@@ -232,6 +233,7 @@ public class Hub extends JavaPlugin implements IRelation
require(TrackManager.class);
require(Titles.class);
+ require(TwoFactorAuth.class);
}
@Override
diff --git a/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java b/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java
index 5b65fd27a..4a0bec73e 100644
--- a/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java
+++ b/Plugins/Mineplex.Hub/src/mineplex/hub/HubManager.java
@@ -136,7 +136,7 @@ import net.minecraft.server.v1_8_R3.EntityPlayer;
public class HubManager extends MiniClientPlugin implements IChatMessageFormatter
{
// ☃❅ Snowman!
- public HubType Type = HubType.Christmas;
+ public HubType Type = HubType.Normal;
private BlockRestore _blockRestore;
private CoreClientManager _clientManager;
diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java
index 5f8828c95..dff9c1246 100644
--- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java
+++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/Arcade.java
@@ -1,36 +1,44 @@
package nautilus.game.arcade;
+import java.io.File;
+import java.util.HashMap;
+
+import net.minecraft.server.v1_8_R3.MinecraftServer;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.spigotmc.SpigotConfig;
+
import mineplex.core.CustomTagFix;
import mineplex.core.FoodDupeFix;
import mineplex.core.PacketsInteractionFix;
import mineplex.core.TimingsFix;
-import mineplex.core.antihack.logging.AntihackLogger;
-import mineplex.core.chatsnap.SnapshotRepository;
-import mineplex.core.customdata.CustomDataManager;
-import mineplex.core.chatsnap.SnapshotManager;
-import mineplex.core.chatsnap.SnapshotPlugin;
-import mineplex.core.globalpacket.GlobalPacketManager;
-import net.minecraft.server.v1_8_R3.BiomeBase;
-import net.minecraft.server.v1_8_R3.MinecraftServer;
import mineplex.core.account.CoreClientManager;
import mineplex.core.achievement.AchievementManager;
import mineplex.core.antihack.AntiHack;
+import mineplex.core.antihack.logging.AntihackLogger;
import mineplex.core.blockrestore.BlockRestore;
import mineplex.core.blood.Blood;
import mineplex.core.boosters.BoosterManager;
import mineplex.core.chat.Chat;
+import mineplex.core.chatsnap.SnapshotManager;
+import mineplex.core.chatsnap.SnapshotPlugin;
+import mineplex.core.chatsnap.SnapshotRepository;
import mineplex.core.command.CommandCenter;
import mineplex.core.common.events.ServerShutdownEvent;
import mineplex.core.common.util.FileUtil;
import mineplex.core.common.util.UtilServer;
import mineplex.core.cosmetic.CosmeticManager;
import mineplex.core.creature.Creature;
+import mineplex.core.customdata.CustomDataManager;
import mineplex.core.disguise.DisguiseManager;
import mineplex.core.donation.DonationManager;
import mineplex.core.elo.EloManager;
import mineplex.core.friend.FriendManager;
import mineplex.core.gadget.GadgetManager;
import mineplex.core.give.Give;
+import mineplex.core.globalpacket.GlobalPacketManager;
import mineplex.core.hologram.HologramManager;
import mineplex.core.ignore.IgnoreManager;
import mineplex.core.incognito.IncognitoManager;
@@ -58,8 +66,7 @@ import mineplex.core.stats.StatsManager;
import mineplex.core.status.ServerStatusManager;
import mineplex.core.teleport.Teleport;
import mineplex.core.thank.ThankManager;
-import mineplex.core.titles.Titles;
-import mineplex.core.titles.tracks.TrackManager;
+import mineplex.core.twofactor.TwoFactorAuth;
import mineplex.core.updater.FileUpdater;
import mineplex.core.updater.Updater;
import mineplex.core.velocity.VelocityFix;
@@ -69,15 +76,6 @@ import mineplex.minecraft.game.core.damage.DamageManager;
import nautilus.game.arcade.anticheatmetadata.GameInfoMetadata;
import nautilus.game.arcade.game.GameServerConfig;
-
-import org.bukkit.Bukkit;
-import org.bukkit.entity.Player;
-import org.bukkit.plugin.java.JavaPlugin;
-import org.spigotmc.SpigotConfig;
-
-import java.io.File;
-import java.util.HashMap;
-
import static mineplex.core.Managers.require;
public class Arcade extends JavaPlugin
@@ -198,6 +196,8 @@ public class Arcade extends JavaPlugin
new CustomTagFix(this, packetHandler);
new PacketsInteractionFix(this, packetHandler);
new FoodDupeFix(this);
+
+ require(TwoFactorAuth.class);
//Updates
getServer().getScheduler().scheduleSyncRepeatingTask(this, new Updater(this), 1, 1);
diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java
index b41df5c9c..d2864e5e5 100644
--- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java
+++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/game/Game.java
@@ -292,7 +292,7 @@ public abstract class Game implements Listener
public long PrepareTime = 9000;
public boolean PlaySoundGameStart = true;
- public double XpMult = 2;
+ public double XpMult = 1;
public boolean SpeedMeasurement = false;
diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameRewardManager.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameRewardManager.java
index 12d448581..4c8db7903 100644
--- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameRewardManager.java
+++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/GameRewardManager.java
@@ -44,7 +44,7 @@ public class GameRewardManager implements Listener
{
ArcadeManager Manager;
- boolean DoubleGem = true;
+ boolean DoubleGem = false;
boolean TimeReward = true;
public GameRewardManager(ArcadeManager manager)
@@ -352,7 +352,7 @@ public class GameRewardManager implements Listener
//Double Gem
if (DoubleGem && game.GemDoubleEnabled)
{
- UtilPlayer.message(player, F.elem(C.cGreen + "+" + (earnedGems) + " Gems") + " for " + F.elem(C.cDGreen + "Holiday Double Gems"));
+ UtilPlayer.message(player, F.elem(C.cGreen + "+" + (earnedGems) + " Gems") + " for " + F.elem(C.cDGreen + "Double Gem Weekend"));
totalGems += earnedGems;
}