public class BukkitBungeeAdapter {
	private static BukkitBungeeAdapter instance;
	private final Plugin PAF_EXTENSION;
	private boolean forceUuidSupport = false;

	public BukkitBungeeAdapter(PAFExtension pPlugin) {
		this((PAFPluginBase) pPlugin);
	}

	public BukkitBungeeAdapter(PAFPluginBase pPlugin) {
		PAF_EXTENSION = pPlugin;
		if (instance == null)
			instance = this;
	}

	public static BukkitBungeeAdapter getInstance() {
		return instance;
	}

	public void registerListener(Object pListener, PAFPluginBase pPlugin) {
		ProxyServer.getInstance().getPluginManager().registerListener(pPlugin, (Listener) pListener);
	}

	public void callEvent(Object pEvent) {
		ProxyServer.getInstance().getPluginManager().callEvent((Event) pEvent);
	}

	public void registerCommand(Object pCommand, PAFPluginBase pPlugin) {
		ProxyServer.getInstance().getPluginManager().registerCommand(pPlugin, (Command) pCommand);
	}

	public void registerTopCommand(TopCommand<?> pCommand, PAFPluginBase pPlugin) {
		ProxyServer.getInstance().getPluginManager().registerCommand(pPlugin, pCommand);
	}

	public ServerSoftware getServerSoftware() {
		return ServerSoftware.BUNGEECORD;
	}

	public void runAsync(PAFPluginBase pPlugin, Runnable pRunnable) {
		ProxyServer.getInstance().getScheduler().runAsync(pPlugin, pRunnable);
	}

	public void schedule(PAFPluginBase instance, Runnable pRunnable, long timeInSeconds) {
		ProxyServer.getInstance().getScheduler().schedule(instance, pRunnable, timeInSeconds, TimeUnit.SECONDS);
	}

	public boolean isOnlineMode() {
		return ProxyServer.getInstance().getConfig().isOnlineMode() || forceUuidSupport;
	}

