Added ApiClient for Replay Mod Website API (completely untested).

This commit is contained in:
Marius Metzger
2015-01-23 16:02:04 +01:00
parent 9ae8f9fc5e
commit f39e0f9eae
15 changed files with 885 additions and 2 deletions

View File

@@ -1,5 +1,7 @@
package eu.crushedpixel.replaymod;
import java.io.IOException;
import net.minecraft.client.Minecraft;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
@@ -12,6 +14,9 @@ import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import eu.crushedpixel.replaymod.api.client.ApiClient;
import eu.crushedpixel.replaymod.api.client.ApiException;
import eu.crushedpixel.replaymod.api.client.holders.AuthKey;
import eu.crushedpixel.replaymod.authentication.AuthenticationHandler;
import eu.crushedpixel.replaymod.events.GuiEventHandler;
import eu.crushedpixel.replaymod.events.GuiReplayOverlay;
@@ -29,9 +34,9 @@ public class ReplayMod
//XXX
//Known Bugs
//
//Keyframe removal doesn't seem to properly work
//Keyframes have problems with Linear Paths
//Rain isn't working
//
//Incompatible with Shaders Mod
//
//

View File

@@ -0,0 +1,133 @@
package eu.crushedpixel.replaymod.api.client;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.crushedpixel.replaymod.api.client.holders.ApiError;
import eu.crushedpixel.replaymod.api.client.holders.AuthKey;
import eu.crushedpixel.replaymod.api.client.holders.Category;
import eu.crushedpixel.replaymod.api.client.holders.FileInfo;
import eu.crushedpixel.replaymod.api.client.holders.Success;
import eu.crushedpixel.replaymod.api.client.holders.UserFiles;
public class ApiClient {
private static Gson gson = new Gson();
private static JsonParser jsonParser = new JsonParser();
public AuthKey getLogin(String username, String password) throws IOException, ApiException {
QueryBuilder builder = new QueryBuilder(ApiMethods.login);
builder.put("user", username);
builder.put("pw", password);
AuthKey auth = invokeAndReturn(builder, AuthKey.class);
return auth;
}
public UserFiles getUserFiles(String auth, String user) throws IOException, ApiException {
QueryBuilder builder = new QueryBuilder(ApiMethods.replay_files);
builder.put("auth", auth);
builder.put("user", user);
UserFiles files = invokeAndReturn(builder, UserFiles.class);
return files;
}
public FileInfo[] getFileInfo(String auth, List<Integer> ids) throws IOException, ApiException {
QueryBuilder builder = new QueryBuilder(ApiMethods.replay_files);
builder.put("auth", auth);
builder.put("ids", buildListString(ids));
FileInfo[] info = invokeAndReturn(builder, FileInfo[].class); //TODO: Test if that works
return info;
}
public void uploadFile(String auth, File file, Category category) throws IOException, ApiException {
QueryBuilder builder = new QueryBuilder(ApiMethods.upload_file);
builder.put("auth", auth);
builder.put("category", category.getId());
String url = builder.toString();
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(url);
((InputStream)client).close();
FileEntity entity = new FileEntity(file);
post.setEntity(entity);
HttpResponse response = client.execute(post);
JsonElement element = jsonParser.parse(EntityUtils.toString(response.getEntity()));
try {
ApiError err = gson.fromJson(element, ApiError.class);
if(err.getDesc() != null) {
throw new ApiException(err);
}
} catch(Exception e) {}
}
public void downloadFile(String auth, int file, File target) throws IOException {
QueryBuilder builder = new QueryBuilder(ApiMethods.download_file);
builder.put("auth", auth);
builder.put("id", file);
String url = builder.toString();
URL website = new URL(url);
InputStream is = website.openStream();
try { //If valid json, an error occured
jsonParser.parse(StreamTools.readStreamtoString(is));
} catch(Exception e) {
Files.copy(is, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
public void rateFile(String auth, int file, boolean like) throws IOException, ApiException {
QueryBuilder builder = new QueryBuilder(ApiMethods.rate_file);
builder.put("auth", auth);
builder.put("id", file);
builder.put("like", like);
invokeAndReturn(builder, Success.class);
}
public void removeFile(String auth, int file) throws IOException, ApiException {
QueryBuilder builder = new QueryBuilder(ApiMethods.remove_file);
builder.put("auth", auth);
builder.put("id", file);
invokeAndReturn(builder, Success.class);
}
private <T> T invokeAndReturn(QueryBuilder builder,Class<T> classOfT) throws IOException, ApiException {
JsonObject arr = GsonApiClient.invoke(builder);
return gson.fromJson(arr, classOfT);
}
@SuppressWarnings("rawtypes")
private String buildListString(List idList) {
if(idList == null) return null;
String ids = "";
Integer x=0;
for(Object id : idList) {
x++;
ids += id.toString();
if(x != idList.size()) {
ids += ",";
}
}
return ids;
}
}

View File

@@ -0,0 +1,20 @@
package eu.crushedpixel.replaymod.api.client;
import eu.crushedpixel.replaymod.api.client.holders.ApiError;
public class ApiException extends Exception {
private static final long serialVersionUID = 349073390504232810L;
private ApiError error;
public ApiException(ApiError error) {
super(error.getDesc());
this.error = error;
}
public ApiError getError() {
return error;
}
}

View File

@@ -0,0 +1,14 @@
package eu.crushedpixel.replaymod.api.client;
public class ApiMethods {
public static final String login = "login";
public static final String logout = "logout";
public static final String replay_files = "replay_files";
public static final String file_details = "file_details";
public static final String upload_file = "upload_file";
public static final String download_file = "download_file";
public static final String remove_file = "remove_file";
public static final String rate_file = "rate_file";
}

View File

@@ -0,0 +1,38 @@
package eu.crushedpixel.replaymod.api.client;
import java.io.IOException;
import java.util.Map;
import com.google.gson.JsonObject;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
public class GsonApiClient {
private static final JsonParser parser = new JsonParser();
public static JsonObject invoke(QueryBuilder query) throws IOException, ApiException {
String apiResult = SimpleApiClient.invoke(query);
return wrapWithJson(apiResult);
}
public static JsonObject invokeJson(String url) throws IOException, ApiException {
String apiResult = SimpleApiClient.invokeUrl(url);
return wrapWithJson(apiResult);
}
public static JsonObject invokeJson(String apiKey, String method, Map<String,Object> paramMap) throws IOException, ApiException {
String apiResult = SimpleApiClient.invoke(method, paramMap);
return wrapWithJson(apiResult);
}
public static JsonObject invokeJson(String apiKey, String method) throws IOException, ApiException {
String apiResult = SimpleApiClient.invoke(method, null);
return wrapWithJson(apiResult);
}
private static JsonObject wrapWithJson(String apiResult) {
JsonElement element = parser.parse(apiResult);
return element.getAsJsonObject();
}
}

View File

@@ -0,0 +1,166 @@
package eu.crushedpixel.replaymod.api.client;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
public class QueryBuilder {
private static final String API_BASE_URL = "http://ReplayMod.com/api/";
public String apiMethod;
public Map<String,String> paramMap;
/**
* Creates an empty QueryBuilder from a given apikey.
* <br>Note that in order to use the QueryBuilder an apiMethod String has to be set.
* @param apiKey The apikey to use
*/
public QueryBuilder() {
this(null);
}
/**
* Creates an empty QueryBuilder from a given apikey and apiMethod.
*
* @param apiKey The apikey to use
* @param apiMethod The apiMethod to use
*/
public QueryBuilder(String apiMethod) {
this.apiMethod = apiMethod;
}
/**
* Creates a QueryBuilder from a given apikey and apiMethod containing a single key/value parameter.
*
* @param apiKey The apikey to use
* @param apiMethod The apiMethod to use
* @param key The parameter key
* @param value The parameter value
*/
public QueryBuilder(String apiMethod, String key, String value) {
this(apiMethod);
put(key, value);
}
/**
* Creates a QueryBuilder from a given apikey and apiMethod containing two key/value parameters.
*
* @param apiKey The apikey to use
* @param apiMethod The apiMethod to use
* @param key1 The first parameter key
* @param value1 The first parameter value
* @param key2 The second parameter key
* @param value2 The second parameter value
*/
public QueryBuilder(String apiMethod, String key1, String value1, String key2, String value2) {
this(apiMethod);
put(key1, value1);
put(key2, value2);
}
/**
* Creates a QueryBuilder from a given apikey and apiMethod containing three key/value parameters.
*
* @param apiKey The apikey to use
* @param apiMethod The apiMethod to use
* @param key1 The first parameter key
* @param value1 The first parameter value
* @param key2 The second parameter key
* @param value2 The second parameter value
* @param key3 The third parameter key
* @param value3 The third parameter value
*/
public QueryBuilder(String apiMethod, String key1, String value1, String key2, String value2, String key3, String value3) {
this(apiMethod);
put(key1, value1);
put(key2, value2);
put(key3, value3);
}
/**
* Adds a key/value parameter to the QueryBuilder.
* @param key The parameter key
* @param value The parameter value
*/
public void put(String key, Object value) {
if(key != null && value != null) {
if(paramMap == null) {
paramMap = new HashMap<String,String>();
}
paramMap.put(key, value.toString());
}
}
/**
* Adds two key/value parameters to the QueryBuilder.
* @param key1 The first parameter key
* @param value1 The first parameter value
* @param key2 The second parameter key
* @param value2 The second parameter value
*/
public void put(String key1, Object value1, String key2, Object value2) {
put(key1, value1);
put(key2, value2);
}
/**
* Adds three key/value parameters to the QueryBuilder.
* @param key1 The first parameter key
* @param value1 The first parameter value
* @param key2 The second parameter key
* @param value2 The second parameter value
* @param key3 The third parameter key
* @param value3 The third parameter value
*/
public void put(String key1, Object value1, String key2, Object value2, String key3, Object value3) {
put(key1, value1);
put(key2, value2);
put(key3, value3);
}
/**
* Adds a map of key/value parameters to the QueryBuilder.
* @param paraMap The map to add
*/
public void put(Map<String,Object> paraMap) {
if(paraMap == null) return;
for(String key: paraMap.keySet()) {
put(key,paraMap.get(key));
}
}
/**
* Creates an URL from the QueryBuilder using the given apikey and apiMethod and applies all
* parameters to it.
*/
public String toString() {
if(apiMethod == null) throw new IllegalArgumentException("apiMethod may not be null");
StringBuilder sb = new StringBuilder();
// build base url
sb.append(API_BASE_URL);
sb.append(apiMethod);
// process parameters
try {
if(paramMap != null) {
boolean first = true;
for(String paramName: paramMap.keySet()) {
if(first) sb.append("?");
if(!first) sb.append("&");
first = false;
sb.append(paramName);
sb.append("=");
String value = paramMap.get(paramName);
sb.append(URLEncoder.encode(value, "UTF-8"));
}
}
return sb.toString();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,119 @@
package eu.crushedpixel.replaymod.api.client;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.crushedpixel.replaymod.api.client.holders.ApiError;
public class SimpleApiClient {
private static final JsonParser jsonParser = new JsonParser();
private static final Gson gson = new Gson();
/**
* Returns a Json String from the given QueryBuilder
* @param query The QueryBuilder to use
* @return A Json String from the API
* @throws IOException
* @throws ApiException
*/
public static String invoke(QueryBuilder query) throws IOException, ApiException {
return invokeImpl(query.toString());
}
/**
* Returns a Json String from the given URL
* @param url The URL to parse the Json from
* @return A Json String from the API
* @throws IOException
* @throws ApiException
*/
public static String invokeUrl(String url) throws IOException, ApiException {
return invokeImpl(url);
}
/**
* Returns a Json String from the API
* @param apiKey The apikey to use
* @param method The apiMethod to be called
* @param paramMap The parameters to apply
* @return A Json String from the API
* @throws IOException
* @throws ApiException
*/
public static String invoke(String method, Map<String,Object> paramMap) throws IOException, ApiException {
return invokeImpl(method, paramMap);
}
/**
* Returns a Json String from the API
* @param apiKey The apikey to use
* @param method The apiMethod to be called
* @return A Json String from the API
* @throws IOException
* @throws ApiException
*/
public static String invoke(String method) throws IOException, ApiException {
return invokeImpl(method, null);
}
private static String invokeImpl(String urlString) throws IOException, ApiException {
// read response
String responseContent = null;
InputStream is = null;
HttpURLConnection httpUrlConnection = null;
HttpURLConnection.setFollowRedirects(false);
try {
URL url = new URL(urlString);
httpUrlConnection = (HttpURLConnection) url.openConnection();
httpUrlConnection.setRequestMethod("GET");
// give it 15 seconds to respond
httpUrlConnection.setReadTimeout(15*1000);
httpUrlConnection.connect();
int responseCode = httpUrlConnection.getResponseCode();
if(responseCode != 200) {
is = httpUrlConnection.getErrorStream();
if(is != null) {
responseContent = StreamTools.readStreamtoString(is,"UTF-8");
} else {
responseContent = "";
}
JsonObject response = jsonParser.parse(responseContent).getAsJsonObject();
throw new ApiException(gson.fromJson(response, ApiError.class));
}
is = httpUrlConnection.getInputStream();
responseContent = StreamTools.readStreamtoString(is,"UTF-8");
} catch(IOException e) {
throw e;
} finally {
if(is != null) {
is.close();
}
if(httpUrlConnection != null) {
httpUrlConnection.disconnect();
}
}
return responseContent;
}
private static String invokeImpl(String method, Map<String,Object> paramMap) throws IOException, ApiException {
QueryBuilder queryBuilder = new QueryBuilder(method);
queryBuilder.put(paramMap);
return invokeImpl(queryBuilder.toString());
}
}

View File

@@ -0,0 +1,247 @@
/* Leichtathletik Daten Verarbeitung (LDV)
* Copyright (C) 2004 Marc Schunk
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package eu.crushedpixel.replaymod.api.client;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
*
* @author Marc Schunk, created on 21.06.2004
*
* A Class to provide helpers of commonly user operations with binary
* and character Streams, such as read to an OutputStream/Writer,
* String, ByteArray. In addition streams can be compressed.
*/
public class StreamTools {
/**
* Checks weather the given array is contained in the first array
*
* @param ar1
* - the containing array
* @param index1
* - the index where the search starts
* @param ar2
* - the array that schould be contained
* @return - true of ar2 is contained in ar1
*/
public static final boolean arrayMatch(char[] ar1, int index1, char[] ar2) {
if((ar1.length - index1 - ar2.length) < 0)
return false;
for(int i = 0; i < ar2.length; i++) {
if(ar1[index1 + i] != ar2[i])
return false;
}
return true;
}
/**
* Reads inStream completly and writes it to outStream. Streams will not be
* closed.
*
* @param inStream
* - an InputStream to be read completely
* @param outStream
* - the destination Stream
* @throws IOException
*/
public static void readStream(InputStream inStream, OutputStream outStream)
throws IOException {
byte[] buffer = new byte[4096];
int len = 0;
while((len = inStream.read(buffer, 0, buffer.length)) > -1) {
outStream.write(buffer, 0, len);
}
}
/**
* Reads inStream completly into an byte[]. Streams will not be closed.
*
* @param inStream
* - an InputStream to be read completely
* @param outStream
* - the destination Stream
* @throws IOException
*/
public static byte[] readStream(InputStream inStream) throws IOException {
byte[] buffer = new byte[4096];
int len = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((len = inStream.read(buffer, 0, buffer.length)) > -1) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
/**
* Reads the given Steam into an String *
*
* @throws IOException
*/
public static String readStreamtoString(InputStream inStream)
throws IOException {
StringWriter sw = new StringWriter();
InputStreamReader isr = new InputStreamReader(inStream);
char[] buffer = new char[4096];
int len = 0;
while((len = isr.read(buffer, 0, buffer.length)) > -1) {
sw.write(buffer, 0, len);
}
return sw.toString();
}
/**
* Reads the given Steam into an String using the given encoding
*
* @throws IOException
*/
public static String readStreamtoString(InputStream inStream, String charSet)
throws IOException {
StringWriter sw = new StringWriter();
InputStreamReader isr = new InputStreamReader(inStream, charSet);
char[] buffer = new char[4096];
int len = 0;
while((len = isr.read(buffer, 0, buffer.length)) > -1) {
sw.write(buffer, 0, len);
}
return sw.toString();
}
public static String stripComments(String inString, String newLineDelimiter)
throws IOException {
StringWriter sw = new StringWriter();
// use both windows and linux/unix line seperators to be independent of the file orgin
String[] lines = inString.split("\r\n|\n");
for(String line : lines) {
if((line.indexOf("#") == -1) && (line.trim().length() > 0)) {
sw.append(line).append(newLineDelimiter);
}
}
return sw.toString();
}
public static final String[] umlauteString = {"&amp;", "&#223;", "&auml;",
"&Auml;", "&ouml;", "&Ouml;", "&uuml;", "&Uuml;", "&szlig;"};
public static final String[] umlauteReplacement = {"&", "ß", "ä", "Ä", "ö",
"Ö", "ü", "Ü", "ß"};
public static String replaceSpecialCharacters(String s) {
for(int i = 0; i < umlauteString.length; i++) {
s = s.replaceAll(umlauteString[i], umlauteReplacement[i]);
}
return s;
}
/**
* Stores the given Stream in an byte[]. Stream is compressed on the fly.
* Streams will not be closed.
*
* @param inStream
* - an InputStream to be read completely
* @param outStream
* - the destination Stream
* @throws IOException
*/
public static byte[] compressStreamToByteArray(InputStream inStream)
throws IOException {
byte[] buffer = new byte[4096];
int len = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream zos = new GZIPOutputStream(baos);
while((len = inStream.read(buffer, 0, buffer.length)) > -1) {
zos.write(buffer, 0, len);
}
zos.close();
return baos.toByteArray();
}
/**
* Stores the given Stream in an byte[]. Stream is compressed on the fly.
* Streams will not be closed.
*
* @param inStream
* - an InputStream to be read completely
* @param outStream
* - the destination Stream
* @throws IOException
*/
public static byte[] compress(byte[] uncompressed) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(uncompressed);
return compressStreamToByteArray(bais);
}
/**
* Stores the given Stream in an byte[]. Stream is decompressed on the fly.
* Thus, the input stream should contain compressed content. Streams will
* not be closed.
*
* @param inStream
* - an InputStream to be read completely
* @param outStream
* - the destination Stream
* @throws IOException
*/
public static byte[] decompressStreamToByteArray(InputStream inStream)
throws IOException {
byte[] buffer = new byte[4096];
int len = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPInputStream gzis = new GZIPInputStream(inStream);
while((len = gzis.read(buffer, 0, buffer.length)) > -1) {
baos.write(buffer, 0, len);
}
inStream.close();
return baos.toByteArray();
}
public static byte[] decompress(byte[] compressed) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(compressed);
return decompressStreamToByteArray(bais);
}
/**
* Reads the given Steam into an String *
*
* @throws IOException
*/
public static String readReaderToString(Reader reader) throws IOException {
StringBuffer sb = new StringBuffer();
char[] buffer = new char[4096];
int len = 0;
while((len = reader.read(buffer, 0, buffer.length)) > -1) {
sb.append(buffer, 0, len);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,27 @@
package eu.crushedpixel.replaymod.api.client.holders;
public class ApiError {
public ApiError(int id, String desc) {
this.id = id;
this.desc = desc;
}
private int id;
private String desc;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}

View File

@@ -0,0 +1,14 @@
package eu.crushedpixel.replaymod.api.client.holders;
public class AuthKey {
private String auth;
public AuthKey(String authkey) {
this.auth = authkey;
}
public String getAuthkey() {
return auth;
}
}

View File

@@ -0,0 +1,23 @@
package eu.crushedpixel.replaymod.api.client.holders;
public enum Category {
SURVIVAL(0), MINIGAME(1), BUILD(2);
private int id;
Category(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public Category fromId(int id) {
for(Category c : values()) {
if(c.id == id) return c;
}
return null;
}
}

View File

@@ -0,0 +1,33 @@
package eu.crushedpixel.replaymod.api.client.holders;
import eu.crushedpixel.replaymod.recording.ReplayMetaData;
public class FileInfo {
private int id;
private ReplayMetaData metadata;
private String owner;
private Rating ratings;
private int size;
private int category;
public int getId() {
return id;
}
public ReplayMetaData getMetadata() {
return metadata;
}
public String getOwner() {
return owner;
}
public Rating getRatings() {
return ratings;
}
public int getSize() {
return size;
}
public int getCategory() {
return category;
}
}

View File

@@ -0,0 +1,14 @@
package eu.crushedpixel.replaymod.api.client.holders;
public class Rating {
private int negative, positive;
public int getNegative() {
return negative;
}
public int getPositive() {
return positive;
}
}

View File

@@ -0,0 +1,10 @@
package eu.crushedpixel.replaymod.api.client.holders;
public class Success {
private boolean success = false;
public boolean isSuccess() {
return success;
}
}

View File

@@ -0,0 +1,20 @@
package eu.crushedpixel.replaymod.api.client.holders;
public class UserFiles {
private String user;
private FileInfo[] files;
private int total_size;
public String getUser() {
return user;
}
public FileInfo[] getFiles() {
return files;
}
public int getTotal_size() {
return total_size;
}
}