From 4f795220100d6c913a6bcd62023230ec6e0445cc Mon Sep 17 00:00:00 2001
From: Dawson <30784509+funkemunky@users.noreply.github.com>
Date: Fri, 19 Aug 2022 11:09:52 -0400
Subject: [PATCH] Fixing memory leak
---
Assembly/dependency-reduced-pom.xml | 2 +-
Assembly/pom.xml | 2 +-
Bukkit/dependency-reduced-pom.xml | 4 +-
Bukkit/pom.xml | 4 +-
.../antivpn/bukkit/BukkitListener.java | 6 +
.../antivpn/bukkit/BukkitPlayerExecutor.java | 7 +-
Bungee/dependency-reduced-pom.xml | 4 +-
Bungee/pom.xml | 4 +-
.../antivpn/bungee/BungeeListener.java | 6 +
.../antivpn/bungee/BungeePlayerExecutor.java | 13 +-
Common/pom.xml | 2 +-
.../brighten/antivpn/api/PlayerExecutor.java | 2 +
Velocity/pom.xml | 4 +-
.../antivpn/velocity/VelocityListener.java | 204 +++++++++---------
.../velocity/VelocityPlayerExecutor.java | 15 +-
pom.xml | 2 +-
16 files changed, 158 insertions(+), 123 deletions(-)
diff --git a/Assembly/dependency-reduced-pom.xml b/Assembly/dependency-reduced-pom.xml
index 9e49b30..187caa7 100644
--- a/Assembly/dependency-reduced-pom.xml
+++ b/Assembly/dependency-reduced-pom.xml
@@ -3,7 +3,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
Assembly
diff --git a/Assembly/pom.xml b/Assembly/pom.xml
index e06ea4d..e385fe3 100644
--- a/Assembly/pom.xml
+++ b/Assembly/pom.xml
@@ -5,7 +5,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
diff --git a/Bukkit/dependency-reduced-pom.xml b/Bukkit/dependency-reduced-pom.xml
index 9e87c11..9a52dbb 100644
--- a/Bukkit/dependency-reduced-pom.xml
+++ b/Bukkit/dependency-reduced-pom.xml
@@ -3,7 +3,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
Bukkit
@@ -56,7 +56,7 @@
dev.brighten.antivpn
Common
- 1.8
+ 1.8.1
provided
diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml
index 6a0561f..f437378 100644
--- a/Bukkit/pom.xml
+++ b/Bukkit/pom.xml
@@ -5,7 +5,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
@@ -69,7 +69,7 @@
dev.brighten.antivpn
Common
- 1.8
+ 1.8.1
provided
diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java
index cdc0193..ae9adc1 100644
--- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java
+++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java
@@ -12,6 +12,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
@@ -157,4 +158,9 @@ public class BukkitListener extends VPNExecutor implements Listener {
AntiVPN.getInstance().checked++;
});
}
+
+ @EventHandler
+ public void onQuit(PlayerQuitEvent event) {
+ AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
+ }
}
diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java
index 3efa096..faa1f19 100644
--- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java
+++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlayerExecutor.java
@@ -10,7 +10,7 @@ import java.util.stream.Collectors;
public class BukkitPlayerExecutor implements PlayerExecutor {
- private final Map cachedPlayers = new WeakHashMap<>();
+ private final Map cachedPlayers = new HashMap<>();
@Override
public Optional getPlayer(String name) {
@@ -34,6 +34,11 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
}
+ @Override
+ public void unloadPlayer(UUID uuid) {
+ cachedPlayers.remove(uuid);
+ }
+
@Override
public List getOnlinePlayers() {
diff --git a/Bungee/dependency-reduced-pom.xml b/Bungee/dependency-reduced-pom.xml
index 8df74b3..6e0518b 100644
--- a/Bungee/dependency-reduced-pom.xml
+++ b/Bungee/dependency-reduced-pom.xml
@@ -3,7 +3,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
Bungee
@@ -50,7 +50,7 @@
dev.brighten.antivpn
Common
- 1.8
+ 1.8.1
provided
diff --git a/Bungee/pom.xml b/Bungee/pom.xml
index 1919049..d2e1a58 100644
--- a/Bungee/pom.xml
+++ b/Bungee/pom.xml
@@ -5,7 +5,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
@@ -63,7 +63,7 @@
dev.brighten.antivpn
Common
- 1.8
+ 1.8.1
provided
diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java
index 4960888..82ef549 100644
--- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java
+++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java
@@ -6,6 +6,7 @@ import dev.brighten.antivpn.api.VPNExecutor;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TextComponent;
+import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.scheduler.ScheduledTask;
@@ -127,4 +128,9 @@ public class BungeeListener extends VPNExecutor implements Listener {
AntiVPN.getInstance().checked++;
});
}
+
+ @EventHandler
+ public void onLeave(PlayerDisconnectEvent event) {
+ AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
+ }
}
diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java
index f3b7412..b5e41fb 100644
--- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java
+++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java
@@ -10,7 +10,7 @@ import java.util.stream.Collectors;
public class BungeePlayerExecutor implements PlayerExecutor {
- private final Map cachedPlayers = new WeakHashMap<>();
+ private final Map cachedPlayers = new HashMap<>();
@Override
public Optional getPlayer(String name) {
@@ -18,7 +18,7 @@ public class BungeePlayerExecutor implements PlayerExecutor {
if(player == null) return Optional.empty();
- return Optional.of(cachedPlayers.computeIfAbsent(player, BungeePlayer::new));
+ return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), key -> new BungeePlayer(player)));
}
@Override
@@ -27,13 +27,18 @@ public class BungeePlayerExecutor implements PlayerExecutor {
if(player == null) return Optional.empty();
- return Optional.of(cachedPlayers.computeIfAbsent(player, BungeePlayer::new));
+ return Optional.of(cachedPlayers.computeIfAbsent(uuid, key -> new BungeePlayer(player)));
+ }
+
+ @Override
+ public void unloadPlayer(UUID uuid) {
+ this.cachedPlayers.remove(uuid);
}
@Override
public List getOnlinePlayers() {
return BungeeCord.getInstance().getPlayers().stream()
- .map(pl -> cachedPlayers.computeIfAbsent(pl, BungeePlayer::new))
+ .map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new BungeePlayer(pl)))
.collect(Collectors.toList());
}
}
diff --git a/Common/pom.xml b/Common/pom.xml
index 28dbdbd..a5f8954 100644
--- a/Common/pom.xml
+++ b/Common/pom.xml
@@ -5,7 +5,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
diff --git a/Common/src/main/java/dev/brighten/antivpn/api/PlayerExecutor.java b/Common/src/main/java/dev/brighten/antivpn/api/PlayerExecutor.java
index be7398e..56909dc 100644
--- a/Common/src/main/java/dev/brighten/antivpn/api/PlayerExecutor.java
+++ b/Common/src/main/java/dev/brighten/antivpn/api/PlayerExecutor.java
@@ -10,5 +10,7 @@ public interface PlayerExecutor {
Optional getPlayer(UUID uuid);
+ void unloadPlayer(UUID uuid);
+
List getOnlinePlayers();
}
diff --git a/Velocity/pom.xml b/Velocity/pom.xml
index cfd8b5e..7d65776 100644
--- a/Velocity/pom.xml
+++ b/Velocity/pom.xml
@@ -5,7 +5,7 @@
AntiVPN
dev.brighten.antivpn
- 1.8
+ 1.8.1
4.0.0
@@ -33,7 +33,7 @@
dev.brighten.antivpn
Common
- 1.8
+ 1.8.1
provided
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
index 8b980f8..eb76e25 100644
--- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
+++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java
@@ -1,5 +1,6 @@
package dev.brighten.antivpn.velocity;
+import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.scheduler.ScheduledTask;
import dev.brighten.antivpn.AntiVPN;
@@ -20,110 +21,113 @@ public class VelocityListener extends VPNExecutor {
VelocityPlugin.INSTANCE.getServer().getEventManager()
.register(VelocityPlugin.INSTANCE, this);
+ VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, DisconnectEvent.class,
+ event -> AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId()));
+
VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class,
event -> {
- if(event.getResult().isAllowed()) {
- if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
- //Is exempt
- || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())
- //Or has a name that starts with a certain prefix. This is for Bedrock exempting.
- || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress()
- .getAddress().getHostAddress())
- || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
- .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return;
+ if (event.getResult().isAllowed()) {
+ if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
+ //Is exempt
+ || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())
+ //Or has a name that starts with a certain prefix. This is for Bedrock exempting.
+ || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress()
+ .getAddress().getHostAddress())
+ || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
+ .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return;
- checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(),
- AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
- if(result.isSuccess()) {
- // If the countryList() size is zero, no need to check.
- // Running country check first
- if(AntiVPN.getInstance().getVpnConfig().countryList().size() > 0
- && !(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().getExecutor().isWhitelisted(event.getPlayer()
- .getRemoteAddress().getAddress().getHostAddress()))
- // This bit of code will decide whether or not to kick the player
- // If it contains the code and it is set to whitelist, it will not kick
- // as they are equal and vise versa. However, if the contains does not match
- // the state, it will kick.
- && AntiVPN.getInstance().getVpnConfig().countryList()
- .contains(result.getCountryCode())
- != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
- //Using our built in kicking system if no commands are configured
- if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().size() == 0) {
- final String kickReason = AntiVPN.getInstance().getVpnConfig()
- .countryVanillaKickReason();
- // Kicking our player
- event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
- .build().deserialize(kickReason
- .replace("%player%", event.getPlayer().getUsername())
- .replace("%country%", result.getCountryName())
- .replace("%code%", result.getCountryCode())));
+ checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(),
+ AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
+ if (result.isSuccess()) {
+ // If the countryList() size is zero, no need to check.
+ // Running country check first
+ if (AntiVPN.getInstance().getVpnConfig().countryList().size() > 0
+ && !(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().getExecutor().isWhitelisted(event.getPlayer()
+ .getRemoteAddress().getAddress().getHostAddress()))
+ // This bit of code will decide whether or not to kick the player
+ // If it contains the code and it is set to whitelist, it will not kick
+ // as they are equal and vise versa. However, if the contains does not match
+ // the state, it will kick.
+ && AntiVPN.getInstance().getVpnConfig().countryList()
+ .contains(result.getCountryCode())
+ != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
+ //Using our built in kicking system if no commands are configured
+ if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().size() == 0) {
+ final String kickReason = AntiVPN.getInstance().getVpnConfig()
+ .countryVanillaKickReason();
+ // Kicking our player
+ event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
+ .build().deserialize(kickReason
+ .replace("%player%", event.getPlayer().getUsername())
+ .replace("%country%", result.getCountryName())
+ .replace("%code%", result.getCountryCode())));
+ } else {
+ for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
+ final String formattedCommand = StringUtils
+ .translateAlternateColorCodes('&',
+ cmd.replace("%player%",
+ event.getPlayer().getUsername())
+ .replace("%country%", result.getCountryName())
+ .replace("%code%", result.getCountryCode()));
+ // Running the command from console
+ VelocityPlugin.INSTANCE.getServer().getCommandManager()
+ .executeAsync(VelocityPlugin.INSTANCE.getServer()
+ .getConsoleCommandSource(),
+ StringUtils.translateAlternateColorCodes('&',
+ formattedCommand));
+ }
+ }
+ } else if (result.isProxy()) {
+ if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
+ event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
+ .build().deserialize(AntiVPN.getInstance().getVpnConfig()
+ .getKickString()));
+ VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername()
+ + " joined on a VPN/Proxy (" + result.getMethod() + ")");
+ //Ensuring the user wishes to alert to staff
+ if (AntiVPN.getInstance().getVpnConfig().alertToStaff())
+ AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
+ .filter(APIPlayer::isAlertsEnabled)
+ .forEach(pl ->
+ pl.sendMessage(AntiVPN.getInstance().getVpnConfig()
+ .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().getVpnConfig().runCommands()) {
+ for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
+ VelocityPlugin.INSTANCE.getServer().getCommandManager()
+ .executeAsync(VelocityPlugin.INSTANCE.getServer()
+ .getConsoleCommandSource(),
+ StringUtils.translateAlternateColorCodes('&',
+ command.replace("%player%",
+ event.getPlayer().getUsername())));
+ }
+ }
+ AntiVPN.getInstance().detections++;
+ }
} else {
- for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
- final String formattedCommand = StringUtils
- .translateAlternateColorCodes('&',
- cmd.replace("%player%",
- event.getPlayer().getUsername())
- .replace("%country%", result.getCountryName())
- .replace("%code%", result.getCountryCode()));
- // Running the command from console
- VelocityPlugin.INSTANCE.getServer().getCommandManager()
- .executeAsync(VelocityPlugin.INSTANCE.getServer()
- .getConsoleCommandSource(),
- StringUtils.translateAlternateColorCodes('&',
- formattedCommand));
- }
+ 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");
}
- } else if(result.isProxy()) {
- if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
- event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
- .build().deserialize(AntiVPN.getInstance().getVpnConfig()
- .getKickString()));
- VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername()
- + " joined on a VPN/Proxy (" + result.getMethod() + ")");
- //Ensuring the user wishes to alert to staff
- if(AntiVPN.getInstance().getVpnConfig().alertToStaff())
- AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
- .filter(APIPlayer::isAlertsEnabled)
- .forEach(pl ->
- pl.sendMessage(AntiVPN.getInstance().getVpnConfig()
- .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().getVpnConfig().runCommands()) {
- for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
- VelocityPlugin.INSTANCE.getServer().getCommandManager()
- .executeAsync(VelocityPlugin.INSTANCE.getServer()
- .getConsoleCommandSource(),
- StringUtils.translateAlternateColorCodes('&',
- command.replace("%player%",
- event.getPlayer().getUsername())));
- }
- }
- AntiVPN.getInstance().detections++;
- }
- } else {
- 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++;
- });
- }
- });
+ AntiVPN.getInstance().checked++;
+ });
+ }
+ });
}
@Override
@@ -136,7 +140,7 @@ public class VelocityListener extends VPNExecutor {
@Override
public void shutdown() {
- if(cacheResetTask != null) {
+ if (cacheResetTask != null) {
cacheResetTask.cancel();
cacheResetTask = null;
}
diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java
index c7edcb4..6c589ee 100644
--- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java
+++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlayerExecutor.java
@@ -9,13 +9,14 @@ import java.util.stream.Collectors;
public class VelocityPlayerExecutor implements PlayerExecutor {
- private final Map cachedPlayers = new WeakHashMap<>();
+ private final Map cachedPlayers = new HashMap<>();
@Override
public Optional getPlayer(String name) {
Optional player = VelocityPlugin.INSTANCE.getServer().getPlayer(name);
- return player.map(value -> cachedPlayers.computeIfAbsent(value, VelocityPlayer::new));
+ return player.map(value -> cachedPlayers.computeIfAbsent(value.getUniqueId(),
+ key -> new VelocityPlayer(value)));
}
@@ -23,13 +24,19 @@ public class VelocityPlayerExecutor implements PlayerExecutor {
public Optional getPlayer(UUID uuid) {
Optional player = VelocityPlugin.INSTANCE.getServer().getPlayer(uuid);
- return player.map(value -> cachedPlayers.computeIfAbsent(value, VelocityPlayer::new));
+ return player.map(value -> cachedPlayers.computeIfAbsent(value.getUniqueId(),
+ key -> new VelocityPlayer(value)));
+ }
+
+ @Override
+ public void unloadPlayer(UUID uuid) {
+ cachedPlayers.remove(uuid);
}
@Override
public List getOnlinePlayers() {
return VelocityPlugin.INSTANCE.getServer().getAllPlayers().stream()
- .map(pl -> cachedPlayers.computeIfAbsent(pl, VelocityPlayer::new))
+ .map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new VelocityPlayer(pl)))
.collect(Collectors.toList());
}
}
diff --git a/pom.xml b/pom.xml
index 8d4e955..e4fdb46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
dev.brighten.antivpn
AntiVPN
pom
- 1.8
+ 1.8.1
Common