Add update-check http api call to see if client is outdated

This commit is contained in:
Adam
2017-04-19 20:40:36 -04:00
parent 8e24e6a81f
commit 913e430047
5 changed files with 187 additions and 1 deletions

View File

@@ -37,6 +37,7 @@ public class RuneliteAPI
private static final String BASE = "https://api.runelite.net/runelite-";
private static final Properties properties = new Properties();
private static String version;
private static int rsVersion;
static
{
@@ -46,6 +47,7 @@ public class RuneliteAPI
properties.load(in);
version = properties.getProperty("runelite.version");
rsVersion = Integer.parseInt(properties.getProperty("rs.version"));
}
catch (IOException ex)
{
@@ -68,4 +70,9 @@ public class RuneliteAPI
RuneliteAPI.version = version;
}
public static int getRsVersion()
{
return rsVersion;
}
}

View File

@@ -1 +1,2 @@
runelite.version=${project.version}
rs.version=${rs.version}

View File

@@ -58,6 +58,11 @@
<artifactId>commons-csv</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>21.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View File

@@ -30,6 +30,7 @@ import javax.naming.NamingException;
import javax.sql.DataSource;
import net.runelite.http.api.RuneliteAPI;
import net.runelite.http.service.hiscore.HiscoreService;
import net.runelite.http.service.updatecheck.UpdateCheckService;
import net.runelite.http.service.worlds.WorldsService;
import net.runelite.http.service.xtea.XteaService;
import org.slf4j.Logger;
@@ -46,6 +47,7 @@ public class Service implements SparkApplication
private final JsonTransformer transformer = new JsonTransformer();
private HiscoreService hiscores = new HiscoreService();
private UpdateCheckService updateCheck = new UpdateCheckService(this);
private WorldsService worlds = new WorldsService();
private XteaService xtea = new XteaService(this);
@@ -57,6 +59,7 @@ public class Service implements SparkApplication
xtea.init();
get("/version", (request, response) -> RuneliteAPI.getVersion());
get("/update-check", updateCheck::check, transformer);
get("/hiscore", (request, response) -> hiscores.lookup(request.queryParams("username")), transformer);
get("/worlds", (request, response) -> worlds.listWorlds(), transformer);
post("/xtea", xtea::submit);
@@ -96,6 +99,36 @@ public class Service implements SparkApplication
this.hiscores = hiscores;
}
public UpdateCheckService getUpdateCheck()
{
return updateCheck;
}
public void setUpdateCheck(UpdateCheckService updateCheck)
{
this.updateCheck = updateCheck;
}
public WorldsService getWorlds()
{
return worlds;
}
public void setWorlds(WorldsService worlds)
{
this.worlds = worlds;
}
public XteaService getXtea()
{
return xtea;
}
public void setXtea(XteaService xtea)
{
this.xtea = xtea;
}
public DataSource getDataSource()
{
return dataSource;

View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.http.service.updatecheck;
import com.google.common.base.Suppliers;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import net.runelite.http.api.RuneliteAPI;
import net.runelite.http.api.worlds.WorldResult;
import net.runelite.http.service.Service;
import net.runelite.http.service.worlds.WorldsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Request;
import spark.Response;
public class UpdateCheckService
{
private static final Logger logger = LoggerFactory.getLogger(UpdateCheckService.class);
private static final int PORT = 43594;
private static final int WORLD_OFFSET = 300;
private static final byte HANDSHAKE_TYPE = 15;
private static final int RESPONSE_OK = 0;
private static final int RESPONSE_OUTDATED = 6;
private final Service service;
private final Supplier<Boolean> updateAvailable = Suppliers.memoizeWithExpiration(this::checkUpdate, 1, TimeUnit.MINUTES);
public UpdateCheckService(Service service)
{
this.service = service;
}
public Boolean check(Request request, Response response)
{
return updateAvailable.get();
}
private boolean checkUpdate()
{
int nextWorld = randomWorld();
if (nextWorld == -1)
{
return false;
}
InetAddress address;
try
{
String url = "oldschool" + nextWorld + ".runescape.com";
address = InetAddress.getByName(url);
}
catch (UnknownHostException ex)
{
logger.warn(null, ex);
return false;
}
try (Socket socket = new Socket(address, PORT))
{
ByteBuffer buffer = ByteBuffer.allocate(5);
buffer.put(HANDSHAKE_TYPE);
buffer.putInt(RuneliteAPI.getRsVersion());
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
os.write(buffer.array());
int reply = is.read();
if (reply == RESPONSE_OUTDATED)
{
return true;
}
if (reply != RESPONSE_OK)
{
logger.debug("Non-ok response for handshake: {}", reply);
}
}
catch (IOException ex)
{
logger.warn(null, ex);
}
return false; // no update
}
private int randomWorld()
{
WorldsService worldsService = service.getWorlds();
try
{
WorldResult worlds = worldsService.listWorlds();
int size = worlds.getWorlds().size();
Random rand = new Random();
int worldNumber = worlds.getWorlds().get(rand.nextInt(size)).getId();
return worldNumber - WORLD_OFFSET;
}
catch (IOException | URISyntaxException ex)
{
logger.warn(null, ex);
return -1;
}
}
}