Skip to content

Commit 2cb8633

Browse files
committed
1.0.0
1 parent 48a13b1 commit 2cb8633

File tree

8 files changed

+852
-1
lines changed

8 files changed

+852
-1
lines changed

README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,48 @@
11
# GeoIP
2-
GeoIP plugin for Nukkit. GeoIP provides an approximate lookup of where your players come from, based on their public IP and public geographical databases.
2+
[![](https://i.loli.net/2019/08/11/g9PU5ufFoqmeKjp.png)](http://www.mcbbs.net/thread-900823-1-1.html "IP定位")
3+
4+
GeoIP plugin for Nukkit.
5+
6+
GeoIP provides an approximate lookup of where your players come from, based on their public IP and public geographical databases.
7+
8+
Please see [mcbbs](http://www.mcbbs.net/thread-900823-1-1.html) for more information.
9+
## Permissions
10+
| Command | Permission | Description | Default |
11+
| - | - | - | - |
12+
| `/geoip` | geoip.show | Shows the GeoIP location of a player. | OP |
13+
| `/geoip` | geoip.show.fullip | Shows the full ip address of a player. | false |
14+
| | geoip.hide | Allows player to hide player's country and city from people who have permission geoip.show | false |
15+
## config.yml
16+
```yaml
17+
database:
18+
show-cities: false
19+
download-if-missing: true
20+
# Url for country
21+
download-url: "https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz"
22+
# Url for cities
23+
download-url-city: "https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz"
24+
update:
25+
enable: true
26+
by-every-x-days: 30
27+
show-on-login: true
28+
# "enable-locale" enables locale on geolocation display.
29+
enable-locale: true
30+
# Not all languages are supported. See https://dev.maxmind.com/geoip/geoip2/web-services/#Languages
31+
locale: en
32+
```
33+
## API Usage
34+
```java
35+
import cn.nukkit.Player;
36+
import cn.nukkit.Server;
37+
import cn.wode490390.nukkit.geoip.GeoIP;
38+
39+
class Example {
40+
Example() {
41+
Player player = Server.getInstance().getPlayer("wode490390");
42+
if (player != null) {
43+
String geoLocation = GeoIP.query(player); //Our API :)
44+
player.sendMessage("Your location: " + geoLocation);
45+
}
46+
}
47+
}
48+
```

pom.xml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<groupId>cn.wode490390.nukkit</groupId>
5+
<artifactId>geoip</artifactId>
6+
<version>1.0.0</version>
7+
<name>GeoIP</name>
8+
<description>GeoIP plugin for Nukkit</description>
9+
<inceptionYear>2018</inceptionYear>
10+
<url>http://wode490390.cn/</url>
11+
<packaging>jar</packaging>
12+
<licenses>
13+
<license>
14+
<name>GNU General Public License, Version 3.0</name>
15+
<url>http://www.gnu.org/licenses/gpl.html</url>
16+
<distribution>repo</distribution>
17+
</license>
18+
</licenses>
19+
<properties>
20+
<maven.compiler.source>1.8</maven.compiler.source>
21+
<maven.compiler.target>1.8</maven.compiler.target>
22+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23+
</properties>
24+
<repositories>
25+
<repository>
26+
<id>nukkitx</id>
27+
<url>http://repo.nukkitx.com/main/</url>
28+
</repository>
29+
</repositories>
30+
<dependencies>
31+
<dependency>
32+
<groupId>cn.nukkit</groupId>
33+
<artifactId>nukkit</artifactId>
34+
<version>1.0-SNAPSHOT</version>
35+
<scope>provided</scope>
36+
</dependency>
37+
<dependency>
38+
<groupId>com.maxmind.geoip2</groupId>
39+
<artifactId>geoip2</artifactId>
40+
<version>2.12.0</version>
41+
</dependency>
42+
<dependency>
43+
<groupId>javatar</groupId>
44+
<artifactId>javatar</artifactId>
45+
<version>2.5</version>
46+
</dependency>
47+
</dependencies>
48+
<build>
49+
<finalName>wodeGeoIP-${project.version}</finalName>
50+
<defaultGoal>clean package</defaultGoal>
51+
<resources>
52+
<resource>
53+
<targetPath>.</targetPath>
54+
<filtering>true</filtering>
55+
<directory>${basedir}/src/main/resources</directory>
56+
<includes>
57+
<include>*.yml</include>
58+
</includes>
59+
</resource>
60+
</resources>
61+
<plugins>
62+
<plugin>
63+
<groupId>org.apache.maven.plugins</groupId>
64+
<artifactId>maven-shade-plugin</artifactId>
65+
<version>3.2.1</version>
66+
<executions>
67+
<execution>
68+
<phase>package</phase>
69+
<goals>
70+
<goal>shade</goal>
71+
</goals>
72+
</execution>
73+
</executions>
74+
<configuration>
75+
<createDependencyReducedPom>false</createDependencyReducedPom>
76+
<minimizeJar>true</minimizeJar>
77+
<artifactSet>
78+
<includes>
79+
<include>*:*</include>
80+
</includes>
81+
</artifactSet>
82+
</configuration>
83+
</plugin>
84+
</plugins>
85+
</build>
86+
</project>
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package cn.wode490390.nukkit.geoip;
2+
3+
import cn.nukkit.Player;
4+
import cn.nukkit.plugin.PluginBase;
5+
import cn.nukkit.utils.Config;
6+
import com.google.common.base.Preconditions;
7+
import com.google.common.collect.Maps;
8+
import com.ice.tar.TarEntry;
9+
import com.ice.tar.TarInputStream;
10+
import com.maxmind.geoip2.DatabaseReader;
11+
import java.io.File;
12+
import java.io.FileOutputStream;
13+
import java.io.IOException;
14+
import java.io.InputStream;
15+
import java.io.OutputStream;
16+
import java.net.MalformedURLException;
17+
import java.net.URL;
18+
import java.net.URLConnection;
19+
import java.util.Arrays;
20+
import java.util.Date;
21+
import java.util.Map;
22+
import java.util.zip.GZIPInputStream;
23+
24+
public class GeoIP extends PluginBase {
25+
26+
private static final Map<Player, String> cache = Maps.newHashMap();
27+
28+
/**
29+
* Querys player's geographical location.
30+
*
31+
* @param palyer
32+
* @return geographical location or null
33+
*/
34+
public static String query(Player palyer) {
35+
Preconditions.checkNotNull(palyer, "Player cannot be null");
36+
return cache.get(palyer);
37+
}
38+
39+
static void setGeoLocation(Player palyer, String location) {
40+
cache.put(palyer, location);
41+
}
42+
43+
Config config;
44+
private File databaseFile;
45+
46+
@Override
47+
public void onEnable() {
48+
try {
49+
new MetricsLite(this);
50+
} catch (Exception ignore) {
51+
52+
}
53+
this.saveDefaultConfig();
54+
this.config = this.getConfig();
55+
if (this.config.getBoolean("database.show-cities", false)) {
56+
this.databaseFile = new File(this.getDataFolder(), "GeoIP2-City.mmdb");
57+
} else {
58+
this.databaseFile = new File(this.getDataFolder(), "GeoIP2-Country.mmdb");
59+
}
60+
if (!this.databaseFile.exists()) {
61+
if (this.config.getBoolean("database.download-if-missing", true)) {
62+
this.downloadDatabase();
63+
} else {
64+
this.getLogger().warning("Can't find GeoIP database!");
65+
this.setEnabled(false);
66+
return;
67+
}
68+
} else if (this.config.getBoolean("database.update.enable", true)) {
69+
// try to update expired mmdb files
70+
long diff = new Date().getTime() - this.databaseFile.lastModified();
71+
if (diff / 86400000 > this.config.getLong("database.update.by-every-x-days", 30)) {
72+
this.downloadDatabase();
73+
}
74+
}
75+
DatabaseReader mmreader;
76+
try {
77+
// locale setting
78+
if (this.config.getBoolean("enable-locale")) {
79+
// If the locale is not avaliable, use "en".
80+
mmreader = new DatabaseReader.Builder(this.databaseFile).locales(Arrays.asList(this.config.getString("locale"), "en")).build();
81+
} else {
82+
mmreader = new DatabaseReader.Builder(this.databaseFile).build();
83+
}
84+
} catch (IOException ex) {
85+
this.getLogger().warning("Failed to read GeoIP database", ex);
86+
this.setEnabled(false);
87+
return;
88+
}
89+
this.getServer().getPluginManager().registerEvents(new GeoIPListener(this, mmreader), this);
90+
this.getServer().getCommandMap().register("geoip", new GeoIPCommand(this));
91+
}
92+
93+
private void downloadDatabase() {
94+
try {
95+
String url;
96+
if (this.config.getBoolean("database.show-cities", false)) {
97+
url = this.config.getString("database.download-url-city");
98+
} else {
99+
url = this.config.getString("database.download-url");
100+
}
101+
if (url == null || url.isEmpty()) {
102+
this.getLogger().warning("GeoIP download url is empty.");
103+
return;
104+
}
105+
this.getLogger().info("Downloading GeoIP database... this might take a while.");
106+
URL downloadUrl = new URL(url);
107+
URLConnection conn = downloadUrl.openConnection();
108+
conn.setConnectTimeout(10000);
109+
conn.connect();
110+
InputStream input = conn.getInputStream();
111+
OutputStream output = new FileOutputStream(this.databaseFile);
112+
byte[] buffer = new byte[2048];
113+
if (url.endsWith(".gz")) {
114+
input = new GZIPInputStream(input);
115+
if (url.endsWith(".tar.gz")) {
116+
// The new GeoIP2 uses tar.gz to pack the db file along with some other txt. So it makes things a bit complicated here.
117+
String filename;
118+
TarInputStream tarInputStream = new TarInputStream(input);
119+
TarEntry entry;
120+
while ((entry = tarInputStream.getNextEntry()) != null) {
121+
if (!entry.isDirectory()) {
122+
filename = entry.getName();
123+
if (filename.substring(filename.length() - 5).equalsIgnoreCase(".mmdb")) {
124+
input = tarInputStream;
125+
break;
126+
}
127+
}
128+
}
129+
}
130+
}
131+
int length = input.read(buffer);
132+
while (length >= 0) {
133+
output.write(buffer, 0, length);
134+
length = input.read(buffer);
135+
}
136+
output.close();
137+
input.close();
138+
} catch (MalformedURLException ex) {
139+
this.getLogger().warning("GeoIP download url is invalid", ex);
140+
} catch (IOException ex) {
141+
this.getLogger().warning("Failed to open connection", ex);
142+
}
143+
}
144+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package cn.wode490390.nukkit.geoip;
2+
3+
import cn.nukkit.Player;
4+
import cn.nukkit.Server;
5+
import cn.nukkit.command.Command;
6+
import cn.nukkit.command.CommandSender;
7+
import cn.nukkit.command.PluginIdentifiableCommand;
8+
import cn.nukkit.command.data.CommandParamType;
9+
import cn.nukkit.command.data.CommandParameter;
10+
import cn.nukkit.lang.TranslationContainer;
11+
import cn.nukkit.plugin.Plugin;
12+
import cn.nukkit.utils.TextFormat;
13+
14+
public class GeoIPCommand extends Command implements PluginIdentifiableCommand {
15+
16+
private final Plugin plugin;
17+
18+
public GeoIPCommand(Plugin plugin) {
19+
super("geoip", "Querys the GeoIP location of a player", "/geoip <player>");
20+
this.setPermission("geoip.show");
21+
this.getCommandParameters().clear();
22+
this.addCommandParameters("default", new CommandParameter[]{
23+
new CommandParameter("player", CommandParamType.TARGET, false)
24+
});
25+
this.plugin = plugin;
26+
}
27+
28+
@Override
29+
public boolean execute(CommandSender sender, String label, String[] args) {
30+
if (!this.plugin.isEnabled() || !this.testPermission(sender)) {
31+
return false;
32+
}
33+
if (args.length > 0) {
34+
Player player = Server.getInstance().getPlayer(args[0]);
35+
if (player != null) {
36+
String geoLocation = GeoIP.query(player);
37+
if (geoLocation != null) {
38+
String[] ip = player.getAddress().split("\\.");
39+
try {
40+
sender.sendMessage(TextFormat.colorize("&6Player &c" + player.getDisplayName() + " &6comes from &c" + geoLocation + "&6. (IP:&c" + (sender.hasPermission("geoip.show.fullip") ? player.getAddress() : ip[0] + ".*.*." + ip[3]) + "&6)"));
41+
return true;
42+
} catch (Exception ex) {
43+
44+
}
45+
}
46+
sender.sendMessage(TextFormat.colorize("&6Player &c" + player.getDisplayName() + " &6comes from &aan unknown country&6."));
47+
} else {
48+
sender.sendMessage(new TranslationContainer("commands.generic.player.notFound"));
49+
}
50+
} else {
51+
sender.sendMessage(new TranslationContainer("commands.generic.usage", this.getUsage()));
52+
}
53+
return true;
54+
}
55+
56+
@Override
57+
public Plugin getPlugin() {
58+
return this.plugin;
59+
}
60+
}

0 commit comments

Comments
 (0)