Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/cli/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"inactiveDays": 7,
"updateInactive": false,
"profileUpdateInterval": 3,
"profileAPI": "mojang",
"minPlaytime": 60,
"excludeBanned": true,
"excludeOps": false,
Expand Down
1 change: 1 addition & 0 deletions config/plugin/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ players:
inactiveDays: 90
updateInactive: true
profileUpdateInterval: 3
profileAPI: "mojang"
minPlaytime: 60
excludeBanned: true
excludeOps: false
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/de/pdinklag/mcstats/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class Config {
private int playerCacheUUIDPrefix = 2;
private String defaultLanguage = "en";

private String profileAPI = "mojang";

public Config() {
}

Expand Down Expand Up @@ -177,4 +179,12 @@ public Path getEventsPath() {
public void setEventsPath(Path eventsPath) {
this.eventsPath = eventsPath;
}

public String getProfileAPI() {
return profileAPI;
}

public void setProfileAPI(String profileAPI) {
this.profileAPI = profileAPI;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package de.pdinklag.mcstats;

import de.pdinklag.mcstats.minetools.API;
import de.pdinklag.mcstats.minetools.APIRequestException;
import de.pdinklag.mcstats.minetools.EmptyResponseException;

/**
* Provides player profiles via the Minetools.eu API.
*/
public class MinetoolsAPIPlayerProfileProvider implements PlayerProfileProvider {
private final LogWriter log;

/**
* Constructs a new provider.
*/
public MinetoolsAPIPlayerProfileProvider(LogWriter log) {
this.log = log;
}

@Override
public PlayerProfile getPlayerProfile(Player player) {
if (player.getAccountType().maybeMojangAccount()) {
try {
PlayerProfile profile = API.requestPlayerProfile(player.getUuid());
player.setAccountType(AccountType.MOJANG);
return profile;
} catch (EmptyResponseException e) {
player.setAccountType(AccountType.OFFLINE);
} catch (APIRequestException e) {
log.writeError("Minetools.eu API profile request for player failed: " + player.getUuid(), e);
}
}
return player.getProfile();
}
}
7 changes: 6 additions & 1 deletion src/main/java/de/pdinklag/mcstats/Updater.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ public abstract class Updater {
private final Path dbPlayerlistPath;

protected PlayerProfileProvider getAuthenticProfileProvider() {
return new MojangAPIPlayerProfileProvider(log);
final String api = config.getProfileAPI();
if ("minetools".equalsIgnoreCase(api)) {
return new MinetoolsAPIPlayerProfileProvider(log);
} else {
return new MojangAPIPlayerProfileProvider(log);
}
}

protected void gatherLocalProfileProviders(PlayerProfileProviderList providers) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/de/pdinklag/mcstats/bukkit/BukkitConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public BukkitConfig(Plugin plugin) {
setMinPlaytime(bukkitConfig.getInt("players.minPlaytime", getMinPlaytime()));
setUpdateInactive(bukkitConfig.getBoolean("players.updateInactive", isUpdateInactive()));
setProfileUpdateInterval(bukkitConfig.getInt("players.profileUpdateInterval", getProfileUpdateInterval()));
setProfileAPI(bukkitConfig.getString("players.profileAPI", getProfileAPI()));

setExcludeBanned(bukkitConfig.getBoolean("players.excludeBanned", isExcludeBanned()));
setExcludeOps(bukkitConfig.getBoolean("players.excludeOps", isExcludeOps()));
Expand Down
1 change: 1 addition & 0 deletions src/main/java/de/pdinklag/mcstats/cli/JSONConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public JSONConfig(Path jsonPath) throws IOException, JSONException {
setMinPlaytime(players.optInt("minPlaytime", getMinPlaytime()));
setUpdateInactive(players.optBoolean("updateInactive", isUpdateInactive()));
setProfileUpdateInterval(players.optInt("profileUpdateInterval", getProfileUpdateInterval()));
setProfileAPI(players.optString("profileAPI", getProfileAPI()));

setExcludeBanned(players.optBoolean("excludeBanned", isExcludeBanned()));
setExcludeOps(players.optBoolean("excludeOps", isExcludeOps()));
Expand Down
71 changes: 71 additions & 0 deletions src/main/java/de/pdinklag/mcstats/minetools/API.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package de.pdinklag.mcstats.minetools;

import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

import org.json.JSONObject;

import de.pdinklag.mcstats.PlayerProfile;
import de.pdinklag.mcstats.util.StreamUtils;

/**
* Minetools.eu API.
*/
public class API {
private static final String API_URL = "https://api.minetools.eu/profile/";
private static final String SKIN_URL = "http://textures.minecraft.net/texture/";

/**
* Requests a player profile from the Minetools.eu API.
*
* @param uuid the UUID of the player in question
* @return the player profile associated to the given UUID.
* @throws EmptyResponseException in case the Minetools.eu API gives an empty response
* @throws APIRequestException in case any error occurs trying to request the
* profile
*/
public static PlayerProfile requestPlayerProfile(String uuid) throws EmptyResponseException, APIRequestException {
try {
final String response;
{
URL url = new URL(API_URL + uuid);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
response = StreamUtils.readStreamFully(conn.getInputStream());
conn.disconnect();
}

if (!response.isEmpty()) {
JSONObject obj = new JSONObject(response);

// Check if the response has the decoded profile
if (!obj.has("decoded")) {
throw new EmptyResponseException();
}

JSONObject decoded = obj.getJSONObject("decoded");
String name = decoded.getString("profileName");

// Extract skin URL from decoded textures
JSONObject textures = decoded.getJSONObject("textures");
if (!textures.has("SKIN")) {
throw new EmptyResponseException();
}

String skinUrl = textures.getJSONObject("SKIN").getString("url");
String skin = skinUrl.substring(SKIN_URL.length());

// Use timestamp from decoded profile, or current time if not available
long timestamp = decoded.optLong("timestamp", System.currentTimeMillis());

return new PlayerProfile(name, skin, timestamp);
} else {
throw new EmptyResponseException();
}
} catch (EmptyResponseException e) {
throw e; // nb: delegate
} catch (Exception e) {
throw new APIRequestException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package de.pdinklag.mcstats.minetools;

/**
* An exception raised while processing a Minetools.eu API request.
*/
public class APIRequestException extends RuntimeException {
APIRequestException() {
}

APIRequestException(String message) {
super(message);
}

APIRequestException(Throwable cause) {
super(cause);
}

APIRequestException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package de.pdinklag.mcstats.minetools;

/**
* Indicates an empty response from the Minetools.eu API.
*/
public class EmptyResponseException extends RuntimeException {
EmptyResponseException() {
}
}