/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.internal.providers.jail; import com.jcwhatever.nucleus.Nucleus; import com.jcwhatever.nucleus.internal.NucLang; import com.jcwhatever.nucleus.internal.NucMsg; import com.jcwhatever.nucleus.internal.providers.InternalProviderInfo; import com.jcwhatever.nucleus.managed.language.Localizable; import com.jcwhatever.nucleus.managed.scheduler.Scheduler; import com.jcwhatever.nucleus.providers.Provider; import com.jcwhatever.nucleus.providers.jail.IJail; import com.jcwhatever.nucleus.providers.jail.IJailProvider; import com.jcwhatever.nucleus.providers.jail.IJailSession; import com.jcwhatever.nucleus.providers.storage.DataStorage; import com.jcwhatever.nucleus.storage.IDataNode; import com.jcwhatever.nucleus.utils.DateUtils; import com.jcwhatever.nucleus.utils.DateUtils.TimeRound; import com.jcwhatever.nucleus.utils.MetaKey; import com.jcwhatever.nucleus.utils.PreCon; import com.jcwhatever.nucleus.utils.Rand; import com.jcwhatever.nucleus.utils.player.PlayerUtils; import com.jcwhatever.nucleus.utils.text.TextUtils; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.plugin.Plugin; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.WeakHashMap; /** * Internal implementation of {@link IJailProvider}. */ public final class NucleusJailProvider extends Provider implements IJailProvider{ public static final String NAME = "NucleusJails"; @Localizable static final String _RELEASE_TIME = "Release in {0} minutes."; private static final MetaKey<Long> RELEASE_MESSAGE_META = new MetaKey<>(Long.class); private IDataNode _dataNode; private Map<UUID, IJailSession> _sessionMap = new HashMap<>(20); private Map<String, IJail> _jails = new HashMap<>(5); private Map<UUID, Location> _lateReleases = new HashMap<UUID, Location>(20); private Map<Plugin, IDataNode> _dataNodes = new WeakHashMap<>(10); private Warden _warden = new Warden(); private NucleusJail _serverJail; public NucleusJailProvider() { setInfo(new InternalProviderInfo(this.getClass(), NAME, "Default jail provider.")); } @Override public void onEnable() { _dataNode = getDataNode(); _serverJail = new NucleusJail(this, Nucleus.getPlugin(), "Server", _dataNode.getNode("Server")); // check for prisoner release. Scheduler.runTaskRepeat(Nucleus.getPlugin(), Rand.getInt(1, 25), 25, _warden); Bukkit.getPluginManager().registerEvents(new BukkitEventListener(), Nucleus.getPlugin()); // load late releases IDataNode lateNode = _dataNode.getNode("late-release"); for (IDataNode dataNode : lateNode) { UUID playerId = TextUtils.parseUUID(dataNode.getName()); if (playerId == null) continue; Location release = dataNode.getLocation(""); if (release == null) continue; _lateReleases.put(playerId, release); } } @Override public void onDisable() { Collection<IJailSession> sessions = getSessions(); for (IJailSession session : sessions) { session.release(); } } @Override public NucleusJail getServerJail() { return _serverJail; } @Nullable @Override public NucleusJail createJail(Plugin plugin, String name) { PreCon.notNull(plugin); PreCon.validNodeName(name); String lookup = getJailLookup(plugin, name); IJail current = _jails.get(lookup); if (current != null) return null; IDataNode dataNode = _dataNodes.get(plugin); if (dataNode == null) { dataNode = DataStorage.get(plugin, getDataPath("jails")); dataNode.load(); _dataNodes.put(plugin, dataNode); } NucleusJail jail = new NucleusJail(this, plugin, name, dataNode.getNode(name)); _jails.put(lookup, jail); return jail; } @Nullable @Override public IJail getJail(Plugin plugin, String name) { PreCon.notNull(plugin); PreCon.notNullOrEmpty(name); return _jails.get(getJailLookup(plugin, name)); } @Override public Collection<IJail> getJails() { return new ArrayList<>(_jails.values()); } @Nullable @Override public IJailSession getSession(UUID playerId) { PreCon.notNull(playerId); return _sessionMap.get(playerId); } @Override public Collection<IJailSession> getSessions() { return new ArrayList<>(_sessionMap.values()); } @Override public boolean isPrisoner(UUID playerId) { PreCon.notNull(playerId); return _sessionMap.containsKey(playerId); } @Override public boolean release(UUID playerId) { PreCon.notNull(playerId); IJailSession session = getSession(playerId); if (session != null) { session.dispose(); _warden.run(true, true); return true; } return false; } NucleusJailSession createSession(NucleusJail jail, UUID playerId, Date expires) { NucleusJailSession session = new NucleusJailSession(jail, playerId, expires); _sessionMap.put(playerId, session); return session; } void removeJail(NucleusJail jail) { if (jail == _serverJail) throw new AssertionError("Server jail cannot be disposed."); _jails.remove(getJailLookup(jail.getPlugin(), jail.getName())); Collection<IJailSession> sessions = getSessions(); for (IJailSession session : sessions) { if (session.equals(jail)) session.dispose(); } } boolean isLateRelease(UUID playerId) { return _lateReleases.containsKey(playerId); } // register a late release so the next time a player re-spawns or logs // in they will be teleported to the release location. private void registerLateRelease(UUID playerId, Location releaseLocation) { PreCon.notNull(playerId); PreCon.notNull(releaseLocation); releaseLocation = releaseLocation.clone(); _lateReleases.put(playerId, releaseLocation); IDataNode node = _dataNode.getNode("late-release"); node.set(playerId.toString(), releaseLocation); node.save(); } // remove a late release @Nullable private Location unregisterLateRelease(UUID playerId) { PreCon.notNull(playerId); Location result = _lateReleases.remove(playerId); if (result != null) { IDataNode node = _dataNode.getNode("late-release"); node.remove(playerId.toString()); node.save(); } return result; } private String getJailLookup(Plugin plugin, String name) { return plugin.getName() + ':' + name.toLowerCase(); } /** * Scheduled task responsible for determining when * to release players from prison. */ class Warden implements Runnable { boolean hasProcessedLateReleases; @Override public void run () { run(false, false); } public void run(boolean silent, boolean doLateReleases) { if (_sessionMap.isEmpty()) return; if (!hasProcessedLateReleases) { doLateReleases = true; hasProcessedLateReleases = true; } List<IJailSession> jailSessions = new ArrayList<>(_sessionMap.values()); Date now = new Date(); for (IJailSession session : jailSessions) { boolean isLateRelease = _lateReleases.containsKey(session.getPlayerId()); if (isLateRelease && !doLateReleases) continue; if (isLateRelease || session.isExpired()) { Player p = PlayerUtils.getPlayer(session.getPlayerId()); if (p == null) { // register player to be released at next login Location releaseLocation = getReleaseLocation(session); if (releaseLocation == null) { NucMsg.warning("Failed to find a jail release location"); continue; } registerLateRelease(session.getPlayerId(), releaseLocation); continue; } _sessionMap.remove(session.getPlayerId()); Location releaseLoc = unregisterLateRelease(session.getPlayerId()); if (releaseLoc == null) releaseLoc = getReleaseLocation(session); if (!session.isReleased()) session.release(); if (releaseLoc != null) p.teleport(releaseLoc); } else if (!silent) { long releaseMinutes = DateUtils .getDeltaMinutes(now, session.getExpiration(), TimeRound.ROUND_UP); Long lastMessageMin = session.getMeta().get(RELEASE_MESSAGE_META); if (lastMessageMin == null || (lastMessageMin != releaseMinutes && (releaseMinutes <= 5 || releaseMinutes % 10 == 0))) { Player p = PlayerUtils.getPlayer(session.getPlayerId()); if (p != null) { NucMsg.tellAnon(p, NucLang.get(_RELEASE_TIME, releaseMinutes)); } session.getMeta().setKey(RELEASE_MESSAGE_META, releaseMinutes); } } } } @Nullable private Location getReleaseLocation(IJailSession session) { Location releaseLoc = session.getReleaseLocation(); if (releaseLoc == null) { World world = session.getJail().getRegion().getWorld(); if (world != null) releaseLoc = world.getSpawnLocation(); } return releaseLoc; } } final class BukkitEventListener implements Listener { @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onCommandPreprocess(PlayerCommandPreprocessEvent event) { // prevent prisoners from using commands if (isPrisoner(event.getPlayer().getUniqueId())) { event.setCancelled(true); } } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) private void onPlayerInteract(PlayerInteractEvent event) { // prevent prisoners from interacting if (isPrisoner(event.getPlayer().getUniqueId())) { event.setCancelled(true); } } @EventHandler private void onPlayerJoin(final PlayerJoinEvent event) { // make sure player that is logging in is released if no longer a prisoner if (isLateRelease(event.getPlayer().getUniqueId())) { Scheduler.runTaskLater(Nucleus.getPlugin(), 5, new Runnable() { @Override public void run() { release(event.getPlayer().getUniqueId()); } }); } } @EventHandler(priority = EventPriority.HIGHEST) private void onPlayerRespawn(PlayerRespawnEvent event) { UUID playerId = event.getPlayer().getUniqueId(); // release prisoner if (isLateRelease(playerId)) { release(playerId); } else if (isPrisoner(playerId)) { // send prisoner back to jail IJailSession session = getSession(playerId); if (session != null) { Location location = session.getJail().getRandomTeleport(); if (location != null) { event.setRespawnLocation(location); } } } } } }