Sponge support

This commit is contained in:
Luck
2016-08-05 12:58:27 +02:00
Unverified
parent 56e11b8b85
commit 03450c3339
37 changed files with 908 additions and 126 deletions
@@ -0,0 +1,178 @@
package me.lucko.luckperms;
import com.google.inject.Inject;
import lombok.Getter;
import me.lucko.luckperms.api.LuckPermsApi;
import me.lucko.luckperms.api.implementation.ApiProvider;
import me.lucko.luckperms.data.Datastore;
import me.lucko.luckperms.data.methods.FlatfileDatastore;
import me.lucko.luckperms.data.methods.MySQLDatastore;
import me.lucko.luckperms.data.methods.SQLiteDatastore;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.listeners.PlayerListener;
import me.lucko.luckperms.runnables.UpdateTask;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.SpongeUserManager;
import me.lucko.luckperms.users.UserManager;
import me.lucko.luckperms.utils.LPConfiguration;
import me.lucko.luckperms.utils.LogUtil;
import me.lucko.luckperms.utils.UuidCache;
import org.slf4j.Logger;
import org.spongepowered.api.Game;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.CommandManager;
import org.spongepowered.api.config.ConfigDir;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.game.state.GamePreInitializationEvent;
import org.spongepowered.api.event.game.state.GameStoppingServerEvent;
import org.spongepowered.api.plugin.Plugin;
import org.spongepowered.api.scheduler.Scheduler;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Getter
@Plugin(id = "luckperms", name = "LuckPerms", version = LPSpongePlugin.VERSION)
public class LPSpongePlugin implements LuckPermsPlugin {
static final String VERSION = "1.5"; // TODO load this from pom
@Inject
private Logger logger;
@Inject
private Game game;
@Inject
@ConfigDir(sharedRoot = false)
private Path configDir;
private Scheduler scheduler = Sponge.getScheduler();
private LPConfiguration configuration;
private UserManager userManager;
private GroupManager groupManager;
private TrackManager trackManager;
private Datastore datastore;
private UuidCache uuidCache;
@Listener
public void onEnable(GamePreInitializationEvent event) {
getLog().info("Loading configuration...");
configuration = new SpongeConfig(this);
// register events
Sponge.getEventManager().registerListeners(this, new PlayerListener(this));
// register commands
getLog().info("Registering commands...");
CommandManager cmdService = Sponge.getCommandManager();
cmdService.register(this, new SpongeCommand(this), "luckperms", "perms", "lp", "permissions", "p", "perm");
getLog().info("Detecting storage method...");
final String storageMethod = configuration.getStorageMethod();
if (storageMethod.equalsIgnoreCase("mysql")) {
getLog().info("Using MySQL as storage method.");
datastore = new MySQLDatastore(this, configuration.getDatabaseValues());
} else if (storageMethod.equalsIgnoreCase("sqlite")) {
getLog().info("Using SQLite as storage method.");
datastore = new SQLiteDatastore(this, new File(getStorageDir(), "luckperms.sqlite"));
} else if (storageMethod.equalsIgnoreCase("flatfile")) {
getLog().info("Using Flatfile (JSON) as storage method.");
datastore = new FlatfileDatastore(this, getStorageDir());
} else {
getLog().severe("Storage method '" + storageMethod + "' was not recognised. Using SQLite as fallback.");
datastore = new SQLiteDatastore(this, new File(getStorageDir(), "luckperms.sqlite"));
}
getLog().info("Initialising datastore...");
datastore.init();
getLog().info("Loading internal permission managers...");
uuidCache = new UuidCache(getConfiguration().getOnlineMode());
userManager = new SpongeUserManager(this);
groupManager = new GroupManager(this);
trackManager = new TrackManager();
// Run update task to refresh any online users
getLog().info("Scheduling Update Task to refresh any online users.");
try {
new UpdateTask(this).run();
} catch (Exception e) {
e.printStackTrace();
}
int mins = getConfiguration().getSyncTime();
if (mins > 0) {
scheduler.createTaskBuilder().async().interval(mins, TimeUnit.MINUTES).execute(new UpdateTask(this))
.submit(LPSpongePlugin.this);
}
getLog().info("Registering API...");
final ApiProvider provider = new ApiProvider(this);
LuckPerms.registerProvider(provider);
Sponge.getServiceManager().setProvider(this, LuckPermsApi.class, provider);
getLog().info("Successfully loaded.");
}
@Listener
public void onDisable(GameStoppingServerEvent event) {
getLog().info("Closing datastore...");
datastore.shutdown();
getLog().info("Unregistering API...");
LuckPerms.unregisterProvider();
}
private File getStorageDir() {
File base = configDir.toFile().getParentFile().getParentFile();
File luckperms = new File(base, "luckperms");
luckperms.mkdirs();
return luckperms;
}
@Override
public me.lucko.luckperms.api.Logger getLog() {
return LogUtil.wrap(getLogger());
}
@Override
public String getVersion() {
return VERSION;
}
@Override
public String getPlayerStatus(UUID uuid) {
return game.getServer().getPlayer(getUuidCache().getExternalUUID(uuid)).isPresent() ? "&aOnline" : "&cOffline";
}
@Override
public int getPlayerCount() {
return game.getServer().getOnlinePlayers().size();
}
@Override
public List<String> getPlayerList() {
return game.getServer().getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
}
@Override
public void runUpdateTask() {
scheduler.createTaskBuilder().async().execute(new UpdateTask(this)).submit(LPSpongePlugin.this);
}
@Override
public void doAsync(Runnable r) {
scheduler.createTaskBuilder().async().execute(r).submit(LPSpongePlugin.this);
}
@Override
public void doSync(Runnable r) {
scheduler.createTaskBuilder().execute(r).submit(LPSpongePlugin.this);
}
}
@@ -0,0 +1,74 @@
package me.lucko.luckperms;
import me.lucko.luckperms.commands.CommandManager;
import me.lucko.luckperms.commands.Sender;
import me.lucko.luckperms.utils.Patterns;
import org.spongepowered.api.command.CommandCallable;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.serializer.TextSerializers;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class SpongeCommand extends CommandManager implements CommandCallable {
public SpongeCommand(LuckPermsPlugin plugin) {
super(plugin);
}
@Override
public CommandResult process(CommandSource source, String s) throws CommandException {
onCommand(makeSender(source), "perms", Arrays.asList(Patterns.SPACE_SPLIT.split(s)));
return CommandResult.success();
}
@Override
public List<String> getSuggestions(CommandSource source, String s) throws CommandException {
return onTabComplete(makeSender(source), Arrays.asList(Patterns.SPACE_SPLIT.split(s)));
}
@Override
public boolean testPermission(CommandSource commandSource) {
return true;
}
@Override
public Optional<? extends Text> getShortDescription(CommandSource commandSource) {
return Optional.of(Text.of("LuckPerms main command."));
}
@Override
public Optional<? extends Text> getHelp(CommandSource commandSource) {
return Optional.of(Text.of("Type /perms for help."));
}
@Override
public Text getUsage(CommandSource commandSource) {
return Text.of("/perms");
}
private static Sender makeSender(CommandSource source) {
return new Sender() {
final WeakReference<CommandSource> cs = new WeakReference<>(source);
@SuppressWarnings("deprecation")
@Override
public void sendMessage(String s) {
final CommandSource c = cs.get();
if (c != null) {
c.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(s));
}
}
@Override
public boolean hasPermission(String node) {
final CommandSource c = cs.get();
return c != null && c.hasPermission(node);
}
};
}
}
@@ -0,0 +1,79 @@
package me.lucko.luckperms;
import me.lucko.luckperms.utils.LPConfiguration;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
import ninja.leaping.configurate.loader.ConfigurationLoader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
class SpongeConfig extends LPConfiguration<LPSpongePlugin> {
private ConfigurationNode root;
SpongeConfig(LPSpongePlugin plugin) {
super(plugin, "global", true, "sqlite");
}
@SuppressWarnings("ResultOfMethodCallIgnored")
private Path makeFile(Path file) throws IOException {
File cfg = file.toFile();
cfg.getParentFile().mkdirs();
if (!cfg.exists()) {
try (InputStream is = getPlugin().getClass().getClassLoader().getResourceAsStream("luckperms.conf")) {
Files.copy(is, cfg.toPath());
}
}
return cfg.toPath();
}
@Override
protected void init() {
try {
ConfigurationLoader<CommentedConfigurationNode> loader = HoconConfigurationLoader.builder()
.setPath(makeFile(getPlugin().getConfigDir().resolve("luckperms.conf")))
.build();
root = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
private ConfigurationNode getNode(String path) {
String[] paths = path.split("\\.");
ConfigurationNode node = root;
for (String s : paths) {
node = node.getNode(s);
}
return node;
}
@Override
protected void set(String path, Object value) {
getNode(path).setValue(value);
}
@Override
protected String getString(String path, String def) {
return getNode(path).getString(def);
}
@Override
protected int getInt(String path, int def) {
return getNode(path).getInt(def);
}
@Override
protected boolean getBoolean(String path, boolean def) {
return getNode(path).getBoolean(def);
}
}
@@ -0,0 +1,107 @@
package me.lucko.luckperms.listeners;
import lombok.AllArgsConstructor;
import me.lucko.luckperms.LPSpongePlugin;
import me.lucko.luckperms.commands.Util;
import me.lucko.luckperms.constants.Message;
import me.lucko.luckperms.users.User;
import me.lucko.luckperms.utils.UuidCache;
import org.spongepowered.api.entity.Entity;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Listener;
import org.spongepowered.api.event.entity.DisplaceEntityEvent;
import org.spongepowered.api.event.network.ClientConnectionEvent;
import org.spongepowered.api.profile.GameProfile;
import org.spongepowered.api.text.serializer.TextSerializers;
import java.util.UUID;
@AllArgsConstructor
public class PlayerListener {
private static final String KICK_MESSAGE = Util.color(Message.PREFIX + "User data could not be loaded. Please contact an administrator.");
private final LPSpongePlugin plugin;
@Listener
public void onClientAuth(ClientConnectionEvent.Auth e) {
final long startTime = System.currentTimeMillis();
if (!plugin.getDatastore().isAcceptingLogins()) {
// Datastore is disabled, prevent players from joining the server
// Just don't load their data, they will be kickec at login
return;
}
final UuidCache cache = plugin.getUuidCache();
final GameProfile p = e.getProfile();
final String name = p.getName().get();
if (!cache.isOnlineMode()) {
UUID uuid = plugin.getDatastore().getUUID(name);
if (uuid != null) {
cache.addToCache(p.getUniqueId(), uuid);
} else {
// No previous data for this player
cache.addToCache(p.getUniqueId(), p.getUniqueId());
plugin.getDatastore().saveUUIDData(name, p.getUniqueId(), b -> {});
}
} else {
// Online mode, no cache needed. This is just for name -> uuid lookup.
plugin.getDatastore().saveUUIDData(name, p.getUniqueId(), b -> {});
}
plugin.getDatastore().loadOrCreateUser(cache.getUUID(p.getUniqueId()), name);
final long time = System.currentTimeMillis() - startTime;
if (time >= 1000) {
plugin.getLog().warn("Processing login for " + p.getName() + " took " + time + "ms.");
}
}
@SuppressWarnings("deprecation")
@Listener
public void onClientLogin(ClientConnectionEvent.Login e) {
final GameProfile player = e.getProfile();
final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(player.getUniqueId()));
if (user == null) {
e.setCancelled(true);
e.setMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(KICK_MESSAGE));
return;
}
user.refreshPermissions();
}
@Listener
public void onClientJoin(ClientConnectionEvent.Join e) {
// Refresh permissions again
refreshPlayer(e.getTargetEntity());
}
@Listener
public void onPlayerTeleport(DisplaceEntityEvent.Teleport e) {
final Entity entity = e.getTargetEntity();
if (!(entity instanceof Player)){
return;
}
refreshPlayer((Player) entity);
}
@Listener
public void onClientLeave(ClientConnectionEvent.Disconnect e) {
final Player player = e.getTargetEntity();
final UuidCache cache = plugin.getUuidCache();
// Unload the user from memory when they disconnect;
cache.clearCache(player.getUniqueId());
final User user = plugin.getUserManager().getUser(cache.getUUID(player.getUniqueId()));
plugin.getUserManager().unloadUser(user);
}
private void refreshPlayer(Player p) {
final User user = plugin.getUserManager().getUser(plugin.getUuidCache().getUUID(p.getUniqueId()));
if (user != null) {
user.refreshPermissions();
}
}
}
@@ -0,0 +1,41 @@
package me.lucko.luckperms.users;
import me.lucko.luckperms.LPSpongePlugin;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.util.Tristate;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
class SpongeUser extends User {
private final LPSpongePlugin plugin;
SpongeUser(UUID uuid, LPSpongePlugin plugin) {
super(uuid, plugin);
this.plugin = plugin;
}
SpongeUser(UUID uuid, String username, LPSpongePlugin plugin) {
super(uuid, username, plugin);
this.plugin = plugin;
}
@Override
public void refreshPermissions() {
Optional<Player> p = plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(getUuid()));
if (!p.isPresent()) return;
final Player player = p.get();
// Clear existing permissions
player.getSubjectData().clearParents();
player.getSubjectData().clearPermissions();
// Re-add all defined permissions for the user
final String world = player.getWorld().getName();
Map<String, Boolean> local = getLocalPermissions(getPlugin().getConfiguration().getServer(), world, null);
local.entrySet().forEach(e -> player.getSubjectData().setPermission(new HashSet<>(), e.getKey(), Tristate.fromBoolean(e.getValue())));
}
}
@@ -0,0 +1,52 @@
package me.lucko.luckperms.users;
import me.lucko.luckperms.LPSpongePlugin;
import org.spongepowered.api.entity.living.player.Player;
import java.util.Optional;
import java.util.UUID;
public class SpongeUserManager extends UserManager {
private final LPSpongePlugin plugin;
public SpongeUserManager(LPSpongePlugin plugin) {
super(plugin);
this.plugin = plugin;
}
@Override
public void unloadUser(User user) {
if (user != null) {
Optional<Player> p = plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid()));
if (p.isPresent()) {
p.get().getSubjectData().clearParents();
p.get().getSubjectData().clearPermissions();
}
getUsers().remove(user.getUuid());
}
}
@Override
public void cleanupUser(User user) {
if (plugin.getGame().getServer().getPlayer(plugin.getUuidCache().getExternalUUID(user.getUuid())).isPresent()) {
unloadUser(user);
}
}
@Override
public User makeUser(UUID uuid) {
return new SpongeUser(uuid, plugin);
}
@Override
public User makeUser(UUID uuid, String username) {
return new SpongeUser(uuid, username, plugin);
}
@Override
public void updateAllUsers() {
plugin.getGame().getServer().getOnlinePlayers().stream()
.map(p -> plugin.getUuidCache().getUUID(p.getUniqueId()))
.forEach(u -> plugin.getDatastore().loadUser(u));
}
}