	public void setForceUuidSupport(boolean forceUuidSupport) {
		this.forceUuidSupport = forceUuidSupport;
	}
} public class InviteEvent extends PAFSubCommandEvent<Invite> {
	private final OnlinePAFPlayer INTERACT_PLAYER;

	public InviteEvent(OnlinePAFPlayer pExecutor, OnlinePAFPlayer pInteractPlayer, String[] args, Invite pCaller) {
		super(pExecutor, args, pCaller);
		INTERACT_PLAYER = pInteractPlayer;
	}

	public PAFPlayer getInteractPlayer() {
		return INTERACT_PLAYER;
	}

} public interface OnlinePAFPlayer extends PAFPlayer {

	void createEntry();

	void connect(ServerInfo pInfo);

	ServerInfo getServer();

	boolean isOnline();

	int changeSettingsWorth(int pSettingsID);

	@Deprecated
	void sendPacket(Chat chat);

	void sendPacket(TextComponent chat);

	boolean teleportTo(OnlinePAFPlayer pPlayer);

	void update();

	/**
	 * @return Returns the player if he is on this Bungeecord. If he is on
	 * another Bungeecord (RedisBungee) it returns null.
	 */
	ProxiedPlayer getPlayer();

	default PlayerParty getParty() {
		return PartyManager.getInstance().getParty(this);
	}
} public interface PAFPlayer {
	String getName();

	/**
	 * @return Returns the display name of a player. The display name is determined by a {@link DisplayNameProvider}.
	 * To register your {@link DisplayNameProvider} use {@link PAFPlayerClass#setDisplayNameProvider(DisplayNameProvider)}
	 */
	String getDisplayName();

	List<PAFPlayer> getFriends();

	UUID getUniqueId();

	/**
	 * Sends a message to a player if he is online
	 *
	 * @param pMessage The message which should be sent. Can either be a string or a list of messages
	 *                 (a random one from the list will be sent)
	 */
	void sendMessage(Object pMessage);

	/**
	 * Sends a random message from the list to the player if he is online
	 *
	 * @param pMessages A list of messages
	 */
	void sendMessage(List<String> pMessages);

	/**
	 * Sends a message to the player if he is online
	 *
	 * @param pTextComponent The message
	 */
	void sendMessage(TextComponent pTextComponent);

	/**
	 * Sends a message to a player if he is online. The message will be broken into two messages if LINE_BREAK is used
	 * in the message
	 *
	 * @param pText The message which should be sent
	 */
	void sendMessage(String pText);

	/**
	 * Please use {@link PAFPlayer#sendPacket(TextComponent)} instead
	 *
	 * @param chat The message which should be sent
	 */
	@Deprecated
	void sendPacket(Chat chat);

	void sendPacket(TextComponent chat);

	/**
	 * @return Returns true if the player exists in the database. Returns false otherwise.
	 */
	boolean doesExist();

	int getSettingsWorth(int pSettingsID);

	List<PAFPlayer> getRequests();

	int getFriendRequestCount();

	boolean hasRequestFrom(PAFPlayer pPlayer);

	boolean hasPermission(String pPermission);

	void denyRequest(PAFPlayer pPlayer);

	/**
	 * @return Returns true if the player is online. If the player is online it is safe to cast an object of this class
	 * to an {@link OnlinePAFPlayer}
	 */
	boolean isOnline();

	boolean isAFriendOf(PAFPlayer pPlayer);

	PAFPlayer getLastPlayerWroteTo();

	void sendFriendRequest(PAFPlayer pSender);

	void addFriend(PAFPlayer pPlayer);

	PAFPlayer getPAFPlayer();

	void removeFriend(PAFPlayer pPlayer);

	void setSetting(int pSettingsID, int pNewWorth);

	void setLastPlayerWroteFrom(PAFPlayer pLastWroteTo);

	long getLastOnline();

	boolean deleteAccount();

	void updateLastOnline();
} public abstract class PAFPlayerManager {
	private static PAFPlayerManager instance;

	public PAFPlayerManager() {
		instance = this;
	}

	public static PAFPlayerManager getInstance() {
		return instance;
	}

	/**
	 * @param pPlayer The name of the player
	 * @return Returns either a PAFPlayer if the player is offline/does not exist
	 * (check with {@link PAFPlayer#doesExist()}) or an {@link OnlinePAFPlayer} if the player is online
	 */
	public abstract PAFPlayer getPlayer(String pPlayer);

	public abstract OnlinePAFPlayer getPlayer(ProxiedPlayer pPlayer);

	/**
	 * @param pPlayer The UUID of the player
	 * @return Returns either a PAFPlayer if the player is offline/does not exist
	 * (check with {@link PAFPlayer#doesExist()}) or an {@link OnlinePAFPlayer} if the player is online
	 */
	public abstract PAFPlayer getPlayer(UUID pPlayer);

	/**
	 * @param playerDataSet The PlayerDataSet. Please make sure that the data set is complete
	 * @return The PAFPlayer linked to this dataset
	 */
	public abstract PAFPlayer getPlayer(PlayerDataSet playerDataSet);
} public class PartyAPI {
	public static final int LEADER_PERMISSION_HEIGHT = 2;
	public static final int PARTY_MEMBER_PERMISSION_HEIGHT = 1;
	public static final int NO_PARTY_PERMISSION_HEIGHT = 0;

	/**
	 * Returns the party, if the given player is in a party or null if he is not
	 * in a party
	 *
	 * @param player The player
	 * @return Returns the party, if the given player is in a party or null if
	 * he is not in a party
	 */
	public static PlayerParty getParty(OnlinePAFPlayer player) {
		return PartyManager.getInstance().getParty(player);
	}

} public abstract class PartyManager implements Deactivated {
	private static PartyManager instance;
	public final long INVITATION_TIMEOUT_TIME;

	@Deprecated
	protected PartyManager() {
		this(60);
	}

	protected PartyManager(long pInvitationTimeoutTime) {
		INVITATION_TIMEOUT_TIME = pInvitationTimeoutTime;
		Disabler.getInstance().registerDeactivated(this);
		instance = this;
	}

	public static PartyManager getInstance() {
		return instance;
	}

	/**
	 * Returns the party in which a player is
	 *
	 * @param player The player
	 * @return Returns the party in which a player is or null if he isn't in a
	 * party.
	 */
	public PlayerParty getParty(OnlinePAFPlayer player) {
		return getParty(player.getUniqueId());
	}

	/**
	 * A player cannot have an party when he is offline. This is only for the case that the player has left the server before the async disconnect was executed
	 *
	 * @param pUUID The uuid of the player
	 * @return Returns the party of the player, if he has one, else it is returning null.
	 */
	public abstract PlayerParty getParty(UUID pUUID);

	/**
	 * Creates a party if the player is not already in a party.
	 *
	 * @param player The player
	 * @return Returns true if the party was created or false if not
	 */
	public abstract PlayerParty createParty(OnlinePAFPlayer player);

	protected abstract void deleteAllParties();

	/**
	 * Deletes a party which is given
	 *
	 * @param party The party
	 */
	public abstract void deleteParty(PlayerParty party);

	/**
	 * Puts the player with the party in the {@link HashMap}
	 *
	 * @param player The Player
	 * @param party  The Party
	 */
	public abstract void addPlayerToParty(OnlinePAFPlayer player, PlayerParty party);

	/**
	 * Removes the player party link from the {@link HashMap}
	 *
	 * @param player The Player
	 */
	public abstract void removePlayerFromParty(PAFPlayer player);

	@Override
	public void onDisable() {
		deleteAllParties();
	}
} public abstract class PlayerParty {
	private final String JOIN_COMMAND_NAME = " " + PartyCommand.getInstance().getSubCommand(Join.class).getCommandName() + " ";
	private final String DENY_COMMAND_NAME;
	private final Matcher acceptInviteMatcher = PLAYER_PATTERN.matcher(Main.getInstance().getMessages().getString("Party.Command.Invite.AcceptInvite"));
	private final Matcher denyInviteMatcher = PLAYER_PATTERN.matcher(Main.getInstance().getMessages().getString("Party.Command.Invite.DeclineInvite"));

	public PlayerParty() {
		PartySubCommand denyCommand = PartyCommand.getInstance().getSubCommand(PartyDenySubCommand.class);
		if (denyCommand != null)
			DENY_COMMAND_NAME = " " + denyCommand.getCommandName() + " ";
		else DENY_COMMAND_NAME = null;
	}

	/**
	 * Returns true if the given player is the leader of this party, and it will
	 * returns false if he is not the leader, of this party
	 *
	 * @param player The player
	 * @return Returns a true if the given player is the leader of this party,
	 * and it will returns false if he is not the leader, of this party
	 */
	public abstract boolean isLeader(PAFPlayer player);

	public boolean isLeader(OnlinePAFPlayer player) {
		return isLeader((PAFPlayer) player);
	}

	public abstract boolean isBanned(PAFPlayer pPlayer);

	public abstract void setBanned(PAFPlayer pPlayer, boolean pIsBanned);

	public abstract boolean isPrivate();

	public abstract void setPrivateState(boolean pIsPrivate);

	/**
	 * Returns true if the player is in the party. Returns false if the player
	 * is not in the party.
	 *
	 * @param pPlayer The player
	 * @return Returns true if the player is in the party. Returns false if the
	 * player is not in the party.
	 */
	public boolean isInParty(OnlinePAFPlayer pPlayer) {
		return isAMember(pPlayer) || pPlayer.getUniqueId().equals(getLeader().getUniqueId());
	}

	protected abstract boolean isAMember(OnlinePAFPlayer pPlayer);

	protected abstract List<UUID> getInvited();

	public boolean isNobodyInvited() {
		return getInvited().isEmpty();
	}

	/**
	 * Gets the leader of this party
	 *
	 * @return Returns the party leader
	 */
	public abstract OnlinePAFPlayer getLeader();

	/**
	 * Sets the party leader.
	 *
	 * @param player The player
	 */
	public abstract void setLeader(OnlinePAFPlayer player);

	/**
	 * @return Returns all players in this party (inclusive the leader).
	 */
	public List<OnlinePAFPlayer> getAllPlayers() {
		List<OnlinePAFPlayer> allPlayers = getPlayers();
		PAFPlayer leader = getLeader();
		if (leader != null)
			allPlayers.add((OnlinePAFPlayer) leader);
		return allPlayers;
	}

	/**
	 * Removes a player from the party
	 *
	 * @param pPlayer The player
	 */
	private void removePlayer(PAFPlayer pPlayer) {
		removePlayerSilent(pPlayer);
		sendMessage((PartyCommand.getInstance().getPrefix()
				+ PatterCollection.PLAYER_PATTERN.matcher(Main.getInstance().getMessages().getString("Party.Command.General.PlayerHasLeftTheParty")).replaceAll(Matcher.quoteReplacement(pPlayer.getDisplayName()))));
	}

	protected abstract void removePlayerSilent(PAFPlayer pPlayer);

	/**
	 * Returns the "normal" players who are in the party.
	 *
	 * @return Returns the "normal" players who are in the party.
	 */
	public abstract List<OnlinePAFPlayer> getPlayers();

	/**
	 * Adds a player to the party
	 *
	 * @param pPlayer The player
	 * @return Returns true if the player was added to this party. Returns false
	 * if the player was not added to this party
	 */
	public abstract boolean addPlayer(OnlinePAFPlayer pPlayer);

	public void leaveParty(OnlinePAFPlayer pPlayer) {
		leaveParty((PAFPlayer) pPlayer);
	}

	public void leaveParty(PAFPlayer pPlayer) {
		removePlayer(pPlayer);
		boolean needsNewLeader = needsNewLeader(pPlayer);
		BukkitBungeeAdapter.getInstance().callEvent(new LeftPartyEvent(this, pPlayer));
		if (deleteParty())
			return;
		if (needsNewLeader) {
			findNewLeader();
		}
	}

	public void kickPlayer(OnlinePAFPlayer pPlayer) {
		removePlayerSilent(pPlayer);
		BukkitBungeeAdapter.getInstance().callEvent(new LeftPartyEvent(this, pPlayer));
		pPlayer.sendMessage(Main.getInstance().getMessages()
				.get(PartyCommand.getInstance().getPrefix(), "Party.Command.Kick.KickedPlayerOutOfThePartyKickedPlayer"));
		this.sendMessage((PartyCommand.getInstance().getPrefix()
				+ PLAYER_PATTERN.matcher(Main.getInstance().getMessages().getString("Party.Command.Kick.KickedPlayerOutOfThePartyOthers"))
				.replaceAll(Matcher.quoteReplacement(pPlayer.getDisplayName()))));
		deleteParty();
	}

	/**
	 * Invites a player into this party
	 *
	 * @param pPlayer The player
	 */
	public void invite(final OnlinePAFPlayer pPlayer) {
		setBanned(pPlayer, false);
		OnlinePAFPlayer lLeader = getLeader();
		pPlayer.sendMessage((PartyCommand.getInstance().getPrefix() + PLAYER_PATTERN.matcher(Main.getInstance().getMessages()
				.getString("Party.Command.Invite.YouWereInvitedBY")).replaceAll(Matcher.quoteReplacement(lLeader.getDisplayName()))));
		TextComponent acceptMessage = new TextComponent(TextComponent.fromLegacyText(acceptInviteMatcher.replaceAll(Matcher.quoteReplacement(lLeader.getName()))));
		acceptMessage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/"
				+ PartyCommand.getInstance().getName() + JOIN_COMMAND_NAME + lLeader.getName()));
		BaseComponent[] clickHereMessage = TextComponent.fromLegacyText(Main.getInstance().getMessages().getString("Party.Command.Invite.AcceptInviteHOVER"));
		acceptMessage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, clickHereMessage));
		pPlayer.sendPacket(acceptMessage);
		if (DENY_COMMAND_NAME != null) {
			TextComponent denyMessage = new TextComponent(TextComponent.fromLegacyText(denyInviteMatcher.replaceAll(Matcher.quoteReplacement(lLeader.getName()))));
			denyMessage.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/"
					+ PartyCommand.getInstance().getName() + DENY_COMMAND_NAME + lLeader.getName()));
			clickHereMessage = TextComponent.fromLegacyText(Main.getInstance().getMessages().getString("Party.Command.Invite.DeclineInviteHOVER"));
			denyMessage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, clickHereMessage));
			pPlayer.sendPacket(denyMessage);
		}
		if (!isPrivate())
			return;
		addToInvited(pPlayer);
		final PlayerParty party = this;
		BukkitBungeeAdapter.getInstance().schedule(Main.getInstance(), () -> {
			if (isInvited(pPlayer)) {
				removeFromInvited(pPlayer);
				OnlinePAFPlayer lLeader1 = getLeader();
				if (lLeader1 == null)
					return;
				pPlayer.sendMessage((PartyCommand.getInstance().getPrefix() + PLAYER_PATTERN.matcher(Main.getInstance()
						.getMessages().getString("Party.Command.Invite.InvitationTimedOutInvited")).replaceAll(Matcher.quoteReplacement(lLeader1.getDisplayName()))));
				lLeader1.sendMessage((PartyCommand.getInstance().getPrefix() + PLAYER_PATTERN.matcher(Main.getInstance()
						.getMessages().getString("Party.Command.Invite.InvitationTimedOutLeader")).replaceAll(Matcher.quoteReplacement(pPlayer.getDisplayName()))));
				if (isPartyEmpty()) {
					lLeader1.sendMessage(
							(PartyCommand.getInstance().getPrefix()
									+ Main.getInstance().getMessages().getString(
									"Party.Command.General.DissolvedPartyCauseOfNotEnoughPlayers")));
					PartyManager.getInstance().deleteParty(party);
				}
			}
		}, PartyManager.getInstance().INVITATION_TIMEOUT_TIME);
	}

	public abstract void removeFromInvited(PAFPlayer pPlayer);

	public void removeFromInvited(OnlinePAFPlayer pPlayer) {
		removeFromInvited((PAFPlayer) pPlayer);
	}

	protected abstract void addToInvited(OnlinePAFPlayer pPlayer);

	public boolean isPartyEmpty() {
		return getPlayers().isEmpty() && isNobodyInvited();
	}

	/**
	 * Returns the size of the invitation list
	 *
	 * @return Returns the size of the invitation list
	 */
	public abstract int getInviteListSize();

	/**
	 * Returns true if the player is already invited or if the party is public. Returns false if the
	 * player is not invited.
	 *
	 * @param pPlayer The player
	 * @return Returns true if the player is already invited. Returns false if
	 * the player is not invited.
	 */
	public boolean isInvited(OnlinePAFPlayer pPlayer) {
		return !isPrivate() || getInvited().contains(pPlayer.getUniqueId());
	}

	public void sendMessage(TextComponent pText) {
		for (OnlinePAFPlayer player : getAllPlayers())
			player.sendMessage(pText);
	}

	public void sendMessage(String pText) {
		for (OnlinePAFPlayer player : getAllPlayers())
			player.sendMessage(pText);
	}

	private boolean deleteParty() {
		int partyMemberCount = this.getAllPlayers().size();
		if ((partyMemberCount < 2 && isPrivate()) || partyMemberCount == 0) {
			sendMessage((PartyCommand.getInstance().getPrefix() + Main.getInstance().getMessages()
					.getString("Party.Command.General.DissolvedPartyCauseOfNotEnoughPlayers")));
			PartyManager.getInstance().deleteParty(this);
			for (OnlinePAFPlayer player : getAllPlayers())
				removePlayerSilent(player);
			return true;
		}
		return false;
	}

	protected abstract boolean needsNewLeader(PAFPlayer pPlayer);

	protected abstract void findNewLeader();

} public abstract class PartyJoinInviteSubCommand extends PartySubCommand {
	private static List<MaxPlayerPermissionCollection> maxPlayerPermissionCollections = null;
	private final Matcher TOO_MANY_PLAYERS_IN_PARTY_MATCHER;
	private final int DEFAULT_MAX_PLAYERS_PER_PARTY;

	public PartyJoinInviteSubCommand(List<String> pCommands, int pPriority, String pHelpText, String pPermission, String pMaxPlayersInPartyMessage) {
		super(pCommands, pPriority, pHelpText, pPermission);
		ConfigurationCreator config = Main.getInstance().getGeneralConfig();
		DEFAULT_MAX_PLAYERS_PER_PARTY = config.getInt("Party.MaxPlayersPerParty.Default");
		TOO_MANY_PLAYERS_IN_PARTY_MATCHER = MAX_PLAYERS_IN_PARTY_PATTERN
				.matcher(pMaxPlayersInPartyMessage);
		if (DEFAULT_MAX_PLAYERS_PER_PARTY != 0 && maxPlayerPermissionCollections == null) {
			maxPlayerPermissionCollections = new ArrayList<>();
			for (String key : config.getSectionKeys("Party.MaxPlayersPerParty.AddSlotsPermissions")) {
				maxPlayerPermissionCollections.add(new MaxPlayerPermissionCollection(config.getInt("Party.MaxPlayersPerParty.AddSlotsPermissions." + key + ".SlotsToAdd"),
						config.getString("Party.MaxPlayersPerParty.AddSlotsPermissions." + key + ".Permission")));
			}
		}
	}

	protected boolean canInvite(OnlinePAFPlayer pPartyLeader, PlayerParty pParty, OnlinePAFPlayer pMessageReceiver) {
		if (!pPartyLeader.getPlayer()
				.hasPermission(Main.getInstance().getGeneralConfig().getString("Party.MaxPlayersPerParty.NoLimitPermission")))
			if (DEFAULT_MAX_PLAYERS_PER_PARTY > 1) {
				int maxPlayersForThisParty = calculateMaxPlayersForParty(pPartyLeader, pParty);
				if (maxPlayersForThisParty < pParty.getAllPlayers().size() + pParty.getInviteListSize() + 1) {
					pMessageReceiver.sendMessage(PREFIX + TOO_MANY_PLAYERS_IN_PARTY_MATCHER
							.replaceAll(Matcher.quoteReplacement(
									maxPlayersForThisParty + "")));
					return false;
				}
			}
		return true;
	}

	private int calculateMaxPlayersForParty(OnlinePAFPlayer pPlayer, PlayerParty party) {
		int maxPlayersInParty = DEFAULT_MAX_PLAYERS_PER_PARTY;
		for (PartyJoinInviteSubCommand.MaxPlayerPermissionCollection maxPlayerPermissionCollection : maxPlayerPermissionCollections)
			if (pPlayer.hasPermission(maxPlayerPermissionCollection.PERMISSION)) {
				maxPlayersInParty += maxPlayerPermissionCollection.SLOTS_TO_ADD;
			}
		return maxPlayersInParty;
	}

	private static class MaxPlayerPermissionCollection {
		public final int SLOTS_TO_ADD;
		public final String PERMISSION;

		private MaxPlayerPermissionCollection(int pSlotsToAdd, String permission) {
			SLOTS_TO_ADD = pSlotsToAdd;
			PERMISSION = permission;
		}
	}
} public class Main extends PAFPluginBase implements ErrorReporter {
	/**
	 * The main instance of this plugin
	 */
	private static Main instance;
	private final List<PAFExtension> pafExtensions = new ArrayList<>();
	/**
	 * The configuration
	 */
	private ConfigLoader config;
	/**
	 * The messages.yml
	 */
	private MessagesLoader messages = null;
	/**
	 * The party prefix
	 */
	private String partyPrefix;
	/**
	 * The language
	 */
	private Language language;
	private Friends friendCommand;
	private boolean shuttingDown = false;

	public static Main getInstance() {
		return instance;
	}

	@Deprecated
	public static PartyManager getPartyManager() {
		return PartyManager.getInstance();
	}

	@Deprecated
	public static PAFPlayerManager getPlayerManager() {
		return PAFPlayerManager.getInstance();
	}

	public ConfigurationCreator getGeneralConfig() {
		return config;
	}

	/**
	 * Will be execute on enable
	 */
	@Override
	public void onEnable() {
		instance = (this);
		loadConfiguration();
		try {
			initPAFClasses();
			registerCommands();
			registerListeners();
			new Metrics(this, 508);
			if (getConfig().getBoolean("General.CheckForUpdates")) {
				UpdateSearcher searcher = new UpdateSearcher("Party-and-Friends-Free", getDescription().getVersion());
				ProxyServer.getInstance().getConsole().sendMessage(new TextComponent(searcher.checkForUpdate()));
			}
		} catch (SQLException e) {
			if (e.getMessage().contains("Unable to load authentication plugin 'caching_sha2_password'."))
				initError(e, BootErrorType.SHA_ENCRYPTED_PASSWORD);
			else if (e.getMessage().contains("REFERENCES command denied to user"))
				initError(e, BootErrorType.MISSING_PERMISSION_REFERENCE_COMMAND);
			else
				initError(e, BootErrorType.MYSQL_CONNECTION_PROBLEM);
		}
	}

	private void initError(Throwable e, BootErrorType pType) {
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.Party.Disabled"))
			registerCommand(new BootErrorCommand(
					(getGeneralConfig().getStringList("Commands.Party.TopCommands.Party.Names").toArray(new String[0])), pType));
		Command partyChatCommand = new BootErrorCommand(
				(getGeneralConfig().getStringList("Commands.Party.TopCommands.PartyChat.Names").toArray(new String[0])), pType);
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.PartyChat.Disabled"))
			registerCommand(partyChatCommand);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Friend.Disabled"))
			registerCommand(new BootErrorCommand(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Friend.Names").toArray(new String[0]), pType));
		BootErrorCommand msg = new BootErrorCommand(
				(getGeneralConfig().getStringList("Commands.Friends.TopCommands.MSG.Names").toArray(new String[0])), pType);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.MSG.Disabled"))
			registerCommand(msg);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Reply.Disabled"))
			registerCommand(new BootErrorCommand(
					(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Reply.Names").toArray(new String[0])), pType));
		CommandSender console = ProxyServer.getInstance().getConsole();
		reportError(console, pType);
		e.printStackTrace();
	}

	private void initPAFClasses() throws SQLException {
		PoolData poolData = new PoolData(Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.MinPoolSize"),
				Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.MaxPoolSize"),
				Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.InitialPoolSize"), Main.getInstance().getGeneralConfig().getInt("MySQL.Pool.IdleConnectionTestPeriod"),
				Main.getInstance().getGeneralConfig().getBoolean("MySQL.Pool.TestConnectionOnCheckin"), Main.getInstance().getGeneralConfig().getString("MySQL.Pool.ConnectionPool"));
		MySQLData mySQLData = new MySQLData(getGeneralConfig().get("MySQL.Host").toString(),
				getGeneralConfig().get("MySQL.Username").toString(), getGeneralConfig().get("MySQL.Password").toString(),
				getGeneralConfig().getInt("MySQL.Port"), getGeneralConfig().get("MySQL.Database").toString(),
				getGeneralConfig().get("MySQL.TablePrefix").toString(), getGeneralConfig().getBoolean("MySQL.UseSSL"), getGeneralConfig().getBoolean("MySQL.Cache"),
				false, 0);
		new PAFPlayerManagerMySQL(mySQLData, poolData);
		if (getGeneralConfig().getBoolean("General.MultiCoreEnhancement")) {
			PAFPlayerMySQL.setMultiCoreEnhancement(true);
		}
		new LocalPartyManager(Main.getInstance().getGeneralConfig().getInt("Commands.Party.SubCommands.Invite.InvitationTimeOutTimeInSeconds"));
		new StandardPermissionProvider();
		new ServerDisplayNameCollection(getGeneralConfig());
	}

	@Override
	public void onDisable() {
		shuttingDown = true;
		ProxyServer.getInstance().getPluginManager().unregisterListeners(this);
		ProxyServer.getInstance().getPluginManager().unregisterCommands(this);
		Disabler.getInstance().disableAll();
		getProxy().getScheduler().cancel(this);
	}

	/**
	 * Loads the configuration files(config.yml and messages.yml)
	 */
	private void loadConfiguration() {
		try {
			config = new ConfigLoader(new File(Main.getInstance().getDataFolder(), "config.yml"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			language = Language.valueOf(getGeneralConfig().getString("General.Language").toUpperCase());
		} catch (IllegalArgumentException e) {
			getProxy().getConsole().sendMessage(new TextComponent(TextComponent.fromLegacyText("&4The given language is not supported by Party and Friends. English will be used instead.")));
			language = Language.ENGLISH;
			e.printStackTrace();
		}
		try {
			messages = new MessagesLoader(language, getGeneralConfig().getBoolean("General.UseOwnLanguageFile"), new File(getDataFolder(), "messages.yml"), this);
			if (getGeneralConfig().getBoolean("General.UseOwnLanguageFile"))
				language = Language.OWN;
		} catch (IOException e) {
			e.printStackTrace();
		}
		partyPrefix = (getMessages().getString("Party.General.PartyPrefix"));
		System.setProperty("com.mchange.v2.log.MLog", "com.mchange.v2.log.FallbackMLog");
		System.setProperty("com.mchange.v2.log.FallbackMLog.DEFAULT_CUTOFF_LEVEL", "WARNING");
		getAdapter().setForceUuidSupport(config.getBoolean("General.ForceUUIDSupportOnOfflineServers"));
	}

	/**
	 * Registers the listeners
	 */
	private void registerListeners() {
		BukkitBungeeAdapter.getInstance().registerListener(new PlayerDisconnectListener(), this);
		ServerSwitchListener serverSwitchListener = new ServerSwitchListener();
		if (!getGeneralConfig().getBoolean("General.DisableAutomaticPartyServerSwitching"))
			BukkitBungeeAdapter.getInstance().registerListener(serverSwitchListener, this);
		JoinEvent joinEventListener;
		BukkitBungeeAdapter.getInstance().registerListener(joinEventListener = new JoinEvent(), this);
		Exception e = joinEventListener.verify();
		if (e != null)
			initError(e, BootErrorType.TOO_OLD_VERSION);
	}

	/**
	 * Registers the commands
	 */
	private void registerCommands() {
		String friendsPrefix = (getMessages().getString("Friends.General.Prefix"));
		new PartyCommand(
				(getGeneralConfig().getStringList("Commands.Party.TopCommands.Party.Names").toArray(new String[0])), partyPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.Party.Disabled"))
			registerTopCommand(PartyCommand.getInstance());
		PartyChat partyChatCommand = new PartyChat(
				(getGeneralConfig().getStringList("Commands.Party.TopCommands.PartyChat.Names").toArray(new String[0])), partyPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Party.TopCommands.PartyChat.Disabled"))
			registerTopCommand(partyChatCommand);
		friendCommand = new Friends(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Friend.Names"), friendsPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Friend.Disabled"))
			registerTopCommand(friendCommand);
		MSG friendsMSGCommand = new MSG(
				(getGeneralConfig().getStringList("Commands.Friends.TopCommands.MSG.Names").toArray(new String[0])), friendsPrefix);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.MSG.Disabled"))
			registerTopCommand(friendsMSGCommand);
		if (!getGeneralConfig().getBoolean("Commands.Friends.TopCommands.Reply.Disabled"))
			registerTopCommand(new Reply(
					(getGeneralConfig().getStringList("Commands.Friends.TopCommands.Reply.Names").toArray(new String[0])), friendsPrefix));
		if (getGeneralConfig().getBoolean("Commands.PAFAdmin.Enabled"))
			registerCommand(new PAFAdminCommand(getGeneralConfig().getStringList("Commands.PAFAdmin.Names").toArray(new String[0])));
	}

	@Deprecated
	public PartyChat getPartyChatCommand() {
		return PartyChat.getInstance();
	}

	@Deprecated
	public MSG getFriendsMSGCommand() {
		return MSG.getInstance();
	}

	@Deprecated
	public Friends getFriendsCommand() {
		return friendCommand;
	}

	/**
	 * @return Returns the normal Main config on Bungeecord, but on Spigot it returns the GUI config.
	 * For that reason it should not be used and instead getGeneralConfig() should be used.
	 */
	@Deprecated
	public Configuration getConfig() {
		return config.getCreatedConfiguration();
	}

	@Deprecated
	public String getFriendsPrefix() {
		return Friends.getInstance().getPrefix();
	}

	public Language getLanguage() {
		return language;
	}

	public LanguageConfiguration getMessages() {
		return messages;
	}

	@Deprecated
	public LanguageConfiguration getMessagesYml() {
		return getMessages();
	}

	@Deprecated
	public PartyCommand getPartyCommand() {
		return PartyCommand.getInstance();
	}

	@Deprecated
	public String getPartyPrefix() {
		return PartyCommand.getInstance().getPrefix();
	}

	public void registerExtension(PAFExtension pPAFExtension) {
		pafExtensions.add(pPAFExtension);
	}

	public void unregisterExtension(PAFExtension pPAFExtension) {
		pafExtensions.remove(pPAFExtension);
	}

	public void reload() {
		onDisable();
		onEnable();
		List<PAFExtension> toReload = new ArrayList<>(pafExtensions);
		pafExtensions.clear();
		for (PAFExtension extension : toReload)
			extension.reload();
	}

	public boolean isShuttingDown() {
		return shuttingDown;
	}
} public class InviteSetting extends SimpleSetting {
	public static final int SETTINGS_ID = 1;
	public static final int PLAYER_RECEIVES_INVITES_BY_EVERYONE_STATE = 0;
	public static final int PLAYER_RECEIVES_INVITES_BY_FRIENDS_STATE = 1;

	public InviteSetting(List<String> pSettingNames, String pPermission, int pPriority) {
		super(pSettingNames, pPermission, pPriority);
	}

	@Override
	protected String getMessage(OnlinePAFPlayer pPlayer) {
		String identifier;
		if (pPlayer.getSettingsWorth(SETTINGS_ID) == PLAYER_RECEIVES_INVITES_BY_EVERYONE_STATE) {
			identifier = "Friends.Command.Settings.PartyInvitedByEveryone";
		} else {
			identifier = "Friends.Command.Settings.PartyInvitedByFriends";
		}
		return Main.getInstance().getMessages().getString(identifier);
	}

	@Override
	public void changeSetting(OnlinePAFPlayer pPlayer, String[] pArgs) {
		int worthNow = pPlayer.changeSettingsWorth(SETTINGS_ID);
		if (worthNow == PLAYER_RECEIVES_INVITES_BY_EVERYONE_STATE) {
			pPlayer.sendMessage((Friends.getInstance().getPrefix() + Main.getInstance()
					.getMessages().getString("Friends.Command.Settings.NowYouCanGetInvitedByEveryone")));
		} else {
			pPlayer.sendMessage((Friends.getInstance().getPrefix() + Main.getInstance()
					.getMessages().getString("Friends.Command.Settings.NowYouCanGetInvitedByFriends")));
		}
	}
} public static final Pattern PLAYER_PATTERN = Pattern.compile("[PLAYER]", Pattern.LITERAL);
import de.simonsator.partyandfriends.api.adapter.BukkitBungeeAdapter;
import de.simonsator.partyandfriends.api.events.command.party.InviteEvent;
import de.simonsator.partyandfriends.api.pafplayers.OnlinePAFPlayer;
import de.simonsator.partyandfriends.api.pafplayers.PAFPlayer;
import de.simonsator.partyandfriends.api.pafplayers.PAFPlayerManager;
import de.simonsator.partyandfriends.api.party.PartyAPI;
import de.simonsator.partyandfriends.api.party.PartyManager;
import de.simonsator.partyandfriends.api.party.PlayerParty;
import de.simonsator.partyandfriends.api.party.abstractcommands.PartyJoinInviteSubCommand;
import de.simonsator.partyandfriends.main.Main;
import de.simonsator.partyandfriends.party.settings.InviteSetting;
import java.util.List;
import java.util.regex.Matcher;
import static de.simonsator.partyandfriends.utilities.PatterCollection.PLAYER_PATTERN;
package de.simonsator.partyandfriends.party.subcommand;




/**
 * The /party chat Invite
 *
 * @author Simonsator
 * @version 1.0.0
 */
public class Invite extends PartyJoinInviteSubCommand {

	public Invite(List<String> pCommands, int pPriority, String pHelpText, String pPermission) {
		super(pCommands, pPriority, pHelpText, pPermission, Main.getInstance().getMessages()
				.getString("Party.Command.Invite.MaxPlayersInPartyReached"));
	}


	/**
	 * Will be executed on /party invite
	 *
	 * @param pPlayer The player
	 * @param args    Arguments The arguments
	 */
	@Override
	public void onCommand(OnlinePAFPlayer pPlayer, String[] args) {
		if (!isPlayerGiven(pPlayer, args))
			return;
		PAFPlayer playerQuery = PAFPlayerManager.getInstance().getPlayer(args[0]);