diff --git a/Assembly/dependency-reduced-pom.xml b/Assembly/dependency-reduced-pom.xml index ccd0cfd..976a05c 100644 --- a/Assembly/dependency-reduced-pom.xml +++ b/Assembly/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ AntiVPN dev.brighten.antivpn - 1.4.0 + 1.5.0 4.0.0 Assembly diff --git a/Assembly/pom.xml b/Assembly/pom.xml index 03f5fef..cda33af 100644 --- a/Assembly/pom.xml +++ b/Assembly/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.4.0 + 1.5.0 4.0.0 @@ -50,6 +50,12 @@ ${version} compile + + dev.brighten.antivpn + Velocity + ${version} + compile + dev.brighten.antivpn Bukkit diff --git a/Bukkit/dependency-reduced-pom.xml b/Bukkit/dependency-reduced-pom.xml index 3e7b35e..27bad2f 100644 --- a/Bukkit/dependency-reduced-pom.xml +++ b/Bukkit/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ AntiVPN dev.brighten.antivpn - 1.4.0 + 1.5.0 4.0.0 Bukkit @@ -56,7 +56,7 @@ dev.brighten.antivpn Common - 1.4.0 + 1.5.0 provided diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index c006c24..e05f8e1 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.4.0 + 1.5.0 4.0.0 @@ -69,7 +69,7 @@ dev.brighten.antivpn Common - 1.4.0 + 1.5.0 provided diff --git a/Bungee/dependency-reduced-pom.xml b/Bungee/dependency-reduced-pom.xml index 4017f08..a3e5d91 100644 --- a/Bungee/dependency-reduced-pom.xml +++ b/Bungee/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ AntiVPN dev.brighten.antivpn - 1.4.0 + 1.5.0 4.0.0 Bungee @@ -50,7 +50,7 @@ dev.brighten.antivpn Common - 1.4.0 + 1.5.0 provided diff --git a/Bungee/pom.xml b/Bungee/pom.xml index f483674..5bf654b 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.4.0 + 1.5.0 4.0.0 @@ -63,7 +63,7 @@ dev.brighten.antivpn Common - 1.4.0 + 1.5.0 provided diff --git a/Common/pom.xml b/Common/pom.xml index a8482ee..b0e1abb 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.4.0 + 1.5.0 4.0.0 diff --git a/Velocity/pom.xml b/Velocity/pom.xml new file mode 100644 index 0000000..4b1ef2b --- /dev/null +++ b/Velocity/pom.xml @@ -0,0 +1,62 @@ + + + + AntiVPN + dev.brighten.antivpn + 1.5.0 + + 4.0.0 + + Velocity + + + 8 + 8 + + + + + velocity + https://nexus.velocitypowered.com/repository/maven-public/ + + + + + + com.velocitypowered + velocity-api + 3.0.1 + provided + + + dev.brighten.antivpn + Common + 1.5.0 + provided + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 8 + 8 + -XDignore.symbol.file + + + + + + src/main/resources + true + + + + + \ No newline at end of file diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityCommandExecutor.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityCommandExecutor.java new file mode 100644 index 0000000..b1ee9bd --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityCommandExecutor.java @@ -0,0 +1,39 @@ +package dev.brighten.antivpn.velocity; + +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.command.CommandExecutor; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.util.Optional; + +@RequiredArgsConstructor +public class VelocityCommandExecutor implements CommandExecutor { + + private final CommandSource sender; + + @Override + public void sendMessage(String message) { + sender.sendMessage(LegacyComponentSerializer.builder().character('&').build().deserialize(message)); + } + + @Override + public boolean hasPermission(String permission) { + return sender.hasPermission(permission); + } + + @Override + public Optional getPlayer() { + if(!isPlayer()) return Optional.empty(); + + return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((Player) sender).getUniqueId()); + } + + @Override + public boolean isPlayer() { + return sender instanceof Player; + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityConfig.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityConfig.java new file mode 100644 index 0000000..55b11fe --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityConfig.java @@ -0,0 +1,166 @@ +package dev.brighten.antivpn.velocity; + +import dev.brighten.antivpn.api.VPNConfig; +import dev.brighten.antivpn.velocity.util.ConfigDefault; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class VelocityConfig implements VPNConfig { + private final ConfigDefault licenseDefault = new ConfigDefault<>("", + "license", VelocityPlugin.INSTANCE), kickStringDefault = + new ConfigDefault<>("Proxies are not allowed on our server", + "kickMessage", VelocityPlugin.INSTANCE), + defaultDatabaseType = new ConfigDefault<>("MySQL", + "database.type", VelocityPlugin.INSTANCE), + defaultDatabaseName = new ConfigDefault<>("kaurivpn", + "database.database", VelocityPlugin.INSTANCE), + defaultUsername = new ConfigDefault<>("root", + "database.username", VelocityPlugin.INSTANCE), + defaultPassword = new ConfigDefault<>("password", + "database.password", VelocityPlugin.INSTANCE), + defaultAuthDatabase = new ConfigDefault<>("admin", + "database.auth", VelocityPlugin.INSTANCE), + defaultIp = new ConfigDefault<>("localhost", "database.ip", VelocityPlugin.INSTANCE), + defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" + + " &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message", + VelocityPlugin.INSTANCE); + private final ConfigDefault cacheResultsDefault = new ConfigDefault<>(true, + "cachedResults", VelocityPlugin.INSTANCE), + defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled", + VelocityPlugin.INSTANCE), defaultCommandsEnable = new ConfigDefault<>(false, + "commands.enabled", VelocityPlugin.INSTANCE), defaultKickPlayers + = new ConfigDefault<>(true, "kickPlayers", VelocityPlugin.INSTANCE), + defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled", + VelocityPlugin.INSTANCE), + defaultMetrics = new ConfigDefault<>(true, "bstats", VelocityPlugin.INSTANCE); + private final ConfigDefault + defaultPort = new ConfigDefault<>(-1, "database.port", VelocityPlugin.INSTANCE); + private final ConfigDefault> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(), + "prefixWhitelists", VelocityPlugin.INSTANCE), defaultCommands = new ConfigDefault<>( + Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute", + VelocityPlugin.INSTANCE); + + private String license, kickMessage, databaseType, databaseName, username, password, ip, alertMsg; + private List prefixWhitelists, commands; + private int port; + private boolean cacheResults, databaseEnabled, commandsEnabled, kickPlayers, alertToStaff, metrics; + + @Override + public String getLicense() { + return license; + } + + @Override + public boolean cachedResults() { + return cacheResults; + } + + @Override + public String getKickString() { + return kickMessage; + } + + @Override + public String alertMessage() { + return alertMsg; + } + + @Override + public boolean alertToStaff() { + return alertToStaff; + } + + @Override + public boolean runCommands() { + return commandsEnabled; + } + + @Override + public List commands() { + return commands; + } + + @Override + public boolean kickPlayersOnDetect() { + return kickPlayers; + } + + @Override + public List getPrefixWhitelists() { + return prefixWhitelists; + } + + @Override + public boolean isDatabaseEnabled() { + return databaseEnabled; + } + + @Override + public String getDatabaseType() { + return databaseType; + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getIp() { + return ip; + } + + @Override + public int getPort() { + if(port == -1) { + switch (getDatabaseType().toLowerCase()) { + case "mongodb": + case "mongo": + case "mongod": + return 27017; + case "sql": + case "mysql": + return 3306; + } + } + + return port; + } + + @Override + public boolean metrics() { + return metrics; + } + + public void update() { + license = licenseDefault.get(); + kickMessage = kickStringDefault.get(); + cacheResults = cacheResultsDefault.get(); + prefixWhitelists = prefixWhitelistsDefault.get(); + databaseEnabled = defaultDatabaseEnabled.get(); + databaseType = defaultDatabaseType.get(); + databaseName = defaultDatabaseName.get(); + username = defaultUsername.get(); + password = defaultPassword.get(); + ip = defaultIp.get(); + port = defaultPort.get(); + commandsEnabled = defaultCommandsEnable.get(); + commands = defaultCommands.get(); + kickPlayers = defaultKickPlayers.get(); + alertToStaff = defaultAlertToStaff.get(); + alertMsg = defaultAlertMsg.get(); + metrics = defaultMetrics.get(); + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java new file mode 100644 index 0000000..c4e584a --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java @@ -0,0 +1,91 @@ +package dev.brighten.antivpn.velocity; + +import com.velocitypowered.api.event.connection.LoginEvent; +import com.velocitypowered.api.scheduler.ScheduledTask; +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.api.VPNExecutor; +import dev.brighten.antivpn.velocity.util.StringUtils; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +public class VelocityListener extends VPNExecutor { + + private ScheduledTask cacheResetTask; + + @Override + public void registerListeners() { + VelocityPlugin.INSTANCE.getServer().getEventManager() + .register(VelocityPlugin.INSTANCE, this); + + VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class, + event -> { + if(event.getResult().isAllowed()) { + if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt + //Or has a name that starts with a certain prefix. This is for Bedrock exempting. + || AntiVPN.getInstance().getConfig().getPrefixWhitelists().stream() + .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return; + + checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(), + AntiVPN.getInstance().getConfig().cachedResults(), result -> { + if(result.isSuccess() && result.isProxy()) { + if(AntiVPN.getInstance().getConfig().kickPlayersOnDetect()) + event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&') + .build().deserialize(AntiVPN.getInstance().getConfig().getKickString())); + VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() + + " joined on a VPN/Proxy (" + result.getMethod() + ")"); + + if(AntiVPN.getInstance().getConfig().alertToStaff()) //Ensuring the user wishes to alert to staff + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getConfig().alertMessage() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%reason%", result.getMethod()) + .replace("%country%", result.getCountryName()) + .replace("%city%", result.getCity()))); + + //In case the user wants to run their own commands instead of using the built in kicking + if(AntiVPN.getInstance().getConfig().runCommands()) { + for (String command : AntiVPN.getInstance().getConfig().commands()) { + VelocityPlugin.INSTANCE.getServer().getCommandManager() + .executeAsync(VelocityPlugin.INSTANCE.getServer() + .getConsoleCommandSource(), + StringUtils.translateAlternateColorCodes('&', + command.replace("%player%", + event.getPlayer().getUsername()))); + } + } + AntiVPN.getInstance().detections++; + } else if(!result.isSuccess()) { + VelocityPlugin.INSTANCE.getLogger() + .log(Level.WARNING, + "The API query was not a success! " + + "You may need to upgrade your license on https://funkemunky.cc/shop"); + } + AntiVPN.getInstance().checked++; + }); + } + }); + } + + @Override + public void runCacheReset() { + cacheResetTask = VelocityPlugin.INSTANCE.getServer().getScheduler() + .buildTask(VelocityPlugin.INSTANCE, this::resetCache) + .repeat(20, TimeUnit.MINUTES) + .schedule(); + } + + @Override + public void shutdown() { + if(cacheResetTask != null) { + cacheResetTask.cancel(); + cacheResetTask = null; + } + threadExecutor.shutdown(); + VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this); + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayer.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayer.java new file mode 100644 index 0000000..b247714 --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayer.java @@ -0,0 +1,31 @@ +package dev.brighten.antivpn.velocity; + +import com.velocitypowered.api.proxy.Player; +import dev.brighten.antivpn.api.APIPlayer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +public class VelocityPlayer extends APIPlayer { + + private final Player player; + public VelocityPlayer(Player player) { + super(player.getUniqueId(), player.getUsername(), player.getRemoteAddress().getAddress()); + + this.player = player; + } + + + @Override + public void sendMessage(String message) { + player.sendMessage(LegacyComponentSerializer.builder().character('&').build().deserialize(message)); + } + + @Override + public void kickPlayer(String reason) { + player.disconnect(LegacyComponentSerializer.builder().character('&').build().deserialize(reason)); + } + + @Override + public boolean hasPermission(String permission) { + return player.hasPermission(permission); + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java new file mode 100644 index 0000000..c7edcb4 --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java @@ -0,0 +1,35 @@ +package dev.brighten.antivpn.velocity; + +import com.velocitypowered.api.proxy.Player; +import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.api.PlayerExecutor; + +import java.util.*; +import java.util.stream.Collectors; + +public class VelocityPlayerExecutor implements PlayerExecutor { + + private final Map cachedPlayers = new WeakHashMap<>(); + + @Override + public Optional getPlayer(String name) { + Optional player = VelocityPlugin.INSTANCE.getServer().getPlayer(name); + + return player.map(value -> cachedPlayers.computeIfAbsent(value, VelocityPlayer::new)); + + } + + @Override + public Optional getPlayer(UUID uuid) { + Optional player = VelocityPlugin.INSTANCE.getServer().getPlayer(uuid); + + return player.map(value -> cachedPlayers.computeIfAbsent(value, VelocityPlayer::new)); + } + + @Override + public List getOnlinePlayers() { + return VelocityPlugin.INSTANCE.getServer().getAllPlayers().stream() + .map(pl -> cachedPlayers.computeIfAbsent(pl, VelocityPlayer::new)) + .collect(Collectors.toList()); + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java new file mode 100644 index 0000000..fc941dd --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java @@ -0,0 +1,93 @@ +package dev.brighten.antivpn.velocity; + +import com.google.inject.Inject; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.command.SimpleCommand; +import com.velocitypowered.api.event.Subscribe; +import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.plugin.Plugin; +import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.ProxyServer; +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.command.Command; +import dev.brighten.antivpn.velocity.util.Config; +import lombok.Getter; +import lombok.val; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.logging.Logger; +import java.util.stream.IntStream; + +@Getter +@Plugin(id = "kaurivpn", name = "KauriVPN", version = "${project.version}", authors = {"funkemunky"}) +public class VelocityPlugin { + + private final ProxyServer server; + private final Logger logger; + public static VelocityPlugin INSTANCE; + + @Inject + @DataDirectory + private Path configDir; + + private Config config; + + @Inject + public VelocityPlugin(ProxyServer server, Logger logger) { + this.server = server; + this.logger = logger; + } + + @Subscribe + public void onInit(ProxyInitializeEvent event) { + INSTANCE = this; + logger.info("Loading config..."); + config = new Config(); + + //Loading plugin + logger.info("Starting AntiVPN services..."); + AntiVPN.start(new VelocityConfig(), new VelocityListener(), new VelocityPlayerExecutor()); + + for (Command command : AntiVPN.getInstance().getCommands()) { + server.getCommandManager().register(server.getCommandManager().metaBuilder(command.name()) + .aliases(command.aliases()).build(), (SimpleCommand) invocation -> { + CommandSource sender = invocation.source(); + if(!invocation.source().hasPermission("antivpn.command.*") + && !invocation.source().hasPermission(command.permission())) { + invocation.source().sendMessage(Component.text("No permission").toBuilder() + .color(TextColor.color(255,0,0)).build()); + return; + } + + val children = command.children(); + + String[] args = invocation.arguments(); + if(children.length > 0 && args.length > 0) { + for (Command child : children) { + if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases()) + .anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) { + if(!sender.hasPermission("antivpn.command.*") + && !sender.hasPermission(child.permission())) { + invocation.source().sendMessage(Component.text("No permission") + .toBuilder().color(TextColor.color(255,0,0)).build()); + return; + } + sender.sendMessage(LegacyComponentSerializer.builder().character('&').build() + .deserialize(child.execute(new VelocityCommandExecutor(sender), IntStream + .range(0, args.length - 1) + .mapToObj(i -> args[i + 1]).toArray(String[]::new)))); + return; + } + } + } + + sender.sendMessage(LegacyComponentSerializer.builder().character('&').build() + .deserialize(command.execute(new VelocityCommandExecutor(sender), args))); + }); + } + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/Configuration.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/Configuration.java new file mode 100644 index 0000000..f576f9b --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/Configuration.java @@ -0,0 +1,414 @@ +package dev.brighten.antivpn.velocity.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +public final class Configuration +{ + + private static final char SEPARATOR = '.'; + final Map self; + private final Configuration defaults; + + public Configuration() + { + this( null ); + } + + public Configuration(Configuration defaults) + { + this( new LinkedHashMap(), defaults ); + } + + Configuration(Map map, Configuration defaults) + { + this.self = new LinkedHashMap<>(); + this.defaults = defaults; + + for ( Map.Entry entry : map.entrySet() ) + { + String key = ( entry.getKey() == null ) ? "null" : entry.getKey().toString(); + + if ( entry.getValue() instanceof Map ) + { + this.self.put( key, new Configuration( (Map) entry.getValue(), ( defaults == null ) ? null : defaults.getSection( key ) ) ); + } else + { + this.self.put( key, entry.getValue() ); + } + } + } + + private Configuration getSectionFor(String path) + { + int index = path.indexOf( SEPARATOR ); + if ( index == -1 ) + { + return this; + } + + String root = path.substring( 0, index ); + Object section = self.get( root ); + if ( section == null ) + { + section = new Configuration( ( defaults == null ) ? null : defaults.getSection( root ) ); + self.put( root, section ); + } + + return (Configuration) section; + } + + private String getChild(String path) + { + int index = path.indexOf( SEPARATOR ); + return ( index == -1 ) ? path : path.substring( index + 1 ); + } + + /*------------------------------------------------------------------------*/ + @SuppressWarnings("unchecked") + public T get(String path, T def) + { + Configuration section = getSectionFor( path ); + Object val; + if ( section == this ) + { + val = self.get( path ); + } else + { + val = section.get( getChild( path ), def ); + } + + if ( val == null && def instanceof Configuration ) + { + self.put( path, def ); + } + + return ( val != null ) ? (T) val : def; + } + + public boolean contains(String path) + { + return get( path, null ) != null; + } + + public Object get(String path) + { + return get( path, getDefault( path ) ); + } + + public Object getDefault(String path) + { + return ( defaults == null ) ? null : defaults.get( path ); + } + + public void set(String path, Object value) + { + if ( value instanceof Map ) + { + value = new Configuration( (Map) value, ( defaults == null ) ? null : defaults.getSection( path ) ); + } + + Configuration section = getSectionFor( path ); + if ( section == this ) + { + if ( value == null ) + { + self.remove( path ); + } else + { + self.put( path, value ); + } + } else + { + section.set( getChild( path ), value ); + } + } + + /*------------------------------------------------------------------------*/ + public Configuration getSection(String path) + { + Object def = getDefault( path ); + return (Configuration) get( path, ( def instanceof Configuration ) ? def : new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) ) ); + } + + /** + * Gets keys, not deep by default. + * + * @return top level keys for this section + */ + public Collection getKeys() + { + return new LinkedHashSet<>( self.keySet() ); + } + + /*------------------------------------------------------------------------*/ + public byte getByte(String path) + { + Object def = getDefault( path ); + return getByte( path, ( def instanceof Number ) ? ( (Number) def ).byteValue() : 0 ); + } + + public byte getByte(String path, byte def) + { + Object val = get( path, def ); + return ( val instanceof Number ) ? ( (Number) val ).byteValue() : def; + } + + public List getByteList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Number ) + { + result.add( ( (Number) object ).byteValue() ); + } + } + + return result; + } + + public short getShort(String path) + { + Object def = getDefault( path ); + return getShort( path, ( def instanceof Number ) ? ( (Number) def ).shortValue() : 0 ); + } + + public short getShort(String path, short def) + { + Object val = get( path, def ); + return ( val instanceof Number ) ? ( (Number) val ).shortValue() : def; + } + + public List getShortList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Number ) + { + result.add( ( (Number) object ).shortValue() ); + } + } + + return result; + } + + public int getInt(String path) + { + Object def = getDefault( path ); + return getInt( path, ( def instanceof Number ) ? ( (Number) def ).intValue() : 0 ); + } + + public int getInt(String path, int def) + { + Object val = get( path, def ); + return ( val instanceof Number ) ? ( (Number) val ).intValue() : def; + } + + public List getIntList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Number ) + { + result.add( ( (Number) object ).intValue() ); + } + } + + return result; + } + + public long getLong(String path) + { + Object def = getDefault( path ); + return getLong( path, ( def instanceof Number ) ? ( (Number) def ).longValue() : 0 ); + } + + public long getLong(String path, long def) + { + Object val = get( path, def ); + return ( val instanceof Number ) ? ( (Number) val ).longValue() : def; + } + + public List getLongList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Number ) + { + result.add( ( (Number) object ).longValue() ); + } + } + + return result; + } + + public float getFloat(String path) + { + Object def = getDefault( path ); + return getFloat( path, ( def instanceof Number ) ? ( (Number) def ).floatValue() : 0 ); + } + + public float getFloat(String path, float def) + { + Object val = get( path, def ); + return ( val instanceof Number ) ? ( (Number) val ).floatValue() : def; + } + + public List getFloatList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Number ) + { + result.add( ( (Number) object ).floatValue() ); + } + } + + return result; + } + + public double getDouble(String path) + { + Object def = getDefault( path ); + return getDouble( path, ( def instanceof Number ) ? ( (Number) def ).doubleValue() : 0 ); + } + + public double getDouble(String path, double def) + { + Object val = get( path, def ); + return ( val instanceof Number ) ? ( (Number) val ).doubleValue() : def; + } + + public List getDoubleList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Number ) + { + result.add( ( (Number) object ).doubleValue() ); + } + } + + return result; + } + + public boolean getBoolean(String path) + { + Object def = getDefault( path ); + return getBoolean( path, ( def instanceof Boolean ) ? (Boolean) def : false ); + } + + public boolean getBoolean(String path, boolean def) + { + Object val = get( path, def ); + return ( val instanceof Boolean ) ? (Boolean) val : def; + } + + public List getBooleanList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Boolean ) + { + result.add( (Boolean) object ); + } + } + + return result; + } + + public char getChar(String path) + { + Object def = getDefault( path ); + return getChar( path, ( def instanceof Character ) ? (Character) def : '\u0000' ); + } + + public char getChar(String path, char def) + { + Object val = get( path, def ); + return ( val instanceof Character ) ? (Character) val : def; + } + + public List getCharList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof Character ) + { + result.add( (Character) object ); + } + } + + return result; + } + + public String getString(String path) + { + Object def = getDefault( path ); + return getString( path, ( def instanceof String ) ? (String) def : "" ); + } + + public String getString(String path, String def) + { + Object val = get( path, def ); + return ( val instanceof String ) ? (String) val : def; + } + + public List getStringList(String path) + { + List list = getList( path ); + List result = new ArrayList<>(); + + for ( Object object : list ) + { + if ( object instanceof String ) + { + result.add( (String) object ); + } + } + + return result; + } + + /*------------------------------------------------------------------------*/ + public List getList(String path) + { + Object def = getDefault( path ); + return getList( path, ( def instanceof List ) ? (List) def : Collections.EMPTY_LIST ); + } + + public List getList(String path, List def) + { + Object val = get( path, def ); + return ( val instanceof List ) ? (List) val : def; + } +} \ No newline at end of file diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/ConfigurationProvider.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/ConfigurationProvider.java new file mode 100644 index 0000000..cf5189e --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/ConfigurationProvider.java @@ -0,0 +1,60 @@ +package dev.brighten.antivpn.velocity.config; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; + +public abstract class ConfigurationProvider +{ + + private static final Map, ConfigurationProvider> providers = new HashMap<>(); + + static + { + try + { + providers.put( YamlConfiguration.class, new YamlConfiguration() ); + } catch ( NoClassDefFoundError ex ) + { + // Ignore, no SnakeYAML + } + + try + { + providers.put( JsonConfiguration.class, new JsonConfiguration() ); + } catch ( NoClassDefFoundError ex ) + { + // Ignore, no Gson + } + } + + public static ConfigurationProvider getProvider(Class provider) + { + return providers.get( provider ); + } + + /*------------------------------------------------------------------------*/ + public abstract void save(Configuration config, File file) throws IOException; + + public abstract void save(Configuration config, Writer writer); + + public abstract Configuration load(File file) throws IOException; + + public abstract Configuration load(File file, Configuration defaults) throws IOException; + + public abstract Configuration load(Reader reader); + + public abstract Configuration load(Reader reader, Configuration defaults); + + public abstract Configuration load(InputStream is); + + public abstract Configuration load(InputStream is, Configuration defaults); + + public abstract Configuration load(String string); + + public abstract Configuration load(String string, Configuration defaults); +} \ No newline at end of file diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/JsonConfiguration.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/JsonConfiguration.java new file mode 100644 index 0000000..83ea436 --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/JsonConfiguration.java @@ -0,0 +1,114 @@ +package dev.brighten.antivpn.velocity.config; + +import com.google.common.base.Charsets; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Type; +import java.util.LinkedHashMap; +import java.util.Map; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +public class JsonConfiguration extends ConfigurationProvider +{ + + private final Gson json = new GsonBuilder().serializeNulls().setPrettyPrinting().registerTypeAdapter( Configuration.class, new JsonSerializer() + { + @Override + public JsonElement serialize(Configuration src, Type typeOfSrc, JsonSerializationContext context) + { + return context.serialize( ( (Configuration) src ).self ); + } + } ).create(); + + @Override + public void save(Configuration config, File file) throws IOException + { + try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), Charsets.UTF_8 ) ) + { + save( config, writer ); + } + } + + @Override + public void save(Configuration config, Writer writer) + { + json.toJson( config.self, writer ); + } + + @Override + public Configuration load(File file) throws IOException + { + return load( file, null ); + } + + @Override + public Configuration load(File file, Configuration defaults) throws IOException + { + try ( FileInputStream is = new FileInputStream( file ) ) + { + return load( is, defaults ); + } + } + + @Override + public Configuration load(Reader reader) + { + return load( reader, null ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(Reader reader, Configuration defaults) + { + Map map = json.fromJson( reader, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } + + @Override + public Configuration load(InputStream is) + { + return load( is, null ); + } + + @Override + public Configuration load(InputStream is, Configuration defaults) + { + return load( new InputStreamReader( is, Charsets.UTF_8 ), defaults ); + } + + @Override + public Configuration load(String string) + { + return load( string, null ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(String string, Configuration defaults) + { + Map map = json.fromJson( string, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } +} \ No newline at end of file diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/YamlConfiguration.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/YamlConfiguration.java new file mode 100644 index 0000000..b69584a --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/config/YamlConfiguration.java @@ -0,0 +1,136 @@ +package dev.brighten.antivpn.velocity.config; + +import com.google.common.base.Charsets; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.LinkedHashMap; +import java.util.Map; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.representer.Represent; +import org.yaml.snakeyaml.representer.Representer; + +@NoArgsConstructor(access = AccessLevel.PACKAGE) +public class YamlConfiguration extends ConfigurationProvider +{ + + private final ThreadLocal yaml = new ThreadLocal() + { + @Override + protected Yaml initialValue() + { + Representer representer = new Representer() + { + { + representers.put( Configuration.class, new Represent() + { + @Override + public Node representData(Object data) + { + return represent( ( (Configuration) data ).self ); + } + } ); + } + }; + + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK ); + + return new Yaml( new Constructor(), representer, options ); + } + }; + + @Override + public void save(Configuration config, File file) throws IOException + { + try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), Charsets.UTF_8 ) ) + { + save( config, writer ); + } + } + + @Override + public void save(Configuration config, Writer writer) + { + yaml.get().dump( config.self, writer ); + } + + @Override + public Configuration load(File file) throws IOException + { + return load( file, null ); + } + + @Override + public Configuration load(File file, Configuration defaults) throws IOException + { + try ( FileInputStream is = new FileInputStream( file ) ) + { + return load( is, defaults ); + } + } + + @Override + public Configuration load(Reader reader) + { + return load( reader, null ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(Reader reader, Configuration defaults) + { + Map map = yaml.get().loadAs( reader, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } + + @Override + public Configuration load(InputStream is) + { + return load( is, null ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(InputStream is, Configuration defaults) + { + Map map = yaml.get().loadAs( is, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } + + @Override + public Configuration load(String string) + { + return load( string, null ); + } + + @Override + @SuppressWarnings("unchecked") + public Configuration load(String string, Configuration defaults) + { + Map map = yaml.get().loadAs( string, LinkedHashMap.class ); + if ( map == null ) + { + map = new LinkedHashMap<>(); + } + return new Configuration( map, defaults ); + } +} \ No newline at end of file diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/Config.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/Config.java new file mode 100644 index 0000000..39fa261 --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/Config.java @@ -0,0 +1,115 @@ +package dev.brighten.antivpn.velocity.util; + +import com.google.common.io.ByteStreams; +import dev.brighten.antivpn.velocity.VelocityPlugin; +import dev.brighten.antivpn.velocity.config.Configuration; +import dev.brighten.antivpn.velocity.config.ConfigurationProvider; +import dev.brighten.antivpn.velocity.config.YamlConfiguration; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Author: nitramleo (Martin) + * Date created: 10-Aug-18 + */ +public class Config { + + private File file; + private Configuration configuration; + + public Config() { + File dataFolder = VelocityPlugin.INSTANCE.getConfigDir().toFile(); + this.file = new File(dataFolder, "config.yml"); + try { + if (!this.file.exists()) { + if (!dataFolder.exists()) { + dataFolder.mkdir(); + } + this.file.createNewFile(); + try (final InputStream is =VelocityPlugin.INSTANCE.getClass().getResourceAsStream("config.yml"); + final OutputStream os = new FileOutputStream(this.file)) { + ByteStreams.copy(is, os); + } + } + this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + public void load() { + File dataFolder = VelocityPlugin.INSTANCE.getConfigDir().toFile(); + this.file = new File(dataFolder, "config.yml"); + try { + this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + public void save() { + try { + ConfigurationProvider.getProvider( YamlConfiguration.class).save(this.configuration, this.file); + } + catch (IOException e) { + e.printStackTrace(); + } + } + + public Configuration getConfiguration() { + return this.configuration; + } + + public File getFile() { + return this.file; + } + + public double getDouble(final String path) { + if (this.configuration.get(path) != null) { + return this.configuration.getDouble(path); + } + return 0.0; + } + + public int getInt(final String path) { + if (this.configuration.get(path) != null) { + return this.configuration.getInt(path); + } + return 0; + } + + public Object get(final String path) { + return this.configuration.get(path); + } + + public void set(final String path, final Object object) { + configuration.set(path, object); + } + + public boolean getBoolean(final String path) { + return this.configuration.get(path) != null && this.configuration.getBoolean(path); + } + + public String getString(final String path) { + if (this.configuration.get(path) != null) { + return StringUtils.translateAlternateColorCodes('&', this.configuration.getString(path)); + } + return "String at path: " + path + " not found!"; + } + + public List getStringList(final String path) { + if (this.configuration.get(path) != null) { + final ArrayList strings = new ArrayList(); + for (final String string : this.configuration.getStringList(path)) { + strings.add(StringUtils.translateAlternateColorCodes('&', string)); + } + return strings; + } + return Arrays.asList("String List at path: " + path + " not found!"); + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/ConfigDefault.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/ConfigDefault.java new file mode 100644 index 0000000..16c6006 --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/ConfigDefault.java @@ -0,0 +1,28 @@ +package dev.brighten.antivpn.velocity.util; + +import dev.brighten.antivpn.velocity.VelocityPlugin; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class ConfigDefault { + + private final A defaultValue; + private final String path; + private final VelocityPlugin plugin; + + public A get() { + if(plugin.getConfig().get(path) != null) + return (A) plugin.getConfig().get(path); + else { + plugin.getConfig().set(path, defaultValue); + plugin.getConfig().save(); + return defaultValue; + } + } + + public A set(A value) { + plugin.getConfig().set(path, value); + + return value; + } +} diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/StringUtils.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/StringUtils.java new file mode 100644 index 0000000..a4f02fe --- /dev/null +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/StringUtils.java @@ -0,0 +1,17 @@ +package dev.brighten.antivpn.velocity.util; + +public class StringUtils { + + public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { + char[] b = textToTranslate.toCharArray(); + + for(int i = 0; i < b.length - 1; ++i) { + if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) { + b[i] = 167; + b[i + 1] = Character.toLowerCase(b[i + 1]); + } + } + + return new String(b); + } +} diff --git a/pom.xml b/pom.xml index e4810a7..cfba9fa 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,14 @@ dev.brighten.antivpn AntiVPN pom - 1.4.0 + 1.5.0 Common Bungee Bukkit Assembly + Velocity