/* * WorldGuard, a suite of tools for Minecraft * Copyright (C) sk89q * Copyright (C) WorldGuard team and contributors * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ package com.sk89q.worldguard.session; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.sk89q.worldedit.world.World; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; import com.sk89q.worldguard.session.handler.EntryFlag; import com.sk89q.worldguard.session.handler.ExitFlag; import com.sk89q.worldguard.session.handler.FarewellFlag; import com.sk89q.worldguard.session.handler.FeedFlag; import com.sk89q.worldguard.session.handler.GameModeFlag; import com.sk89q.worldguard.session.handler.GodMode; import com.sk89q.worldguard.session.handler.GreetingFlag; import com.sk89q.worldguard.session.handler.Handler; import com.sk89q.worldguard.session.handler.HealFlag; import com.sk89q.worldguard.session.handler.InvincibilityFlag; import com.sk89q.worldguard.session.handler.NotifyEntryFlag; import com.sk89q.worldguard.session.handler.NotifyExitFlag; import com.sk89q.worldguard.session.handler.TimeLockFlag; import com.sk89q.worldguard.session.handler.WaterBreathing; import com.sk89q.worldguard.session.handler.WeatherLockFlag; import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.BiPredicate; import java.util.logging.Level; import static com.google.common.base.Preconditions.checkNotNull; public abstract class AbstractSessionManager implements SessionManager { public static final int RUN_DELAY = 20; public static final long SESSION_LIFETIME = 10; private static final BiPredicate BYPASS_PERMISSION_TEST = (world, player) -> { return player.hasPermission("worldguard.region.bypass." + world.getName()); }; private final LoadingCache bypassCache = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(2, TimeUnit.SECONDS) .build(CacheLoader.from(tuple -> BYPASS_PERMISSION_TEST.test(tuple.getWorld(), tuple.getPlayer()))); private final LoadingCache sessions = CacheBuilder.newBuilder() .expireAfterAccess(SESSION_LIFETIME, TimeUnit.MINUTES) .build(CacheLoader.from(key -> createSession(key.playerRef.get()))); private boolean hasCustom = false; private List> handlers = new LinkedList<>(); private static final List> defaultHandlers = new LinkedList<>(); static { Handler.Factory[] factories = { HealFlag.FACTORY, FeedFlag.FACTORY, NotifyEntryFlag.FACTORY, NotifyExitFlag.FACTORY, EntryFlag.FACTORY, ExitFlag.FACTORY, FarewellFlag.FACTORY, GreetingFlag.FACTORY, GameModeFlag.FACTORY, InvincibilityFlag.FACTORY, TimeLockFlag.FACTORY, WeatherLockFlag.FACTORY, GodMode.FACTORY, WaterBreathing.FACTORY }; defaultHandlers.addAll(Arrays.asList(factories)); } protected AbstractSessionManager() { handlers.addAll(defaultHandlers); } @Override public boolean customHandlersRegistered() { return hasCustom; } @Override public boolean registerHandler(Handler.Factory factory, @Nullable Handler.Factory after) { if (factory == null) return false; WorldGuard.logger.log(Level.INFO, "Registering session handler " + factory.getClass().getEnclosingClass().getName()); hasCustom = true; if (after == null) { handlers.add(factory); } else { int index = handlers.indexOf(after); if (index == -1) return false; handlers.add(index + 1, factory); // shifts "after" right one, and everything after "after" right one } return true; } @Override public boolean unregisterHandler(Handler.Factory factory) { if (defaultHandlers.contains(factory)) { WorldGuard.logger.log(Level.WARNING, "Someone is unregistering a default WorldGuard handler: " + factory.getClass().getEnclosingClass().getName() + ". This may cause parts of WorldGuard to stop functioning"); } else { WorldGuard.logger.log(Level.INFO, "Unregistering session handler " + factory.getClass().getEnclosingClass().getName()); } return handlers.remove(factory); } @Override public boolean hasBypass(LocalPlayer player, World world) { Session sess = getIfPresent(player); if (sess == null || sess.hasBypassDisabled()) { return false; } if (WorldGuard.getInstance().getPlatform().getGlobalStateManager().disablePermissionCache) { return BYPASS_PERMISSION_TEST.test(world, player); } return bypassCache.getUnchecked(new WorldPlayerTuple(world, player)); } @Override public void resetState(LocalPlayer player) { checkNotNull(player, "player"); @Nullable Session session = sessions.getIfPresent(new CacheKey(player)); if (session != null) { session.resetState(player); } } @Override @Nullable public Session getIfPresent(LocalPlayer player) { return sessions.getIfPresent(new CacheKey(player)); } @Override public Session get(LocalPlayer player) { return sessions.getUnchecked(new CacheKey(player)); } @Override public Session createSession(LocalPlayer player) { Session session = new Session(this); for (Handler.Factory factory : handlers) { session.register(factory.create(session)); } session.initialize(player); return session; } protected static final class CacheKey { final WeakReference playerRef; final UUID uuid; CacheKey(LocalPlayer player) { playerRef = new WeakReference<>(player); uuid = player.getUniqueId(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CacheKey cacheKey = (CacheKey) o; return uuid.equals(cacheKey.uuid); } @Override public int hashCode() { return uuid.hashCode(); } } }