Merge pull request #2244 from open-osrs/project-cleanup
project: remove modules to be externalized
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
description = "Cache Client"
|
||||
|
||||
dependencies {
|
||||
api(project(":cache"))
|
||||
api(project(":protocol"))
|
||||
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.nettyAll)
|
||||
implementation(Libraries.slf4jApi)
|
||||
|
||||
testImplementation(Libraries.junit)
|
||||
testImplementation(Libraries.slf4jSimple)
|
||||
testImplementation(project(path = ":cache", configuration = "testArchives"))
|
||||
}
|
||||
|
||||
tasks {
|
||||
register<JavaExec>("download") {
|
||||
dependsOn(":cache-client:build")
|
||||
|
||||
classpath = project.sourceSets.main.get().runtimeClasspath
|
||||
main = "net.runelite.cache.client.CacheClient"
|
||||
args(listOf(ProjectVersions.rsversion))
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import net.runelite.protocol.api.update.ArchiveResponsePacket;
|
||||
|
||||
public class ArchiveResponseHandler extends SimpleChannelInboundHandler<ArchiveResponsePacket>
|
||||
{
|
||||
private final CacheClient client;
|
||||
|
||||
public ArchiveResponseHandler(CacheClient client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, ArchiveResponsePacket archiveResponse) throws Exception
|
||||
{
|
||||
client.onFileFinish(archiveResponse.getIndex(),
|
||||
archiveResponse.getArchive(),
|
||||
archiveResponse.getData());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,486 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.runelite.cache.fs.Archive;
|
||||
import net.runelite.cache.fs.Index;
|
||||
import net.runelite.cache.fs.Storage;
|
||||
import net.runelite.cache.fs.Store;
|
||||
import net.runelite.cache.index.ArchiveData;
|
||||
import net.runelite.cache.index.IndexData;
|
||||
import net.runelite.protocol.update.decoders.HandshakeResponseDecoder;
|
||||
import net.runelite.protocol.update.encoders.ArchiveRequestEncoder;
|
||||
import net.runelite.protocol.update.encoders.EncryptionEncoder;
|
||||
import net.runelite.protocol.api.update.ArchiveRequestPacket;
|
||||
import net.runelite.protocol.api.login.HandshakeResponseType;
|
||||
import net.runelite.cache.util.Crc32;
|
||||
import net.runelite.protocol.api.handshake.UpdateHandshakePacket;
|
||||
import net.runelite.protocol.handshake.UpdateHandshakeEncoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CacheClient implements AutoCloseable
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(CacheClient.class);
|
||||
|
||||
private static final String HOST = "oldschool1.runescape.com";
|
||||
private static final int PORT = 43594;
|
||||
|
||||
private static final int MAX_REQUESTS = 19; // too many and the server closes the conncetion
|
||||
|
||||
private final Store store; // store cache will be written to
|
||||
private final String host;
|
||||
private final int clientRevision;
|
||||
private DownloadWatcher watcher;
|
||||
|
||||
private ClientState state;
|
||||
|
||||
private final EventLoopGroup group = new NioEventLoopGroup(1);
|
||||
private Channel channel;
|
||||
|
||||
private CompletableFuture<HandshakeResponseType> handshakeFuture;
|
||||
private final Queue<PendingFileRequest> requests = new ArrayDeque<>();
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
Path path = Paths.get(System.getProperty("user.home"), "jagexcache" + File.separator + "oldschool" + File.separator + "LIVE");
|
||||
final File jagexcache = new File(String.valueOf(path));
|
||||
|
||||
jagexcache.mkdirs();
|
||||
|
||||
try (Store store = new Store(jagexcache))
|
||||
{
|
||||
store.load();
|
||||
CacheClient c = new CacheClient(store, Integer.parseInt(args[0]));
|
||||
c.connect();
|
||||
CompletableFuture<HandshakeResponseType> handshake = c.handshake();
|
||||
handshake.get();
|
||||
c.download();
|
||||
c.close();
|
||||
store.save();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public CacheClient(Store store, int clientRevision)
|
||||
{
|
||||
this(store, HOST, clientRevision);
|
||||
}
|
||||
|
||||
public CacheClient(Store store, String host, int clientRevision)
|
||||
{
|
||||
this.store = store;
|
||||
this.host = host;
|
||||
this.clientRevision = clientRevision;
|
||||
}
|
||||
|
||||
public CacheClient(Store store, int clientRevision, DownloadWatcher watcher)
|
||||
{
|
||||
this(store, clientRevision);
|
||||
this.watcher = watcher;
|
||||
}
|
||||
|
||||
public void connect()
|
||||
{
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group)
|
||||
.channel(NioSocketChannel.class)
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.handler(new ChannelInitializer<SocketChannel>()
|
||||
{
|
||||
@Override
|
||||
public void initChannel(SocketChannel ch) throws Exception
|
||||
{
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
|
||||
//p.addFirst(new HttpProxyHandler(new InetSocketAddress("runelite.net", 3128)));
|
||||
p.addLast("decoder", new HandshakeResponseDecoder());
|
||||
|
||||
p.addLast(
|
||||
new CacheClientHandler(),
|
||||
new HandshakeResponseHandler(CacheClient.this),
|
||||
new ArchiveResponseHandler(CacheClient.this)
|
||||
);
|
||||
|
||||
p.addLast(
|
||||
new UpdateHandshakeEncoder(),
|
||||
new EncryptionEncoder(),
|
||||
new ArchiveRequestEncoder()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Start the client.
|
||||
ChannelFuture f = b.connect(host, PORT).syncUninterruptibly();
|
||||
channel = f.channel();
|
||||
}
|
||||
|
||||
public CompletableFuture<HandshakeResponseType> handshake()
|
||||
{
|
||||
UpdateHandshakePacket handshakePacket = new UpdateHandshakePacket();
|
||||
handshakePacket.setRevision(getClientRevision());
|
||||
|
||||
state = ClientState.HANDSHAKING;
|
||||
|
||||
assert handshakeFuture == null;
|
||||
handshakeFuture = new CompletableFuture<>();
|
||||
|
||||
channel.writeAndFlush(handshakePacket);
|
||||
|
||||
logger.info("Sent handshake with revision {}", handshakePacket.getRevision());
|
||||
|
||||
return handshakeFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
channel.close().syncUninterruptibly();
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
|
||||
public int getClientRevision()
|
||||
{
|
||||
return clientRevision;
|
||||
}
|
||||
|
||||
public ClientState getState()
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState(ClientState state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
CompletableFuture<HandshakeResponseType> getHandshakeFuture()
|
||||
{
|
||||
return handshakeFuture;
|
||||
}
|
||||
|
||||
public List<IndexInfo> requestIndexes() throws IOException
|
||||
{
|
||||
logger.info("Requesting indexes");
|
||||
|
||||
FileResult result = requestFile(255, 255, true).join();
|
||||
result.decompress(null);
|
||||
|
||||
ByteBuf buffer = Unpooled.wrappedBuffer(result.getContents());
|
||||
int indexCount = result.getContents().length / 8;
|
||||
List<IndexInfo> indexInfo = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < indexCount; ++i)
|
||||
{
|
||||
int crc = buffer.readInt();
|
||||
int revision = buffer.readInt();
|
||||
indexInfo.add(new IndexInfo(i, crc, revision));
|
||||
}
|
||||
|
||||
return indexInfo;
|
||||
}
|
||||
|
||||
public void download() throws IOException
|
||||
{
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
List<IndexInfo> indexes = requestIndexes();
|
||||
for (IndexInfo indexInfo : indexes)
|
||||
{
|
||||
int i = indexInfo.getId();
|
||||
int crc = indexInfo.getCrc();
|
||||
int revision = indexInfo.getRevision();
|
||||
|
||||
Index index = store.findIndex(i);
|
||||
|
||||
if (index == null)
|
||||
{
|
||||
logger.info("Index {} does not exist, creating", i);
|
||||
}
|
||||
else if (index.getRevision() != revision)
|
||||
{
|
||||
if (revision < index.getRevision())
|
||||
{
|
||||
logger.warn("Index {} revision is going BACKWARDS! (our revision {}, their revision {})", index.getId(), index.getRevision(), revision);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.info("Index {} has the wrong revision (our revision {}, their revision {})", index.getId(), index.getRevision(), revision);
|
||||
}
|
||||
}
|
||||
else if (index.getCrc() != crc)
|
||||
{
|
||||
logger.warn("Index {} CRC has changed! (our crc {}, their crc {})",
|
||||
index.getCrc(), index.getCrc(), crc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// despite the index being up to date, not everything
|
||||
// can be downloaded, eg. for tracks.
|
||||
logger.info("Index {} is up to date", index.getId());
|
||||
}
|
||||
|
||||
logger.info("Downloading index {}", i);
|
||||
|
||||
FileResult indexFileResult = requestFile(255, i, true).join();
|
||||
indexFileResult.decompress(null);
|
||||
|
||||
logger.info("Downloaded index {}", i);
|
||||
|
||||
if (indexFileResult.getCrc() != crc)
|
||||
{
|
||||
logger.warn("Corrupted download for index {}", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
IndexData indexData = new IndexData();
|
||||
indexData.load(indexFileResult.getContents());
|
||||
|
||||
if (index == null)
|
||||
{
|
||||
index = store.addIndex(i);
|
||||
}
|
||||
|
||||
// update index settings
|
||||
index.setProtocol(indexData.getProtocol());
|
||||
index.setNamed(indexData.isNamed());
|
||||
index.setCrc(crc);
|
||||
index.setRevision(revision);
|
||||
|
||||
logger.info("Index {} has {} archives", i, indexData.getArchives().length);
|
||||
|
||||
for (ArchiveData ad : indexData.getArchives())
|
||||
{
|
||||
Archive existing = index.getArchive(ad.getId());
|
||||
|
||||
if (existing != null && existing.getRevision() == ad.getRevision()
|
||||
&& existing.getCrc() == ad.getCrc()
|
||||
&& existing.getNameHash() == ad.getNameHash())
|
||||
{
|
||||
logger.debug("Archive {}/{} in index {} is up to date",
|
||||
ad.getId(), indexData.getArchives().length, index.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
logger.info("Archive {}/{} in index {} is out of date, downloading",
|
||||
ad.getId(), indexData.getArchives().length, index.getId());
|
||||
}
|
||||
else if (ad.getRevision() < existing.getRevision())
|
||||
{
|
||||
logger.warn("Archive {}/{} in index {} revision is going BACKWARDS! (our revision {}, their revision {})",
|
||||
ad.getId(), indexData.getArchives().length, index.getId(),
|
||||
existing.getRevision(), ad.getRevision());
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.info("Archive {}/{} in index {} is out of date, downloading. " +
|
||||
"revision: ours: {} theirs: {}, crc: ours: {} theirs {}, name: ours {} theirs {}",
|
||||
ad.getId(), indexData.getArchives().length, index.getId(),
|
||||
existing.getRevision(), ad.getRevision(),
|
||||
existing.getCrc(), ad.getCrc(),
|
||||
existing.getNameHash(), ad.getNameHash());
|
||||
}
|
||||
|
||||
final Archive archive = existing == null
|
||||
? index.addArchive(ad.getId())
|
||||
: existing;
|
||||
|
||||
archive.setRevision(ad.getRevision());
|
||||
archive.setCrc(ad.getCrc());
|
||||
archive.setNameHash(ad.getNameHash());
|
||||
|
||||
// Add files
|
||||
archive.setFileData(ad.getFiles());
|
||||
|
||||
CompletableFuture<FileResult> future = requestFile(index.getId(), ad.getId(), false);
|
||||
future.handle((fr, ex) ->
|
||||
{
|
||||
byte[] data = fr.getCompressedData();
|
||||
|
||||
Crc32 crc32 = new Crc32();
|
||||
crc32.update(data, 0, data.length);
|
||||
int hash = crc32.getHash();
|
||||
|
||||
if (hash != archive.getCrc())
|
||||
{
|
||||
logger.warn("crc mismatch on downloaded archive {}/{}: {} != {}",
|
||||
archive.getIndex().getId(), archive.getArchiveId(),
|
||||
hash, archive.getCrc());
|
||||
throw new RuntimeException("crc mismatch");
|
||||
}
|
||||
|
||||
if (watcher != null)
|
||||
{
|
||||
watcher.downloadComplete(archive, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
Storage storage = store.getStorage();
|
||||
storage.saveArchive(archive, data);
|
||||
}
|
||||
catch (IOException ex1)
|
||||
{
|
||||
logger.warn("unable to save archive data", ex1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// flush any pending requests
|
||||
channel.flush();
|
||||
|
||||
while (!requests.isEmpty())
|
||||
{
|
||||
// wait for pending requests
|
||||
synchronized (this)
|
||||
{
|
||||
try
|
||||
{
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
logger.warn(null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.stop();
|
||||
logger.info("Download completed in {}", stopwatch);
|
||||
}
|
||||
|
||||
private synchronized CompletableFuture<FileResult> requestFile(int index, int fileId, boolean flush)
|
||||
{
|
||||
if (state != ClientState.CONNECTED)
|
||||
{
|
||||
throw new IllegalStateException("Can't request files until connected!");
|
||||
}
|
||||
|
||||
if (!flush)
|
||||
{
|
||||
while (requests.size() >= MAX_REQUESTS)
|
||||
{
|
||||
channel.flush();
|
||||
|
||||
try
|
||||
{
|
||||
wait();
|
||||
}
|
||||
catch (InterruptedException ex)
|
||||
{
|
||||
logger.warn("interrupted while waiting for requests", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArchiveRequestPacket archiveRequest = new ArchiveRequestPacket();
|
||||
archiveRequest.setPriority(false);
|
||||
archiveRequest.setIndex(index);
|
||||
archiveRequest.setArchive(fileId);
|
||||
|
||||
CompletableFuture<FileResult> future = new CompletableFuture<>();
|
||||
PendingFileRequest pf = new PendingFileRequest(index,
|
||||
fileId, future);
|
||||
|
||||
logger.trace("Sending request for {}/{}", index, fileId);
|
||||
|
||||
requests.add(pf);
|
||||
|
||||
if (!flush)
|
||||
{
|
||||
channel.write(archiveRequest);
|
||||
}
|
||||
else
|
||||
{
|
||||
channel.writeAndFlush(archiveRequest);
|
||||
}
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
private PendingFileRequest findRequest(int index, int file)
|
||||
{
|
||||
for (PendingFileRequest pr : requests)
|
||||
{
|
||||
if (pr.getIndex() == index && pr.getArchive() == file)
|
||||
{
|
||||
return pr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected synchronized void onFileFinish(int index, int file, byte[] compressedData)
|
||||
{
|
||||
PendingFileRequest pr = findRequest(index, file);
|
||||
|
||||
if (pr == null)
|
||||
{
|
||||
logger.warn("File download {}/{} with no pending request", index, file);
|
||||
return;
|
||||
}
|
||||
|
||||
requests.remove(pr);
|
||||
|
||||
notify();
|
||||
|
||||
FileResult result = new FileResult(index, file, compressedData);
|
||||
|
||||
logger.debug("File download finished for index {} file {}, length {}", index, file, compressedData.length);
|
||||
|
||||
pr.getFuture().complete(result);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CacheClientHandler extends ChannelInboundHandlerAdapter
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(CacheClientHandler.class);
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception
|
||||
{
|
||||
logger.warn("Channel has gone inactive");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
|
||||
{
|
||||
// Close the connection when an exception is raised.
|
||||
logger.warn(null, cause);
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
public enum ClientState
|
||||
{
|
||||
HANDSHAKING,
|
||||
CONNECTED
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import net.runelite.cache.fs.Archive;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DownloadWatcher
|
||||
{
|
||||
void downloadComplete(Archive archive, byte[] data);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import net.runelite.cache.fs.Container;
|
||||
|
||||
public class FileResult
|
||||
{
|
||||
private final int index;
|
||||
private final int fileId;
|
||||
private final byte[] compressedData;
|
||||
|
||||
private byte[] contents;
|
||||
private int revision;
|
||||
private int crc;
|
||||
private int compression; // compression method used by archive data
|
||||
|
||||
public FileResult(int index, int fileId, byte[] compressedData)
|
||||
{
|
||||
this.index = index;
|
||||
this.fileId = fileId;
|
||||
this.compressedData = compressedData;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
public int getFileId()
|
||||
{
|
||||
return fileId;
|
||||
}
|
||||
|
||||
public byte[] getCompressedData()
|
||||
{
|
||||
return compressedData;
|
||||
}
|
||||
|
||||
public void decompress(int[] keys) throws IOException
|
||||
{
|
||||
Container res = Container.decompress(compressedData, keys);
|
||||
|
||||
contents = res.data;
|
||||
revision = res.revision;
|
||||
crc = res.crc;
|
||||
compression = res.compression;
|
||||
}
|
||||
|
||||
public byte[] getContents()
|
||||
{
|
||||
return contents;
|
||||
}
|
||||
|
||||
public int getRevision()
|
||||
{
|
||||
return revision;
|
||||
}
|
||||
|
||||
public int getCrc()
|
||||
{
|
||||
return crc;
|
||||
}
|
||||
|
||||
public int getCompression()
|
||||
{
|
||||
return compression;
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.runelite.protocol.update.decoders.ArchiveResponseDecoder;
|
||||
import net.runelite.protocol.api.update.EncryptionPacket;
|
||||
import net.runelite.protocol.api.handshake.HandshakeResponsePacket;
|
||||
import net.runelite.protocol.api.login.HandshakeResponseType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class HandshakeResponseHandler extends SimpleChannelInboundHandler<HandshakeResponsePacket>
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(HandshakeResponseHandler.class);
|
||||
|
||||
private final CacheClient client;
|
||||
|
||||
public HandshakeResponseHandler(CacheClient client)
|
||||
{
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, HandshakeResponsePacket handshakeResponse) throws Exception
|
||||
{
|
||||
Channel channel = ctx.channel();
|
||||
ChannelPipeline p = ctx.pipeline();
|
||||
CompletableFuture<HandshakeResponseType> handshakeFuture = client.getHandshakeFuture();
|
||||
|
||||
assert handshakeFuture != null;
|
||||
|
||||
if (handshakeResponse.getResponse() != HandshakeResponseType.RESPONSE_OK)
|
||||
{
|
||||
logger.warn("Non-ok response from server {}", handshakeResponse.getResponse());
|
||||
handshakeFuture.complete(handshakeResponse.getResponse());
|
||||
ctx.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send encryption packet
|
||||
EncryptionPacket encryptionPacket = new EncryptionPacket();
|
||||
encryptionPacket.setKey((byte) 0);
|
||||
channel.writeAndFlush(encryptionPacket);
|
||||
|
||||
client.setState(ClientState.CONNECTED);
|
||||
|
||||
logger.info("Client is now connected!");
|
||||
|
||||
p.replace("decoder", "decoder", new ArchiveResponseDecoder());
|
||||
|
||||
handshakeFuture.complete(handshakeResponse.getResponse());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
public class IndexInfo
|
||||
{
|
||||
private final int id;
|
||||
private final int crc;
|
||||
private final int revision;
|
||||
|
||||
public IndexInfo(int id, int crc, int revision)
|
||||
{
|
||||
this.id = id;
|
||||
this.crc = crc;
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "IndexInfo{" + "id=" + id + ", crc=" + crc + ", revision=" + revision + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 5;
|
||||
hash = 71 * hash + this.id;
|
||||
hash = 71 * hash + this.crc;
|
||||
hash = 71 * hash + this.revision;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final IndexInfo other = (IndexInfo) obj;
|
||||
if (this.id != other.id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this.crc != other.crc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (this.revision != other.revision)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getCrc()
|
||||
{
|
||||
return crc;
|
||||
}
|
||||
|
||||
public int getRevision()
|
||||
{
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class PendingFileRequest
|
||||
{
|
||||
private final int index;
|
||||
private final int archive;
|
||||
private final CompletableFuture<FileResult> future;
|
||||
|
||||
public PendingFileRequest(int index, int archive, CompletableFuture<FileResult> future)
|
||||
{
|
||||
this.index = index;
|
||||
this.archive = archive;
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
public int getArchive()
|
||||
{
|
||||
return archive;
|
||||
}
|
||||
|
||||
public CompletableFuture<FileResult> getFuture()
|
||||
{
|
||||
return future;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-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.cache.client;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.runelite.cache.CacheProperties;
|
||||
import net.runelite.cache.fs.Store;
|
||||
import net.runelite.protocol.api.login.HandshakeResponseType;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.impl.SimpleLogger;
|
||||
|
||||
public class CacheClientTest
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(CacheClientTest.class);
|
||||
|
||||
@Before
|
||||
public void before()
|
||||
{
|
||||
System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void test() throws Exception
|
||||
{
|
||||
try (Store store = new Store(new File("D:\\rs\\07\\temp\\cache")))
|
||||
{
|
||||
store.load();
|
||||
|
||||
CacheClient c = new CacheClient(store, CacheProperties.getRsVersion());
|
||||
c.connect();
|
||||
CompletableFuture<HandshakeResponseType> handshake = c.handshake();
|
||||
|
||||
HandshakeResponseType result = handshake.get();
|
||||
logger.info("Handshake result: {}", result);
|
||||
|
||||
Assert.assertEquals(HandshakeResponseType.RESPONSE_OK, result);
|
||||
|
||||
c.download();
|
||||
|
||||
c.close();
|
||||
|
||||
store.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
description = "Cache Updater"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
compileOnly(Libraries.lombok)
|
||||
|
||||
implementation(Libraries.minio)
|
||||
implementation(Libraries.mysqlConnectorJava)
|
||||
implementation(Libraries.springbootDevtools)
|
||||
implementation(Libraries.springbootStarter)
|
||||
implementation(Libraries.springbootStarterJdbc)
|
||||
implementation(Libraries.sql2o)
|
||||
implementation(project(":cache-client"))
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
-- MySQL dump 10.16 Distrib 10.2.9-MariaDB, for Linux (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: cache
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.2.9-MariaDB
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `archive`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `archive`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `archive` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`archiveId` int(11) NOT NULL,
|
||||
`nameHash` int(11) NOT NULL,
|
||||
`crc` int(11) NOT NULL,
|
||||
`revision` int(11) NOT NULL,
|
||||
`hash` binary(32) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `archive_revision` (`archiveId`,`revision`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `cache`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `cache`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `cache` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`revision` int(11) NOT NULL,
|
||||
`date` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `revision_date` (`revision`,`date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `file`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `file`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `file` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`archive` int(11) NOT NULL,
|
||||
`fileId` int(11) NOT NULL,
|
||||
`nameHash` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `archive_file` (`archive`,`fileId`),
|
||||
CONSTRAINT `file_ibfk_1` FOREIGN KEY (`archive`) REFERENCES `archive` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `index`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `index`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `index` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cache` int(11) NOT NULL,
|
||||
`indexId` int(11) NOT NULL,
|
||||
`crc` int(11) NOT NULL,
|
||||
`revision` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `indexId` (`cache`,`indexId`,`revision`,`crc`) USING BTREE,
|
||||
CONSTRAINT `index_ibfk_1` FOREIGN KEY (`cache`) REFERENCES `cache` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `index_archive`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `index_archive`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `index_archive` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`index` int(11) NOT NULL,
|
||||
`archive` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_index_archive` (`index`,`archive`) USING BTREE,
|
||||
KEY `archive` (`archive`) USING BTREE,
|
||||
CONSTRAINT `index_archive_ibfk_1` FOREIGN KEY (`index`) REFERENCES `index` (`id`),
|
||||
CONSTRAINT `index_archive_ibfk_2` FOREIGN KEY (`archive`) REFERENCES `archive` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2018-02-02 21:55:48
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.cache.updater;
|
||||
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.errors.InvalidEndpointException;
|
||||
import io.minio.errors.InvalidPortException;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.sql.DataSource;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.sql2o.Sql2o;
|
||||
import org.sql2o.converters.Converter;
|
||||
import org.sql2o.quirks.NoQuirks;
|
||||
|
||||
@Configuration
|
||||
public class CacheConfiguration
|
||||
{
|
||||
@Value("${minio.endpoint}")
|
||||
private String minioUrl;
|
||||
|
||||
@Value("${minio.accesskey}")
|
||||
private String minioAccessKey;
|
||||
|
||||
@Value("${minio.secretkey}")
|
||||
private String minioSecretKey;
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "datasource.runelite-cache")
|
||||
public DataSource dataSource()
|
||||
{
|
||||
return DataSourceBuilder.create().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("Runelite Cache SQL2O")
|
||||
public Sql2o sql2o(DataSource dataSource)
|
||||
{
|
||||
Map<Class, Converter> converters = new HashMap<>();
|
||||
converters.put(Instant.class, new InstantConverter());
|
||||
return new Sql2o(dataSource, new NoQuirks(converters));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MinioClient minioClient() throws InvalidEndpointException, InvalidPortException
|
||||
{
|
||||
return new MinioClient(minioUrl, minioAccessKey, minioSecretKey);
|
||||
}
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import net.runelite.cache.updater.beans.ArchiveEntry;
|
||||
import net.runelite.cache.updater.beans.CacheEntry;
|
||||
import net.runelite.cache.updater.beans.IndexEntry;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Query;
|
||||
import org.sql2o.ResultSetIterable;
|
||||
|
||||
class CacheDAO
|
||||
{
|
||||
// cache prepared statements for high volume queries
|
||||
private Query associateArchive;
|
||||
private Query findArchive, insertArchive;
|
||||
private Query associateFile;
|
||||
|
||||
public CacheEntry findMostRecent(Connection con)
|
||||
{
|
||||
return con.createQuery("select id, revision, date from cache order by revision desc, date desc limit 1")
|
||||
.executeAndFetchFirst(CacheEntry.class);
|
||||
}
|
||||
|
||||
public List<IndexEntry> findIndexesForCache(Connection con, CacheEntry cache)
|
||||
{
|
||||
return con.createQuery("select id, indexId, crc, revision from `index` where cache = :cache")
|
||||
.addParameter("cache", cache.getId())
|
||||
.executeAndFetch(IndexEntry.class);
|
||||
}
|
||||
|
||||
public ResultSetIterable<ArchiveEntry> findArchivesForIndex(Connection con, IndexEntry indexEntry)
|
||||
{
|
||||
return con.createQuery("select archive.id, archive.archiveId, archive.nameHash,"
|
||||
+ " archive.crc, archive.revision, archive.hash from index_archive "
|
||||
+ "join archive on index_archive.archive = archive.id "
|
||||
+ "where index_archive.index = :id")
|
||||
.addParameter("id", indexEntry.getId())
|
||||
.executeAndFetchLazy(ArchiveEntry.class);
|
||||
}
|
||||
|
||||
public CacheEntry createCache(Connection con, int revision, Instant date)
|
||||
{
|
||||
int cacheId = con.createQuery("insert into cache (revision, date) values (:revision, :date)")
|
||||
.addParameter("revision", revision)
|
||||
.addParameter("date", date)
|
||||
.executeUpdate()
|
||||
.getKey(int.class);
|
||||
|
||||
CacheEntry entry = new CacheEntry();
|
||||
entry.setId(cacheId);
|
||||
entry.setRevision(revision);
|
||||
entry.setDate(date);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public IndexEntry createIndex(Connection con, CacheEntry cache, int indexId, int crc, int revision)
|
||||
{
|
||||
int id = con.createQuery("insert into `index` (cache, indexId, crc, revision) values (:cache, :indexId, :crc, :revision)")
|
||||
.addParameter("cache", cache.getId())
|
||||
.addParameter("indexId", indexId)
|
||||
.addParameter("crc", crc)
|
||||
.addParameter("revision", revision)
|
||||
.executeUpdate()
|
||||
.getKey(int.class);
|
||||
|
||||
IndexEntry entry = new IndexEntry();
|
||||
entry.setId(id);
|
||||
entry.setIndexId(indexId);
|
||||
entry.setCrc(crc);
|
||||
entry.setRevision(revision);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void associateArchiveToIndex(Connection con, ArchiveEntry archive, IndexEntry index)
|
||||
{
|
||||
if (associateArchive == null)
|
||||
{
|
||||
associateArchive = con.createQuery("insert into index_archive (`index`, archive) values (:index, :archive)");
|
||||
}
|
||||
associateArchive
|
||||
.addParameter("index", index.getId())
|
||||
.addParameter("archive", archive.getId())
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
public ArchiveEntry findArchive(Connection con, IndexEntry index,
|
||||
int archiveId, int nameHash, int crc, int revision)
|
||||
{
|
||||
if (findArchive == null)
|
||||
{
|
||||
findArchive = con.createQuery("select distinct archive.id, archive.archiveId, archive.nameHash,"
|
||||
+ " archive.crc, archive.revision, archive.hash from archive "
|
||||
+ " join index_archive on index_archive.archive = archive.id"
|
||||
+ " join `index` on index.id = index_archive.index"
|
||||
+ " where archive.archiveId = :archiveId"
|
||||
+ " and archive.nameHash = :nameHash"
|
||||
+ " and archive.crc = :crc"
|
||||
+ " and archive.revision = :revision"
|
||||
+ " and index.indexId = :indexId");
|
||||
}
|
||||
|
||||
ArchiveEntry entry = findArchive
|
||||
.addParameter("archiveId", archiveId)
|
||||
.addParameter("nameHash", nameHash)
|
||||
.addParameter("crc", crc)
|
||||
.addParameter("revision", revision)
|
||||
.addParameter("indexId", index.getIndexId())
|
||||
.executeAndFetchFirst(ArchiveEntry.class);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public ArchiveEntry createArchive(Connection con, IndexEntry index,
|
||||
int archiveId, int nameHash, int crc, int revision, byte[] hash)
|
||||
{
|
||||
if (insertArchive == null)
|
||||
{
|
||||
insertArchive = con.createQuery("insert into archive (archiveId, nameHash, crc, revision, hash) values "
|
||||
+ "(:archiveId, :nameHash, :crc, :revision, :hash)");
|
||||
}
|
||||
|
||||
int id = insertArchive
|
||||
.addParameter("archiveId", archiveId)
|
||||
.addParameter("nameHash", nameHash)
|
||||
.addParameter("crc", crc)
|
||||
.addParameter("revision", revision)
|
||||
.addParameter("hash", hash)
|
||||
.executeUpdate()
|
||||
.getKey(int.class);
|
||||
|
||||
ArchiveEntry entry = new ArchiveEntry();
|
||||
entry.setId(id);
|
||||
entry.setArchiveId(archiveId);
|
||||
entry.setNameHash(nameHash);
|
||||
entry.setCrc(crc);
|
||||
entry.setRevision(revision);
|
||||
entry.setHash(hash);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void associateFileToArchive(Connection con, ArchiveEntry archive, int fileId, int nameHash)
|
||||
{
|
||||
if (associateFile == null)
|
||||
{
|
||||
associateFile = con.createQuery("insert into file (archive, fileId, nameHash) values (:archive, :fileId, :nameHash)");
|
||||
}
|
||||
associateFile
|
||||
.addParameter("archive", archive.getId())
|
||||
.addParameter("fileId", fileId)
|
||||
.addParameter("nameHash", nameHash)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import net.runelite.cache.fs.Archive;
|
||||
import net.runelite.cache.fs.Index;
|
||||
import net.runelite.cache.fs.Storage;
|
||||
import net.runelite.cache.fs.Store;
|
||||
import net.runelite.cache.index.FileData;
|
||||
import net.runelite.cache.updater.beans.ArchiveEntry;
|
||||
import net.runelite.cache.updater.beans.CacheEntry;
|
||||
import net.runelite.cache.updater.beans.IndexEntry;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.ResultSetIterable;
|
||||
|
||||
public class CacheStorage implements Storage
|
||||
{
|
||||
private CacheEntry cacheEntry;
|
||||
private final CacheDAO cacheDao;
|
||||
private final Connection con;
|
||||
|
||||
public CacheStorage(CacheEntry cacheEntry, CacheDAO cacheDao, Connection con)
|
||||
{
|
||||
this.cacheEntry = cacheEntry;
|
||||
this.cacheDao = cacheDao;
|
||||
this.con = con;
|
||||
}
|
||||
|
||||
public CacheEntry getCacheEntry()
|
||||
{
|
||||
return cacheEntry;
|
||||
}
|
||||
|
||||
public void setCacheEntry(CacheEntry cacheEntry)
|
||||
{
|
||||
this.cacheEntry = cacheEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Store store) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Store store) throws IOException
|
||||
{
|
||||
List<IndexEntry> indexes = cacheDao.findIndexesForCache(con, cacheEntry);
|
||||
for (IndexEntry indexEntry : indexes)
|
||||
{
|
||||
Index index = store.addIndex(indexEntry.getIndexId());
|
||||
index.setCrc(indexEntry.getCrc());
|
||||
index.setRevision(indexEntry.getRevision());
|
||||
|
||||
try (ResultSetIterable<ArchiveEntry> archives = cacheDao.findArchivesForIndex(con, indexEntry))
|
||||
{
|
||||
for (ArchiveEntry archiveEntry : archives)
|
||||
{
|
||||
if (index.getArchive(archiveEntry.getArchiveId()) != null)
|
||||
{
|
||||
throw new IOException("Duplicate archive " + archiveEntry + " on " + indexEntry);
|
||||
}
|
||||
|
||||
Archive archive = index.addArchive(archiveEntry.getArchiveId());
|
||||
archive.setNameHash(archiveEntry.getNameHash());
|
||||
archive.setCrc(archiveEntry.getCrc());
|
||||
archive.setRevision(archiveEntry.getRevision());
|
||||
archive.setHash(archiveEntry.getHash());
|
||||
|
||||
// File data is not necessary for cache updating
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Store store) throws IOException
|
||||
{
|
||||
for (Index index : store.getIndexes())
|
||||
{
|
||||
IndexEntry entry = cacheDao.createIndex(con, cacheEntry, index.getId(), index.getCrc(), index.getRevision());
|
||||
|
||||
for (Archive archive : index.getArchives())
|
||||
{
|
||||
ArchiveEntry archiveEntry = cacheDao.findArchive(con, entry, archive.getArchiveId(),
|
||||
archive.getNameHash(), archive.getCrc(), archive.getRevision());
|
||||
if (archiveEntry == null)
|
||||
{
|
||||
byte[] hash = archive.getHash();
|
||||
archiveEntry = cacheDao.createArchive(con, entry, archive.getArchiveId(),
|
||||
archive.getNameHash(), archive.getCrc(), archive.getRevision(), hash);
|
||||
|
||||
for (FileData file : archive.getFileData())
|
||||
{
|
||||
cacheDao.associateFileToArchive(con, archiveEntry, file.getId(), file.getNameHash());
|
||||
}
|
||||
}
|
||||
|
||||
cacheDao.associateArchiveToIndex(con, archiveEntry, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] loadArchive(Archive archive) throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveArchive(Archive archive, byte[] data) throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater;
|
||||
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.errors.InvalidEndpointException;
|
||||
import io.minio.errors.InvalidPortException;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.runelite.cache.client.CacheClient;
|
||||
import net.runelite.cache.client.IndexInfo;
|
||||
import net.runelite.cache.fs.Archive;
|
||||
import net.runelite.cache.fs.Store;
|
||||
import net.runelite.cache.updater.beans.CacheEntry;
|
||||
import net.runelite.cache.updater.beans.IndexEntry;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.protocol.api.login.HandshakeResponseType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CacheUpdater implements CommandLineRunner
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(CacheUpdater.class);
|
||||
|
||||
private final Sql2o sql2o;
|
||||
private final MinioClient minioClient;
|
||||
|
||||
@Value("${minio.bucket}")
|
||||
private String minioBucket;
|
||||
|
||||
@Autowired
|
||||
public CacheUpdater(
|
||||
@Qualifier("Runelite Cache SQL2O") Sql2o sql2o,
|
||||
MinioClient minioClient
|
||||
)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
this.minioClient = minioClient;
|
||||
}
|
||||
|
||||
public void update() throws IOException, InvalidEndpointException, InvalidPortException, InterruptedException
|
||||
{
|
||||
int rsVersion = RuneLiteAPI.getRsVersion();
|
||||
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
CacheEntry cache = cacheDao.findMostRecent(con);
|
||||
boolean created = false;
|
||||
if (cache == null)
|
||||
{
|
||||
created = true;
|
||||
cache = cacheDao.createCache(con, rsVersion, Instant.now());
|
||||
}
|
||||
|
||||
CacheStorage storage = new CacheStorage(cache, cacheDao, con);
|
||||
Store store = new Store(storage);
|
||||
store.load();
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
CacheClient client = new CacheClient(store, rsVersion,
|
||||
(Archive archive, byte[] data) -> executor.submit(new CacheUploader(minioClient, minioBucket, archive, data)));
|
||||
|
||||
client.connect();
|
||||
HandshakeResponseType result = client.handshake().join();
|
||||
|
||||
if (result != HandshakeResponseType.RESPONSE_OK)
|
||||
{
|
||||
logger.warn("Out of date!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<IndexInfo> indexes = client.requestIndexes();
|
||||
List<IndexEntry> entries = cacheDao.findIndexesForCache(con, cache);
|
||||
|
||||
if (!checkOutOfDate(indexes, entries))
|
||||
{
|
||||
logger.info("All up to date.");
|
||||
return;
|
||||
}
|
||||
|
||||
client.download();
|
||||
|
||||
CacheEntry newCache = created ? cache : cacheDao.createCache(con, rsVersion, Instant.now());
|
||||
|
||||
storage.setCacheEntry(newCache);
|
||||
store.save();
|
||||
|
||||
// ensure objects are added to the store before they become
|
||||
// visible in the database
|
||||
executor.shutdown();
|
||||
while (!executor.awaitTermination(1, TimeUnit.SECONDS))
|
||||
{
|
||||
logger.debug("Waiting for termination of executor...");
|
||||
}
|
||||
|
||||
// commit database
|
||||
con.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkOutOfDate(List<IndexInfo> indexes, List<IndexEntry> dbIndexes)
|
||||
{
|
||||
if (indexes.size() != dbIndexes.size())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < indexes.size(); ++i)
|
||||
{
|
||||
IndexInfo ii = indexes.get(i);
|
||||
IndexEntry ie = dbIndexes.get(i);
|
||||
|
||||
if (ii.getId() != ie.getIndexId()
|
||||
|| ii.getRevision() != ie.getRevision()
|
||||
|| ii.getCrc() != ie.getCrc())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(CacheUpdater.class, args).close();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.errors.ErrorResponseException;
|
||||
import io.minio.errors.InsufficientDataException;
|
||||
import io.minio.errors.InternalException;
|
||||
import io.minio.errors.InvalidArgumentException;
|
||||
import io.minio.errors.InvalidBucketNameException;
|
||||
import io.minio.errors.InvalidResponseException;
|
||||
import io.minio.errors.NoResponseException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import net.runelite.cache.fs.Archive;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
public class CacheUploader implements Runnable
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(CacheUploader.class);
|
||||
|
||||
private final MinioClient minioClient;
|
||||
private final String minioBucket;
|
||||
private final Archive archive;
|
||||
private final byte[] data;
|
||||
|
||||
public CacheUploader(MinioClient minioClient, String minioBucket, Archive archive, byte[] data)
|
||||
{
|
||||
this.minioClient = minioClient;
|
||||
this.minioBucket = minioBucket;
|
||||
this.archive = archive;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
byte[] hash = Hashing.sha256().hashBytes(data).asBytes();
|
||||
String hashStr = BaseEncoding.base16().encode(hash);
|
||||
|
||||
archive.setHash(hash);
|
||||
|
||||
String path = new StringBuilder()
|
||||
.append(hashStr, 0, 2)
|
||||
.append('/')
|
||||
.append(hashStr.substring(2))
|
||||
.toString();
|
||||
|
||||
try
|
||||
{
|
||||
try (InputStream in = minioClient.getObject(minioBucket, path))
|
||||
{
|
||||
return; // already exists
|
||||
}
|
||||
catch (ErrorResponseException ex)
|
||||
{
|
||||
// doesn't exist
|
||||
}
|
||||
|
||||
minioClient.putObject(minioBucket, path, new ByteArrayInputStream(data), data.length, "binary/octet-stream");
|
||||
}
|
||||
catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidArgumentException | InvalidBucketNameException | NoResponseException | IOException | InvalidKeyException | NoSuchAlgorithmException | XmlPullParserException | InvalidResponseException ex)
|
||||
{
|
||||
logger.warn("unable to upload data to store", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 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 HOLDER 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.cache.updater;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import org.sql2o.converters.Converter;
|
||||
import org.sql2o.converters.ConverterException;
|
||||
|
||||
public class InstantConverter implements Converter<Instant>
|
||||
{
|
||||
@Override
|
||||
public Instant convert(Object val) throws ConverterException
|
||||
{
|
||||
Timestamp ts = (Timestamp) val;
|
||||
return ts.toInstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toDatabaseParam(Instant val)
|
||||
{
|
||||
return Timestamp.from(val);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater.beans;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ArchiveEntry
|
||||
{
|
||||
private int id;
|
||||
private int archiveId;
|
||||
private int nameHash;
|
||||
private int crc;
|
||||
private int revision;
|
||||
private byte[] hash;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater.beans;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CacheEntry
|
||||
{
|
||||
private int id;
|
||||
private int revision;
|
||||
private Instant date;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater.beans;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FileEntry
|
||||
{
|
||||
private int id;
|
||||
private int archiveId;
|
||||
private int fileId;
|
||||
private int nameHash;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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.cache.updater.beans;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class IndexEntry
|
||||
{
|
||||
private int id;
|
||||
private int indexId;
|
||||
private int crc;
|
||||
private int revision;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
# Database
|
||||
datasource:
|
||||
runelite-cache:
|
||||
driverClassName: com.mysql.jdbc.Driver
|
||||
type: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
|
||||
url: jdbc:mysql://localhost/runelite-cache
|
||||
username: runelite
|
||||
password: runelite
|
||||
|
||||
# Minio client storage for cache
|
||||
minio:
|
||||
endpoint: http://localhost:9000
|
||||
accesskey: AM54M27O4WZK65N6F8IP
|
||||
secretkey: /PZCxzmsJzwCHYlogcymuprniGCaaLUOET2n6yMP
|
||||
bucket: runelite
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
war
|
||||
}
|
||||
|
||||
description = "Web Service OpenOSRS"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
api(project(":cache"))
|
||||
api(project(":http-api"))
|
||||
api(project(":http-service"))
|
||||
|
||||
implementation(Libraries.gson)
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.okhttp3)
|
||||
implementation(Libraries.springbootJdbc)
|
||||
implementation(Libraries.springbootDevtools)
|
||||
implementation(Libraries.springbootStarterWeb)
|
||||
implementation(Libraries.sql2o)
|
||||
implementation(Libraries.jedis) {
|
||||
exclude(module = "commons-pool2")
|
||||
}
|
||||
|
||||
providedCompile(Libraries.mariadbJdbc)
|
||||
providedCompile(Libraries.lombok)
|
||||
providedCompile(Libraries.springbootStarterTomcat)
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import com.google.common.base.Strings;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.sql.DataSource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.service.util.InstantConverter;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.slf4j.ILoggerFactory;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.sql2o.Sql2o;
|
||||
import org.sql2o.converters.Converter;
|
||||
import org.sql2o.quirks.NoQuirks;
|
||||
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
@EnableScheduling
|
||||
@Slf4j
|
||||
public class SpringBootWebApplication extends SpringBootServletInitializer
|
||||
{
|
||||
@Bean
|
||||
protected ServletContextListener listener()
|
||||
{
|
||||
return new ServletContextListener()
|
||||
{
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
log.info("OpenOSRS API started");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
// Destroy okhttp client
|
||||
OkHttpClient client = RuneLiteAPI.CLIENT;
|
||||
client.dispatcher().executorService().shutdown();
|
||||
client.connectionPool().evictAll();
|
||||
try
|
||||
{
|
||||
Cache cache = client.cache();
|
||||
if (cache != null)
|
||||
{
|
||||
cache.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn(null, ex);
|
||||
}
|
||||
|
||||
log.info("OpenOSRS API stopped");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.openosrs")
|
||||
@Bean("dataSourceRuneLite")
|
||||
public DataSourceProperties dataSourceProperties()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.openosrs-cache")
|
||||
@Bean("dataSourceRuneLiteCache")
|
||||
public DataSourceProperties dataSourcePropertiesCache()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.openosrs-tracker")
|
||||
@Bean("dataSourceRuneLiteTracker")
|
||||
public DataSourceProperties dataSourcePropertiesTracker()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@Bean(value = "runelite", destroyMethod = "")
|
||||
public DataSource runeliteDataSource(@Qualifier("dataSourceRuneLite") DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
return getDataSource(dataSourceProperties);
|
||||
}
|
||||
|
||||
@Bean(value = "runelite-cache2", destroyMethod = "")
|
||||
public DataSource runeliteCache2DataSource(@Qualifier("dataSourceRuneLiteCache") DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
return getDataSource(dataSourceProperties);
|
||||
}
|
||||
|
||||
@Bean(value = "runelite-tracker", destroyMethod = "")
|
||||
public DataSource runeliteTrackerDataSource(@Qualifier("dataSourceRuneLiteTracker") DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
return getDataSource(dataSourceProperties);
|
||||
}
|
||||
|
||||
@Bean("Runelite SQL2O")
|
||||
public Sql2o sql2o(@Qualifier("runelite") DataSource dataSource)
|
||||
{
|
||||
return createSql2oFromDataSource(dataSource);
|
||||
}
|
||||
|
||||
@Bean("Runelite Cache SQL2O")
|
||||
public Sql2o cacheSql2o(@Qualifier("runelite-cache2") DataSource dataSource)
|
||||
{
|
||||
return createSql2oFromDataSource(dataSource);
|
||||
}
|
||||
|
||||
@Bean("Runelite XP Tracker SQL2O")
|
||||
public Sql2o trackerSql2o(@Qualifier("runelite-tracker") DataSource dataSource)
|
||||
{
|
||||
return createSql2oFromDataSource(dataSource);
|
||||
}
|
||||
|
||||
private static DataSource getDataSource(DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
if (!Strings.isNullOrEmpty(dataSourceProperties.getJndiName()))
|
||||
{
|
||||
// Use JNDI provided datasource, which is already configured with pooling
|
||||
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
|
||||
return dataSourceLookup.getDataSource(dataSourceProperties.getJndiName());
|
||||
}
|
||||
else
|
||||
{
|
||||
return dataSourceProperties.initializeDataSourceBuilder().build();
|
||||
}
|
||||
}
|
||||
|
||||
private static Sql2o createSql2oFromDataSource(final DataSource dataSource)
|
||||
{
|
||||
final Map<Class, Converter> converters = new HashMap<>();
|
||||
converters.put(Instant.class, new InstantConverter());
|
||||
return new Sql2o(dataSource, new NoQuirks(converters));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
|
||||
{
|
||||
return application.sources(SpringBootWebApplication.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException
|
||||
{
|
||||
super.onStartup(servletContext);
|
||||
ILoggerFactory loggerFactory = StaticLoggerBinder.getSingleton().getLoggerFactory();
|
||||
if (loggerFactory instanceof LoggerContext)
|
||||
{
|
||||
LoggerContext loggerContext = (LoggerContext) loggerFactory;
|
||||
loggerContext.setPackagingDataEnabled(false);
|
||||
log.debug("Disabling logback packaging data");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(SpringBootWebApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
public class SpringWebMvcConfigurer implements WebMvcConfigurer
|
||||
{
|
||||
/**
|
||||
* Configure .js as application/json to trick Cloudflare into caching json responses
|
||||
*/
|
||||
@Override
|
||||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
|
||||
{
|
||||
configurer.mediaType("js", MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use GSON instead of Jackson for JSON serialization
|
||||
* @param converters
|
||||
*/
|
||||
@Override
|
||||
public void extendMessageConverters(List<HttpMessageConverter<?>> converters)
|
||||
{
|
||||
// Could not figure out a better way to force GSON
|
||||
converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
|
||||
|
||||
GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
|
||||
gsonHttpMessageConverter.setGson(RuneLiteAPI.GSON);
|
||||
converters.add(gsonHttpMessageConverter);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.animation;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
class AnimationCache
|
||||
{
|
||||
private int npcid;
|
||||
private int anim1;
|
||||
private int anim2;
|
||||
private int anim3;
|
||||
private int anim4;
|
||||
private int anim5;
|
||||
private int anim6;
|
||||
private int anim7;
|
||||
private int anim8;
|
||||
private int anim9;
|
||||
private int anim10;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.animation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import net.runelite.http.api.animation.AnimationKey;
|
||||
import net.runelite.http.api.animation.AnimationRequest;
|
||||
import net.runelite.http.service.util.exception.NotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/animation")
|
||||
public class AnimationController
|
||||
{
|
||||
@Autowired
|
||||
private AnimationEndpoint animationService;
|
||||
|
||||
@RequestMapping(method = POST)
|
||||
public void submit(@RequestBody AnimationRequest animationRequest)
|
||||
{
|
||||
animationService.submit(animationRequest);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<AnimationKey> get()
|
||||
{
|
||||
return animationService.get().stream()
|
||||
.map(AnimationController::entryToKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("/{npcid}")
|
||||
public AnimationKey getRegion(@PathVariable int npcid)
|
||||
{
|
||||
AnimationEntry animationEntry = animationService.getNPC(npcid);
|
||||
if (animationEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return entryToKey(animationEntry);
|
||||
}
|
||||
|
||||
private static AnimationKey entryToKey(AnimationEntry xe)
|
||||
{
|
||||
AnimationKey animationKey = new AnimationKey();
|
||||
animationKey.setNPCId(xe.getNPCId());
|
||||
animationKey.setAnimations(new int[]
|
||||
{
|
||||
xe.getAnimations()[0],
|
||||
xe.getAnimations()[1],
|
||||
xe.getAnimations()[2],
|
||||
xe.getAnimations()[3],
|
||||
xe.getAnimations()[4],
|
||||
xe.getAnimations()[5],
|
||||
xe.getAnimations()[6],
|
||||
xe.getAnimations()[7],
|
||||
xe.getAnimations()[8],
|
||||
xe.getAnimations()[9],
|
||||
});
|
||||
return animationKey;
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* 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.animation;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.animation.AnimationKey;
|
||||
import net.runelite.http.api.animation.AnimationRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Query;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@Service
|
||||
public class AnimationEndpoint
|
||||
{
|
||||
private static final String CREATE_SQL = "CREATE TABLE IF NOT EXISTS `animation` (\n"
|
||||
+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"
|
||||
+ " `npcid` int(11) NOT NULL,\n"
|
||||
+ " `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n"
|
||||
+ " `rev` int(11) NOT NULL,\n"
|
||||
+ " `anim1` int(11) NOT NULL,\n"
|
||||
+ " `anim2` int(11),\n"
|
||||
+ " `anim3` int(11),\n"
|
||||
+ " `anim4` int(11),\n"
|
||||
+ " `anim5` int(11),\n"
|
||||
+ " `anim6` int(11),\n"
|
||||
+ " `anim7` int(11),\n"
|
||||
+ " `anim8` int(11),\n"
|
||||
+ " `anim9` int(11),\n"
|
||||
+ " `anim10` int(11),\n"
|
||||
+ " PRIMARY KEY (`id`),\n"
|
||||
+ " KEY `npcid` (`npcid`,`time`)\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
private final Sql2o sql2o;
|
||||
|
||||
private final Cache<Integer, AnimationCache> keyCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(1024)
|
||||
.build();
|
||||
|
||||
@Autowired
|
||||
public AnimationEndpoint(
|
||||
@Qualifier("Runelite SQL2O") Sql2o sql2o
|
||||
)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
con.createQuery(CREATE_SQL)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private AnimationEntry findLatestAnimations(Connection con, int npcid)
|
||||
{
|
||||
return con.createQuery("select npcid, time, anim1, anim2, anim3, anim4, anim5, anim6, anim7, anim8, anim9, anim10 from animation "
|
||||
+ "where npcid = :npcid "
|
||||
+ "order by time desc "
|
||||
+ "limit 1")
|
||||
.addParameter("npcid", npcid)
|
||||
.executeAndFetchFirst(AnimationEntry.class);
|
||||
}
|
||||
|
||||
public void submit(AnimationRequest animationRequest)
|
||||
{
|
||||
boolean cached = true;
|
||||
for (AnimationKey key : animationRequest.getKeys())
|
||||
{
|
||||
int npcid = key.getNPCId();
|
||||
int[] animations = key.getAnimations();
|
||||
|
||||
AnimationCache animationCache = keyCache.getIfPresent(npcid);
|
||||
if (animationCache == null
|
||||
|| animationCache.getAnim1() != animations[0]
|
||||
|| animationCache.getAnim2() != animations[1]
|
||||
|| animationCache.getAnim3() != animations[2]
|
||||
|| animationCache.getAnim4() != animations[3]
|
||||
|| animationCache.getAnim5() != animations[4]
|
||||
|| animationCache.getAnim6() != animations[5]
|
||||
|| animationCache.getAnim7() != animations[6]
|
||||
|| animationCache.getAnim8() != animations[7]
|
||||
|| animationCache.getAnim9() != animations[8]
|
||||
|| animationCache.getAnim10() != animations[9])
|
||||
{
|
||||
cached = false;
|
||||
keyCache.put(npcid, new AnimationCache(npcid, animations[0], animations[1], animations[2], animations[3], animations[4], animations[5], animations[6], animations[7], animations[8], animations[9]));
|
||||
}
|
||||
}
|
||||
|
||||
if (cached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
Query query = null;
|
||||
|
||||
for (AnimationKey key : animationRequest.getKeys())
|
||||
{
|
||||
int npcid = key.getNPCId();
|
||||
int[] animations = key.getAnimations();
|
||||
|
||||
AnimationEntry animationEntry = findLatestAnimations(con, npcid);
|
||||
|
||||
if (animations.length != 10)
|
||||
{
|
||||
throw new IllegalArgumentException("Key length must be 10");
|
||||
}
|
||||
|
||||
// already have these?
|
||||
if (animationEntry != null
|
||||
&& animationEntry.getAnimations()[0] == animations[0]
|
||||
&& animationEntry.getAnimations()[1] == animations[1]
|
||||
&& animationEntry.getAnimations()[2] == animations[2]
|
||||
&& animationEntry.getAnimations()[3] == animations[3]
|
||||
&& animationEntry.getAnimations()[4] == animations[4]
|
||||
&& animationEntry.getAnimations()[5] == animations[5]
|
||||
&& animationEntry.getAnimations()[6] == animations[6]
|
||||
&& animationEntry.getAnimations()[7] == animations[7]
|
||||
&& animationEntry.getAnimations()[8] == animations[8]
|
||||
&& animationEntry.getAnimations()[9] == animations[9])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (query == null)
|
||||
{
|
||||
query = con.createQuery("insert into animation (npcid, rev, anim1, anim2, anim3, anim4, anim5, anim6, anim7, anim8, anim9, anim10) "
|
||||
+ "values (:npcid, :rev, :anim1, :anim2, :anim3, :anim4, anim5, anim6, anim7, anim8, anim9, anim10)");
|
||||
}
|
||||
|
||||
query.addParameter("npcid", npcid)
|
||||
.addParameter("rev", animationRequest.getRevision())
|
||||
.addParameter("anim1", animations[0])
|
||||
.addParameter("anim2", animations[1])
|
||||
.addParameter("anim3", animations[2])
|
||||
.addParameter("anim4", animations[3])
|
||||
.addParameter("anim5", animations[4])
|
||||
.addParameter("anim6", animations[5])
|
||||
.addParameter("anim7", animations[6])
|
||||
.addParameter("anim8", animations[7])
|
||||
.addParameter("anim9", animations[8])
|
||||
.addParameter("anim10", animations[9])
|
||||
.addToBatch();
|
||||
}
|
||||
|
||||
if (query != null)
|
||||
{
|
||||
query.executeBatch();
|
||||
con.commit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<AnimationEntry> get()
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
return con.createQuery(
|
||||
"select t1.npcid, t2.time, t2.rev, t2.anim1, t2.anim2, t2.anim3, t2.anim4, t2.anim5, t2.anim6, t2.anim7, t2.anim8, t2.anim9, t2.anim10 from " +
|
||||
"(select npcid,max(id) as id from animation group by npcid) t1 " +
|
||||
"join animation t2 on t1.id = t2.id")
|
||||
.executeAndFetch(AnimationEntry.class);
|
||||
}
|
||||
}
|
||||
|
||||
public AnimationEntry getNPC(int npcid)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
return con.createQuery("select npcid, time, rev, anim1, anim2, anim3, anim4, anim5, anim6, anim7, anim8, anim9, anim10 from animation "
|
||||
+ "where npcid = :npcid order by time desc limit 1")
|
||||
.addParameter("npcid", npcid)
|
||||
.executeAndFetchFirst(AnimationEntry.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* 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.animation;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public class AnimationEntry
|
||||
{
|
||||
private int npcid;
|
||||
private Instant time;
|
||||
private int rev;
|
||||
private int[] animations;
|
||||
|
||||
public int getNPCId()
|
||||
{
|
||||
return npcid;
|
||||
}
|
||||
|
||||
public void setNPCId(int npcid)
|
||||
{
|
||||
this.npcid = npcid;
|
||||
}
|
||||
|
||||
public Instant getTime()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(Instant time)
|
||||
{
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public int getRev()
|
||||
{
|
||||
return rev;
|
||||
}
|
||||
|
||||
public void setRev(int rev)
|
||||
{
|
||||
this.rev = rev;
|
||||
}
|
||||
|
||||
public int[] getAnimations()
|
||||
{
|
||||
return animations;
|
||||
}
|
||||
|
||||
public void setAnimations(int[] animations)
|
||||
{
|
||||
this.animations = animations;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.chat;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import com.google.common.base.Strings;
|
||||
import net.runelite.http.api.chat.House;
|
||||
import net.runelite.http.service.util.exception.NotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/chat")
|
||||
public class ChatController
|
||||
{
|
||||
private static final Pattern STRING_VALIDATION = Pattern.compile("[^a-zA-Z0-9' -]");
|
||||
private static final int STRING_MAX_LENGTH = 50;
|
||||
|
||||
@Autowired
|
||||
private ChatService chatService;
|
||||
|
||||
@PostMapping("/layout")
|
||||
public void submitLayout(@RequestParam String name, @RequestParam String layout)
|
||||
{
|
||||
if (Strings.isNullOrEmpty(layout))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
chatService.setLayout(name, layout);
|
||||
}
|
||||
|
||||
@GetMapping("/layout")
|
||||
public String getLayout(@RequestParam String name)
|
||||
{
|
||||
String layout = chatService.getLayout(name);
|
||||
if (layout == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
@PostMapping("/hosts")
|
||||
public void submitHost(@RequestParam int world, @RequestParam String location, @RequestParam String owner, @RequestParam boolean guildedAltar, @RequestParam boolean occultAltar, @RequestParam boolean spiritTree, @RequestParam boolean fairyRing, @RequestParam boolean wildernessObelisk, @RequestParam boolean repairStand, @RequestParam boolean combatDummy, @RequestParam(required = false, defaultValue = "false") boolean remove)
|
||||
{
|
||||
if (!location.equals("Rimmington") && !location.equals("Yanille"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
House house = new House();
|
||||
house.setOwner(owner);
|
||||
house.setGuildedAltarPresent(guildedAltar);
|
||||
house.setOccultAltarPresent(occultAltar);
|
||||
house.setSpiritTreePresent(spiritTree);
|
||||
house.setFairyRingPresent(fairyRing);
|
||||
house.setWildernessObeliskPresent(wildernessObelisk);
|
||||
house.setRepairStandPresent(repairStand);
|
||||
house.setCombatDummyPresent(combatDummy);
|
||||
|
||||
if (remove)
|
||||
{
|
||||
chatService.removeHost(world, location, house);
|
||||
}
|
||||
else
|
||||
{
|
||||
chatService.addHost(world, location, house);
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/hosts")
|
||||
public House[] getHosts(@RequestParam int world, @RequestParam String location)
|
||||
{
|
||||
return chatService.getHosts(world, location);
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.chat;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.chat.ChatClient;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.api.chat.House;
|
||||
import net.runelite.http.service.util.redis.RedisPool;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
@Service
|
||||
public class ChatService
|
||||
{
|
||||
private static final Duration EXPIRE = Duration.ofMinutes(2);
|
||||
|
||||
private final RedisPool jedisPool;
|
||||
private final ChatClient chatClient = new ChatClient();
|
||||
|
||||
|
||||
@Autowired
|
||||
public ChatService(RedisPool jedisPool)
|
||||
{
|
||||
this.jedisPool = jedisPool;
|
||||
}
|
||||
|
||||
public String getLayout(String name)
|
||||
{
|
||||
String value;
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
value = jedis.get("layout." + name);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setLayout(String name, String layout)
|
||||
{
|
||||
if (!chatClient.testLayout(layout))
|
||||
{
|
||||
throw new IllegalArgumentException(layout);
|
||||
}
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.setex("layout." + name, (int) EXPIRE.getSeconds(), layout);
|
||||
}
|
||||
}
|
||||
|
||||
public void addHost(int world, String location, House house)
|
||||
{
|
||||
String houseJSON = house.toString();
|
||||
|
||||
String key = "hosts.w" + Integer.toString(world) + "." + location;
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.rpush(key, houseJSON);
|
||||
}
|
||||
}
|
||||
|
||||
public House[] getHosts(int world, String location)
|
||||
{
|
||||
List<String> json;
|
||||
String key = "hosts.w" + Integer.toString(world) + "." + location;
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
json = jedis.lrange(key, 0, 25);
|
||||
}
|
||||
|
||||
if (json.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
House[] hosts = new House[json.size()];
|
||||
for (int i = 0; i < json.size(); i++)
|
||||
{
|
||||
hosts[i] = RuneLiteAPI.GSON.fromJson(json.get(i), House.class);
|
||||
}
|
||||
return hosts;
|
||||
}
|
||||
|
||||
public void removeHost(int world, String location, House house)
|
||||
{
|
||||
String json = house.toString();
|
||||
String key = "hosts.w" + Integer.toString(world) + "." + location;
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.lrem(key, 0, json);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.xtea;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
class XteaCache
|
||||
{
|
||||
private int region;
|
||||
private int key1;
|
||||
private int key2;
|
||||
private int key3;
|
||||
private int key4;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.xtea;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import net.runelite.http.api.xtea.XteaKey;
|
||||
import net.runelite.http.api.xtea.XteaRequest;
|
||||
import net.runelite.http.service.util.exception.NotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/xtea")
|
||||
public class XteaController
|
||||
{
|
||||
@Autowired
|
||||
private XteaEndpoint xteaService;
|
||||
|
||||
@RequestMapping(method = POST)
|
||||
public void submit(@RequestBody XteaRequest xteaRequest)
|
||||
{
|
||||
xteaService.submit(xteaRequest);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<XteaKey> get()
|
||||
{
|
||||
return xteaService.get().stream()
|
||||
.map(XteaController::entryToKey)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("/{region}")
|
||||
public XteaKey getRegion(@PathVariable int region)
|
||||
{
|
||||
XteaEntry xteaRegion = xteaService.getRegion(region);
|
||||
if (xteaRegion == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return entryToKey(xteaRegion);
|
||||
}
|
||||
|
||||
private static XteaKey entryToKey(XteaEntry xe)
|
||||
{
|
||||
XteaKey xteaKey = new XteaKey();
|
||||
xteaKey.setRegion(xe.getRegion());
|
||||
xteaKey.setKeys(new int[]
|
||||
{
|
||||
xe.getKey1(),
|
||||
xe.getKey2(),
|
||||
xe.getKey3(),
|
||||
xe.getKey4()
|
||||
});
|
||||
return xteaKey;
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* 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.xtea;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.xtea.XteaKey;
|
||||
import net.runelite.http.api.xtea.XteaRequest;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Query;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@Service
|
||||
public class XteaEndpoint
|
||||
{
|
||||
private static final String CREATE_SQL = "CREATE TABLE IF NOT EXISTS `xtea` (\n"
|
||||
+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"
|
||||
+ " `region` int(11) NOT NULL,\n"
|
||||
+ " `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n"
|
||||
+ " `rev` int(11) NOT NULL,\n"
|
||||
+ " `key1` int(11) NOT NULL,\n"
|
||||
+ " `key2` int(11) NOT NULL,\n"
|
||||
+ " `key3` int(11) NOT NULL,\n"
|
||||
+ " `key4` int(11) NOT NULL,\n"
|
||||
+ " PRIMARY KEY (`id`),\n"
|
||||
+ " KEY `region` (`region`,`time`)\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
private final Sql2o sql2o;
|
||||
|
||||
private final Cache<Integer, XteaCache> keyCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(1024)
|
||||
.build();
|
||||
|
||||
@Autowired
|
||||
public XteaEndpoint(
|
||||
@Qualifier("Runelite SQL2O") Sql2o sql2o
|
||||
)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
con.createQuery(CREATE_SQL)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private XteaEntry findLatestXtea(Connection con, int region)
|
||||
{
|
||||
return con.createQuery("select region, time, key1, key2, key3, key4 from xtea "
|
||||
+ "where region = :region "
|
||||
+ "order by time desc "
|
||||
+ "limit 1")
|
||||
.addParameter("region", region)
|
||||
.executeAndFetchFirst(XteaEntry.class);
|
||||
}
|
||||
|
||||
public void submit(XteaRequest xteaRequest)
|
||||
{
|
||||
boolean cached = true;
|
||||
for (XteaKey key : xteaRequest.getKeys())
|
||||
{
|
||||
int region = key.getRegion();
|
||||
int[] keys = key.getKeys();
|
||||
|
||||
XteaCache xteaCache = keyCache.getIfPresent(region);
|
||||
if (xteaCache == null
|
||||
|| xteaCache.getKey1() != keys[0]
|
||||
|| xteaCache.getKey2() != keys[1]
|
||||
|| xteaCache.getKey3() != keys[2]
|
||||
|| xteaCache.getKey4() != keys[3])
|
||||
{
|
||||
cached = false;
|
||||
keyCache.put(region, new XteaCache(region, keys[0], keys[1], keys[2], keys[3]));
|
||||
}
|
||||
}
|
||||
|
||||
if (cached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
Query query = null;
|
||||
|
||||
for (XteaKey key : xteaRequest.getKeys())
|
||||
{
|
||||
int region = key.getRegion();
|
||||
int[] keys = key.getKeys();
|
||||
|
||||
XteaEntry xteaEntry = findLatestXtea(con, region);
|
||||
|
||||
if (keys.length != 4)
|
||||
{
|
||||
throw new IllegalArgumentException("Key length must be 4");
|
||||
}
|
||||
|
||||
// already have these?
|
||||
// TODO : check if useful / works should check with findLatestXtea
|
||||
if (xteaEntry != null
|
||||
&& xteaEntry.getKey1() == keys[0]
|
||||
&& xteaEntry.getKey2() == keys[1]
|
||||
&& xteaEntry.getKey3() == keys[2]
|
||||
&& xteaEntry.getKey4() == keys[3])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (query == null)
|
||||
{
|
||||
query = con.createQuery("insert into xtea (region, rev, key1, key2, key3, key4) "
|
||||
+ "values (:region, :rev, :key1, :key2, :key3, :key4)");
|
||||
}
|
||||
|
||||
query.addParameter("region", region)
|
||||
.addParameter("rev", xteaRequest.getRevision())
|
||||
.addParameter("key1", keys[0])
|
||||
.addParameter("key2", keys[1])
|
||||
.addParameter("key3", keys[2])
|
||||
.addParameter("key4", keys[3])
|
||||
.addToBatch();
|
||||
}
|
||||
|
||||
if (query != null)
|
||||
{
|
||||
query.executeBatch();
|
||||
con.commit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<XteaEntry> get()
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
return con.createQuery(
|
||||
"select t1.region, t2.time, t2.rev, t2.key1, t2.key2, t2.key3, t2.key4 from " +
|
||||
"(select region,max(id) as id from xtea group by region) t1 " +
|
||||
"join xtea t2 on t1.id = t2.id")
|
||||
.executeAndFetch(XteaEntry.class);
|
||||
}
|
||||
}
|
||||
|
||||
public XteaEntry getRegion(int region)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
return con.createQuery("select region, time, rev, key1, key2, key3, key4 from xtea "
|
||||
+ "where region = :region order by time desc limit 1")
|
||||
.addParameter("region", region)
|
||||
.executeAndFetchFirst(XteaEntry.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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.xtea;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public class XteaEntry
|
||||
{
|
||||
private int region;
|
||||
private Instant time;
|
||||
private int rev;
|
||||
private int key1;
|
||||
private int key2;
|
||||
private int key3;
|
||||
private int key4;
|
||||
|
||||
public int getRegion()
|
||||
{
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(int region)
|
||||
{
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public Instant getTime()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(Instant time)
|
||||
{
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public int getRev()
|
||||
{
|
||||
return rev;
|
||||
}
|
||||
|
||||
public void setRev(int rev)
|
||||
{
|
||||
this.rev = rev;
|
||||
}
|
||||
|
||||
public int getKey1()
|
||||
{
|
||||
return key1;
|
||||
}
|
||||
|
||||
public void setKey1(int key1)
|
||||
{
|
||||
this.key1 = key1;
|
||||
}
|
||||
|
||||
public int getKey2()
|
||||
{
|
||||
return key2;
|
||||
}
|
||||
|
||||
public void setKey2(int key2)
|
||||
{
|
||||
this.key2 = key2;
|
||||
}
|
||||
|
||||
public int getKey3()
|
||||
{
|
||||
return key3;
|
||||
}
|
||||
|
||||
public void setKey3(int key3)
|
||||
{
|
||||
this.key3 = key3;
|
||||
}
|
||||
|
||||
public int getKey4()
|
||||
{
|
||||
return key4;
|
||||
}
|
||||
|
||||
public void setKey4(int key4)
|
||||
{
|
||||
this.key4 = key4;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
# Enable debug logging
|
||||
debug: true
|
||||
logging.level.net.runelite: DEBUG
|
||||
|
||||
# Development data sources
|
||||
datasource:
|
||||
runelite:
|
||||
jndiName:
|
||||
driverClassName: org.mariadb.jdbc.Driver
|
||||
type: org.mariadb.jdbc.MariaDbDataSource
|
||||
url: jdbc:mariadb://localhost:3306/runelite
|
||||
username: runelite
|
||||
password: runelite
|
||||
runelite-cache:
|
||||
jndiName:
|
||||
driverClassName: org.mariadb.jdbc.Driver
|
||||
type: org.mariadb.jdbc.MariaDbDataSource
|
||||
url: jdbc:mariadb://localhost:3306/cache
|
||||
username: runelite
|
||||
password: runelite
|
||||
runelite-tracker:
|
||||
jndiName:
|
||||
driverClassName: org.mariadb.jdbc.Driver
|
||||
type: org.mariadb.jdbc.MariaDbDataSource
|
||||
url: jdbc:mariadb://localhost:3306/xptracker
|
||||
username: runelite
|
||||
password: runelite
|
||||
|
||||
# Development oauth callback (without proxy)
|
||||
oauth:
|
||||
callback: http://localhost:8080/account/callback
|
||||
@@ -1,39 +0,0 @@
|
||||
datasource:
|
||||
openosrs:
|
||||
jndiName: java:comp/env/jdbc/runelite
|
||||
openosrs-cache:
|
||||
jndiName: java:comp/env/jdbc/runelite-cache2
|
||||
openosrs-tracker:
|
||||
jndiName: java:comp/env/jdbc/runelite-tracker
|
||||
|
||||
# By default Spring tries to register the datasource as an MXBean,
|
||||
# so if multiple apis are deployed on one web container with
|
||||
# shared datasource it tries to register it multiples times and
|
||||
# fails when starting the 2nd api
|
||||
spring.jmx.enabled: false
|
||||
|
||||
# Google OAuth client
|
||||
oauth:
|
||||
client-id:
|
||||
client-secret:
|
||||
callback: https://api.runelite.net/oauth/
|
||||
|
||||
# Minio client storage for cache
|
||||
minio:
|
||||
endpoint: http://localhost:9000
|
||||
accesskey: AM54M27O4WZK65N6F8IP
|
||||
secretkey: /PZCxzmsJzwCHYlogcymuprniGCaaLUOET2n6yMP
|
||||
bucket: runelite
|
||||
|
||||
# Redis client for temporary data storage
|
||||
redis:
|
||||
pool.size: 10
|
||||
host: http://localhost:6379
|
||||
|
||||
|
||||
# Twitter client for feed
|
||||
runelite:
|
||||
twitter:
|
||||
consumerkey:
|
||||
secretkey:
|
||||
listid: 968949795153948673
|
||||
@@ -1,135 +0,0 @@
|
||||
-- MySQL dump 10.16 Distrib 10.2.18-MariaDB, for Linux (x86_64)
|
||||
--
|
||||
-- Host: localhost Database: xptracker
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 10.2.18-MariaDB
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
--
|
||||
-- Table structure for table `player`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `player`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `player` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(32) NOT NULL,
|
||||
`tracked_since` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
`last_updated` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
`rank` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `xp`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `xp`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `xp` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`time` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
`player` int(11) NOT NULL,
|
||||
`attack_xp` int(11) NOT NULL,
|
||||
`defence_xp` int(11) NOT NULL,
|
||||
`strength_xp` int(11) NOT NULL,
|
||||
`hitpoints_xp` int(11) NOT NULL,
|
||||
`ranged_xp` int(11) NOT NULL,
|
||||
`prayer_xp` int(11) NOT NULL,
|
||||
`magic_xp` int(11) NOT NULL,
|
||||
`cooking_xp` int(11) NOT NULL,
|
||||
`woodcutting_xp` int(11) NOT NULL,
|
||||
`fletching_xp` int(11) NOT NULL,
|
||||
`fishing_xp` int(11) NOT NULL,
|
||||
`firemaking_xp` int(11) NOT NULL,
|
||||
`crafting_xp` int(11) NOT NULL,
|
||||
`smithing_xp` int(11) NOT NULL,
|
||||
`mining_xp` int(11) NOT NULL,
|
||||
`herblore_xp` int(11) NOT NULL,
|
||||
`agility_xp` int(11) NOT NULL,
|
||||
`thieving_xp` int(11) NOT NULL,
|
||||
`slayer_xp` int(11) NOT NULL,
|
||||
`farming_xp` int(11) NOT NULL,
|
||||
`runecraft_xp` int(11) NOT NULL,
|
||||
`hunter_xp` int(11) NOT NULL,
|
||||
`construction_xp` int(11) NOT NULL,
|
||||
`overall_xp` int(11) GENERATED ALWAYS AS (`attack_xp` + `defence_xp` + `strength_xp` + `hitpoints_xp` + `ranged_xp` + `prayer_xp` + `magic_xp` + `cooking_xp` + `woodcutting_xp` + `fletching_xp` + `fishing_xp` + `firemaking_xp` + `crafting_xp` + `smithing_xp` + `mining_xp` + `herblore_xp` + `agility_xp` + `thieving_xp` + `slayer_xp` + `farming_xp` + `runecraft_xp` + `hunter_xp` + `construction_xp`) VIRTUAL,
|
||||
`attack_level` int(11) GENERATED ALWAYS AS (level_for_xp(`attack_xp` AS `attack_xp`)) VIRTUAL,
|
||||
`defence_level` int(11) GENERATED ALWAYS AS (level_for_xp(`defence_xp` AS `defence_xp`)) VIRTUAL,
|
||||
`strength_level` int(11) GENERATED ALWAYS AS (level_for_xp(`strength_xp` AS `strength_xp`)) VIRTUAL,
|
||||
`hitpoints_level` int(11) GENERATED ALWAYS AS (level_for_xp(`hitpoints_xp` AS `hitpoints_xp`)) VIRTUAL,
|
||||
`ranged_level` int(11) GENERATED ALWAYS AS (level_for_xp(`ranged_xp` AS `ranged_xp`)) VIRTUAL,
|
||||
`prayer_level` int(11) GENERATED ALWAYS AS (level_for_xp(`prayer_xp` AS `prayer_xp`)) VIRTUAL,
|
||||
`magic_level` int(11) GENERATED ALWAYS AS (level_for_xp(`magic_xp` AS `magic_xp`)) VIRTUAL,
|
||||
`cooking_level` int(11) GENERATED ALWAYS AS (level_for_xp(`cooking_xp` AS `cooking_xp`)) VIRTUAL,
|
||||
`woodcutting_level` int(11) GENERATED ALWAYS AS (level_for_xp(`woodcutting_xp` AS `woodcutting_xp`)) VIRTUAL,
|
||||
`fletching_level` int(11) GENERATED ALWAYS AS (level_for_xp(`fletching_xp` AS `fletching_xp`)) VIRTUAL,
|
||||
`fishing_level` int(11) GENERATED ALWAYS AS (level_for_xp(`fishing_xp` AS `fishing_xp`)) VIRTUAL,
|
||||
`firemaking_level` int(11) GENERATED ALWAYS AS (level_for_xp(`firemaking_xp` AS `firemaking_xp`)) VIRTUAL,
|
||||
`crafting_level` int(11) GENERATED ALWAYS AS (level_for_xp(`crafting_xp` AS `crafting_xp`)) VIRTUAL,
|
||||
`smithing_level` int(11) GENERATED ALWAYS AS (level_for_xp(`smithing_xp` AS `smithing_xp`)) VIRTUAL,
|
||||
`mining_level` int(11) GENERATED ALWAYS AS (level_for_xp(`mining_xp` AS `mining_xp`)) VIRTUAL,
|
||||
`herblore_level` int(11) GENERATED ALWAYS AS (level_for_xp(`herblore_xp` AS `herblore_xp`)) VIRTUAL,
|
||||
`agility_level` int(11) GENERATED ALWAYS AS (level_for_xp(`agility_xp` AS `agility_xp`)) VIRTUAL,
|
||||
`thieving_level` int(11) GENERATED ALWAYS AS (level_for_xp(`thieving_xp` AS `thieving_xp`)) VIRTUAL,
|
||||
`slayer_level` int(11) GENERATED ALWAYS AS (level_for_xp(`slayer_xp` AS `slayer_xp`)) VIRTUAL,
|
||||
`farming_level` int(11) GENERATED ALWAYS AS (level_for_xp(`farming_xp` AS `farming_xp`)) VIRTUAL,
|
||||
`runecraft_level` int(11) GENERATED ALWAYS AS (level_for_xp(`runecraft_xp` AS `runecraft_xp`)) VIRTUAL,
|
||||
`hunter_level` int(11) GENERATED ALWAYS AS (level_for_xp(`hunter_xp` AS `hunter_xp`)) VIRTUAL,
|
||||
`construction_level` int(11) GENERATED ALWAYS AS (level_for_xp(`construction_xp` AS `construction_xp`)) VIRTUAL,
|
||||
`overall_level` int(11) GENERATED ALWAYS AS (`attack_level` + `defence_level` + `strength_level` + `hitpoints_level` + `ranged_level` + `prayer_level` + `magic_level` + `cooking_level` + `woodcutting_level` + `fletching_level` + `fishing_level` + `firemaking_level` + `crafting_level` + `smithing_level` + `mining_level` + `herblore_level` + `agility_level` + `thieving_level` + `slayer_level` + `farming_level` + `runecraft_level` + `hunter_level` + `construction_level`) VIRTUAL,
|
||||
`attack_rank` int(11) NOT NULL,
|
||||
`defence_rank` int(11) NOT NULL,
|
||||
`strength_rank` int(11) NOT NULL,
|
||||
`hitpoints_rank` int(11) NOT NULL,
|
||||
`ranged_rank` int(11) NOT NULL,
|
||||
`prayer_rank` int(11) NOT NULL,
|
||||
`magic_rank` int(11) NOT NULL,
|
||||
`cooking_rank` int(11) NOT NULL,
|
||||
`woodcutting_rank` int(11) NOT NULL,
|
||||
`fletching_rank` int(11) NOT NULL,
|
||||
`fishing_rank` int(11) NOT NULL,
|
||||
`firemaking_rank` int(11) NOT NULL,
|
||||
`crafting_rank` int(11) NOT NULL,
|
||||
`smithing_rank` int(11) NOT NULL,
|
||||
`mining_rank` int(11) NOT NULL,
|
||||
`herblore_rank` int(11) NOT NULL,
|
||||
`agility_rank` int(11) NOT NULL,
|
||||
`thieving_rank` int(11) NOT NULL,
|
||||
`slayer_rank` int(11) NOT NULL,
|
||||
`farming_rank` int(11) NOT NULL,
|
||||
`runecraft_rank` int(11) NOT NULL,
|
||||
`hunter_rank` int(11) NOT NULL,
|
||||
`construction_rank` int(11) NOT NULL,
|
||||
`overall_rank` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `player_time` (`player`,`time`),
|
||||
KEY `idx_time` (`time`),
|
||||
CONSTRAINT `fk_player` FOREIGN KEY (`player`) REFERENCES `player` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||
|
||||
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||
|
||||
-- Dump completed on 2019-02-15 21:01:17
|
||||
@@ -1,110 +0,0 @@
|
||||
{{#info}}
|
||||
# {{title}}
|
||||
{{join schemes " | "}}://{{host}}{{basePath}}
|
||||
|
||||
{{description}}
|
||||
|
||||
{{#contact}}
|
||||
[**Contact the developer**](mailto:{{email}})
|
||||
{{/contact}}
|
||||
|
||||
**Version** {{version}}
|
||||
|
||||
{{#if termsOfService}}
|
||||
[**Terms of Service**]({{termsOfService}})
|
||||
{{/if}}
|
||||
|
||||
{{/info}}
|
||||
|
||||
{{#if consumes}}__Consumes:__ {{join consumes ", "}}{{/if}}
|
||||
|
||||
{{#if produces}}__Produces:__ {{join produces ", "}}{{/if}}
|
||||
|
||||
{{#if securityDefinitions}}
|
||||
# Security Definitions
|
||||
{{> security}}
|
||||
{{/if}}
|
||||
|
||||
<details>
|
||||
<summary><b>Table Of Contents</b></summary>
|
||||
[toc]
|
||||
</details>
|
||||
|
||||
# APIs
|
||||
|
||||
{{#each paths}}
|
||||
## {{@key}}
|
||||
{{#this}}
|
||||
{{#get}}
|
||||
### GET
|
||||
{{> operation}}
|
||||
{{/get}}
|
||||
|
||||
{{#put}}
|
||||
### PUT
|
||||
{{> operation}}
|
||||
{{/put}}
|
||||
|
||||
{{#post}}
|
||||
### POST
|
||||
|
||||
{{> operation}}
|
||||
|
||||
{{/post}}
|
||||
|
||||
{{#delete}}
|
||||
### DELETE
|
||||
{{> operation}}
|
||||
{{/delete}}
|
||||
|
||||
{{#option}}
|
||||
### OPTION
|
||||
{{> operation}}
|
||||
{{/option}}
|
||||
|
||||
{{#patch}}
|
||||
### PATCH
|
||||
{{> operation}}
|
||||
{{/patch}}
|
||||
|
||||
{{#head}}
|
||||
### HEAD
|
||||
{{> operation}}
|
||||
{{/head}}
|
||||
|
||||
{{/this}}
|
||||
{{/each}}
|
||||
|
||||
# Definitions
|
||||
{{#each definitions}}
|
||||
## <a name="/definitions/{{key}}">{{@key}}</a>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>type</th>
|
||||
<th>required</th>
|
||||
<th>description</th>
|
||||
<th>example</th>
|
||||
</tr>
|
||||
{{#each this.properties}}
|
||||
<tr>
|
||||
<td>{{@key}}</td>
|
||||
<td>
|
||||
{{#ifeq type "array"}}
|
||||
{{#items.$ref}}
|
||||
{{type}}[<a href="{{items.$ref}}">{{basename items.$ref}}</a>]
|
||||
{{/items.$ref}}
|
||||
{{^items.$ref}}{{type}}[{{items.type}}]{{/items.$ref}}
|
||||
{{else}}
|
||||
{{#$ref}}<a href="{{$ref}}">{{basename $ref}}</a>{{/$ref}}
|
||||
{{^$ref}}{{type}}{{#format}} ({{format}}){{/format}}{{/$ref}}
|
||||
{{/ifeq}}
|
||||
</td>
|
||||
<td>{{#required}}required{{/required}}{{^required}}optional{{/required}}</td>
|
||||
<td>{{#description}}{{{description}}}{{/description}}{{^description}}-{{/description}}</td>
|
||||
<td>{{example}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
{{/each}}
|
||||
@@ -1,71 +0,0 @@
|
||||
{{#deprecated}}-deprecated-{{/deprecated}}
|
||||
<a id="{{operationId}}">{{summary}}</a>
|
||||
|
||||
{{description}}
|
||||
|
||||
{{#if externalDocs.url}}{{externalDocs.description}}. [See external documents for more details]({{externalDocs.url}})
|
||||
{{/if}}
|
||||
|
||||
{{#if security}}
|
||||
#### Security
|
||||
{{/if}}
|
||||
|
||||
{{#security}}
|
||||
{{#each this}}
|
||||
* {{@key}}
|
||||
{{#this}} * {{this}}
|
||||
{{/this}}
|
||||
{{/each}}
|
||||
{{/security}}
|
||||
|
||||
#### Request
|
||||
|
||||
{{#if consumes}}__Content-Type:__ {{join consumes ", "}}{{/if}}
|
||||
|
||||
##### Parameters
|
||||
{{#if parameters}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Located in</th>
|
||||
<th>Required</th>
|
||||
<th>Description</th>
|
||||
<th>Default</th>
|
||||
<th>Schema</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
|
||||
{{#parameters}}
|
||||
<tr>
|
||||
<th>{{name}}</th>
|
||||
<td>{{in}}</td>
|
||||
<td>{{#if required}}yes{{else}}no{{/if}}</td>
|
||||
<td>{{description}}{{#if pattern}} (**Pattern**: `{{pattern}}`){{/if}}</td>
|
||||
<td> - </td>
|
||||
{{#ifeq in "body"}}
|
||||
<td>
|
||||
{{#ifeq schema.type "array"}}Array[<a href="{{schema.items.$ref}}">{{basename schema.items.$ref}}</a>]{{/ifeq}}
|
||||
{{#schema.$ref}}<a href="{{schema.$ref}}">{{basename schema.$ref}}</a> {{/schema.$ref}}
|
||||
</td>
|
||||
{{else}}
|
||||
{{#ifeq type "array"}}
|
||||
<td>Array[{{items.type}}] ({{collectionFormat}})</td>
|
||||
{{else}}
|
||||
<td>{{type}} {{#format}}({{format}}){{/format}}</td>
|
||||
{{/ifeq}}
|
||||
{{/ifeq}}
|
||||
</tr>
|
||||
{{/parameters}}
|
||||
{{#if parameters}}
|
||||
</table>
|
||||
{{/if}}
|
||||
|
||||
|
||||
#### Response
|
||||
|
||||
{{#if produces}}__Content-Type:__ {{join produces ", "}}{{/if}}
|
||||
|
||||
| Status Code | Reason | Response Model |
|
||||
|-------------|-------------|----------------|
|
||||
{{#each responses}}| {{@key}} | {{description}} | {{#schema.$ref}}<a href="{{schema.$ref}}">{{basename schema.$ref}}</a>{{/schema.$ref}}{{#ifeq schema.type "array"}}Array[<a href="{{schema.items.$ref}}">{{basename schema.items.$ref}}</a>]{{/ifeq}}{{^schema}} - {{/schema}}|
|
||||
{{/each}}
|
||||
@@ -1,88 +0,0 @@
|
||||
{{#each securityDefinitions}}
|
||||
### {{@key}}
|
||||
{{#this}}
|
||||
{{#ifeq type "oauth2"}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th colspan="2">{{type}}</th>
|
||||
</tr>
|
||||
{{#if description}}
|
||||
<tr>
|
||||
<th>description</th>
|
||||
<th colspan="2">{{description}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if authorizationUrl}}
|
||||
<tr>
|
||||
<th>authorizationUrl</th>
|
||||
<th colspan="2">{{authorizationUrl}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if flow}}
|
||||
<tr>
|
||||
<th>flow</th>
|
||||
<th colspan="2">{{flow}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if tokenUrl}}
|
||||
<tr>
|
||||
<th>tokenUrl</th>
|
||||
<th colspan="2">{{tokenUrl}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if scopes}}
|
||||
<tr>
|
||||
<td rowspan="3">scopes</td>
|
||||
{{#each scopes}}
|
||||
<td>{{@key}}</td>
|
||||
<td>{{this}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
{{/ifeq}}
|
||||
{{#ifeq type "apiKey"}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th colspan="2">{{type}}</th>
|
||||
</tr>
|
||||
{{#if description}}
|
||||
<tr>
|
||||
<th>description</th>
|
||||
<th colspan="2">{{description}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if name}}
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th colspan="2">{{name}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
{{#if in}}
|
||||
<tr>
|
||||
<th>in</th>
|
||||
<th colspan="2">{{in}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
{{/ifeq}}
|
||||
{{#ifeq type "basic"}}
|
||||
<table>
|
||||
<tr>
|
||||
<th>type</th>
|
||||
<th colspan="2">{{type}}</th>
|
||||
</tr>
|
||||
{{#if description}}
|
||||
<tr>
|
||||
<th>description</th>
|
||||
<th colspan="2">{{description}}</th>
|
||||
</tr>
|
||||
{{/if}}
|
||||
</table>
|
||||
{{/ifeq}}
|
||||
{{/this}}
|
||||
{{/each}}
|
||||
@@ -1,49 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/3.0.1/github-markdown.min.css"/>
|
||||
<style>
|
||||
.markdown-body {
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 45px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.markdown-body {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-body table {
|
||||
display: table;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- JS -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.0/showdown.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/gh/JanLoebel/showdown-toc/src/showdown-toc.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var markdown = document.querySelector('noscript').innerText
|
||||
var converter = new showdown.Converter({emoji: true, extensions: ['toc']})
|
||||
converter.setFlavor('github')
|
||||
var html = converter.makeHtml(markdown)
|
||||
|
||||
document.body.innerHTML = html
|
||||
})
|
||||
</script>
|
||||
|
||||
<title>{{info.title}} {{info.version}}</title>
|
||||
</head>
|
||||
<body class="markdown-body">
|
||||
<noscript>{{>markdown}}</noscript>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,32 +0,0 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
|
||||
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
|
||||
<display-name>RuneLite API</display-name>
|
||||
</web-app>
|
||||
@@ -1,17 +0,0 @@
|
||||
# Use in-memory database for tests
|
||||
datasource:
|
||||
runelite:
|
||||
jndiName:
|
||||
driverClassName: org.h2.Driver
|
||||
type: org.h2.jdbcx.JdbcDataSource
|
||||
url: jdbc:h2:mem:runelite
|
||||
runelite-cache:
|
||||
jndiName:
|
||||
driverClassName: org.h2.Driver
|
||||
type: org.h2.jdbcx.JdbcDataSource
|
||||
url: jdbc:h2:mem:cache
|
||||
runelite-tracker:
|
||||
jndiName:
|
||||
driverClassName: org.h2.Driver
|
||||
type: org.h2.jdbcx.JdbcDataSource
|
||||
url: jdbc:h2:mem:xptracker
|
||||
Binary file not shown.
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
war
|
||||
}
|
||||
|
||||
description = "Web Service"
|
||||
|
||||
dependencies {
|
||||
annotationProcessor(Libraries.mapstructProcessor)
|
||||
annotationProcessor(Libraries.lombok)
|
||||
|
||||
api(project(":cache"))
|
||||
api(project(":http-api"))
|
||||
api(project(":runelite-api"))
|
||||
|
||||
implementation(Libraries.scribejava)
|
||||
implementation(Libraries.gson)
|
||||
implementation(Libraries.guava)
|
||||
implementation(Libraries.minio)
|
||||
implementation(Libraries.mapstruct)
|
||||
implementation(Libraries.mongodbDriverSync)
|
||||
implementation(Libraries.slf4jApi)
|
||||
implementation(Libraries.springbootJdbc)
|
||||
implementation(Libraries.springbootDevtools)
|
||||
implementation(Libraries.springbootStarterWeb)
|
||||
implementation(Libraries.sql2o)
|
||||
implementation(Libraries.jedis) {
|
||||
exclude(module = "commons-pool2")
|
||||
}
|
||||
|
||||
providedCompile(Libraries.mariadbJdbc)
|
||||
providedCompile(Libraries.lombok)
|
||||
providedCompile(Libraries.springbootStarterTomcat)
|
||||
|
||||
testImplementation(Libraries.h2)
|
||||
testImplementation(Libraries.okhttp3Webserver)
|
||||
testImplementation(Libraries.springbootStarterTest) {
|
||||
exclude(module = "commons-logging")
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import com.google.common.base.Strings;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.naming.NamingException;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.sql.DataSource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.service.util.InstantConverter;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.slf4j.ILoggerFactory;
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
|
||||
import org.springframework.jndi.JndiTemplate;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.sql2o.Sql2o;
|
||||
import org.sql2o.converters.Converter;
|
||||
import org.sql2o.quirks.NoQuirks;
|
||||
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
@EnableScheduling
|
||||
@Slf4j
|
||||
public class SpringBootWebApplication extends SpringBootServletInitializer
|
||||
{
|
||||
@Bean
|
||||
protected ServletContextListener listener()
|
||||
{
|
||||
return new ServletContextListener()
|
||||
{
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
log.info("RuneLite API started");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
// Destroy okhttp client
|
||||
OkHttpClient client = RuneLiteAPI.CLIENT;
|
||||
client.dispatcher().executorService().shutdown();
|
||||
client.connectionPool().evictAll();
|
||||
try
|
||||
{
|
||||
Cache cache = client.cache();
|
||||
if (cache != null)
|
||||
{
|
||||
cache.close();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn(null, ex);
|
||||
}
|
||||
|
||||
log.info("RuneLite API stopped");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.runelite")
|
||||
@Bean("dataSourceRuneLite")
|
||||
public DataSourceProperties dataSourceProperties()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.runelite-cache")
|
||||
@Bean("dataSourceRuneLiteCache")
|
||||
public DataSourceProperties dataSourcePropertiesCache()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.runelite-tracker")
|
||||
@Bean("dataSourceRuneLiteTracker")
|
||||
public DataSourceProperties dataSourcePropertiesTracker()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@Bean(value = "runelite", destroyMethod = "")
|
||||
public DataSource runeliteDataSource(@Qualifier("dataSourceRuneLite") DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
return getDataSource(dataSourceProperties);
|
||||
}
|
||||
|
||||
@Bean(value = "runelite-cache", destroyMethod = "")
|
||||
public DataSource runeliteCache2DataSource(@Qualifier("dataSourceRuneLiteCache") DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
return getDataSource(dataSourceProperties);
|
||||
}
|
||||
|
||||
@Bean(value = "runelite-tracker", destroyMethod = "")
|
||||
public DataSource runeliteTrackerDataSource(@Qualifier("dataSourceRuneLiteTracker") DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
return getDataSource(dataSourceProperties);
|
||||
}
|
||||
|
||||
@Bean("Runelite SQL2O")
|
||||
public Sql2o sql2o(@Qualifier("runelite") DataSource dataSource)
|
||||
{
|
||||
return createSql2oFromDataSource(dataSource);
|
||||
}
|
||||
|
||||
@Bean("Runelite Cache SQL2O")
|
||||
public Sql2o cacheSql2o(@Qualifier("runelite-cache") DataSource dataSource)
|
||||
{
|
||||
return createSql2oFromDataSource(dataSource);
|
||||
}
|
||||
|
||||
@Bean("Runelite XP Tracker SQL2O")
|
||||
public Sql2o trackerSql2o(@Qualifier("runelite-tracker") DataSource dataSource)
|
||||
{
|
||||
return createSql2oFromDataSource(dataSource);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "")
|
||||
public MongoClient mongoClient(@Value("${mongo.host:}") String host, @Value("${mongo.jndiName:}") String jndiName) throws NamingException
|
||||
{
|
||||
if (!Strings.isNullOrEmpty(jndiName))
|
||||
{
|
||||
JndiTemplate jndiTemplate = new JndiTemplate();
|
||||
return jndiTemplate.lookup(jndiName, MongoClient.class);
|
||||
}
|
||||
else if (!Strings.isNullOrEmpty(host))
|
||||
{
|
||||
return MongoClients.create(host);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("Either mongo.host or mongo.jndiName must be set");
|
||||
}
|
||||
}
|
||||
|
||||
private static DataSource getDataSource(DataSourceProperties dataSourceProperties)
|
||||
{
|
||||
if (!Strings.isNullOrEmpty(dataSourceProperties.getJndiName()))
|
||||
{
|
||||
// Use JNDI provided datasource, which is already configured with pooling
|
||||
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
|
||||
return dataSourceLookup.getDataSource(dataSourceProperties.getJndiName());
|
||||
}
|
||||
else
|
||||
{
|
||||
return dataSourceProperties.initializeDataSourceBuilder().build();
|
||||
}
|
||||
}
|
||||
|
||||
private static Sql2o createSql2oFromDataSource(final DataSource dataSource)
|
||||
{
|
||||
final Map<Class, Converter> converters = new HashMap<>();
|
||||
converters.put(Instant.class, new InstantConverter());
|
||||
return new Sql2o(dataSource, new NoQuirks(converters));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
|
||||
{
|
||||
return application.sources(SpringBootWebApplication.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException
|
||||
{
|
||||
super.onStartup(servletContext);
|
||||
ILoggerFactory loggerFactory = StaticLoggerBinder.getSingleton().getLoggerFactory();
|
||||
if (loggerFactory instanceof LoggerContext)
|
||||
{
|
||||
LoggerContext loggerContext = (LoggerContext) loggerFactory;
|
||||
loggerContext.setPackagingDataEnabled(false);
|
||||
log.debug("Disabling logback packaging data");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(SpringBootWebApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.GsonHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
public class SpringWebMvcConfigurer extends WebMvcConfigurerAdapter
|
||||
{
|
||||
/**
|
||||
* Configure .js as application/json to trick Cloudflare into caching json responses
|
||||
*/
|
||||
@Override
|
||||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
|
||||
{
|
||||
configurer.mediaType("js", MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use GSON instead of Jackson for JSON serialization
|
||||
* @param converters
|
||||
*/
|
||||
@Override
|
||||
public void extendMessageConverters(List<HttpMessageConverter<?>> converters)
|
||||
{
|
||||
// Could not figure out a better way to force GSON
|
||||
converters.removeIf(MappingJackson2HttpMessageConverter.class::isInstance);
|
||||
|
||||
GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
|
||||
gsonHttpMessageConverter.setGson(RuneLiteAPI.GSON);
|
||||
converters.add(gsonHttpMessageConverter);
|
||||
}
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* 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.account;
|
||||
|
||||
import com.github.scribejava.apis.GoogleApi20;
|
||||
import com.github.scribejava.core.builder.ServiceBuilder;
|
||||
import com.github.scribejava.core.model.OAuth2AccessToken;
|
||||
import com.github.scribejava.core.model.OAuthRequest;
|
||||
import com.github.scribejava.core.model.Response;
|
||||
import com.github.scribejava.core.model.Verb;
|
||||
import com.github.scribejava.core.oauth.OAuth20Service;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.api.account.OAuthResponse;
|
||||
import net.runelite.http.api.ws.WebsocketGsonFactory;
|
||||
import net.runelite.http.api.ws.WebsocketMessage;
|
||||
import net.runelite.http.api.ws.messages.LoginResponse;
|
||||
import net.runelite.http.service.account.beans.SessionEntry;
|
||||
import net.runelite.http.service.account.beans.UserEntry;
|
||||
import net.runelite.http.service.util.redis.RedisPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Sql2o;
|
||||
import org.sql2o.Sql2oException;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/account")
|
||||
public class AccountService
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(AccountService.class);
|
||||
|
||||
private static final String CREATE_SESSIONS = "CREATE TABLE IF NOT EXISTS `sessions` (\n"
|
||||
+ " `user` int(11) NOT NULL PRIMARY KEY,\n"
|
||||
+ " `uuid` varchar(36) NOT NULL,\n"
|
||||
+ " `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n"
|
||||
+ " `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n"
|
||||
+ " UNIQUE KEY `uuid` (`uuid`),\n"
|
||||
+ " KEY `user` (`user`)\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
private static final String CREATE_USERS = "CREATE TABLE IF NOT EXISTS `users` (\n"
|
||||
+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"
|
||||
+ " `username` tinytext NOT NULL,\n"
|
||||
+ " `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n"
|
||||
+ " PRIMARY KEY (`id`),\n"
|
||||
+ " UNIQUE KEY `username` (`username`(64))\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
private static final String SESSIONS_FK = "ALTER TABLE `sessions`\n"
|
||||
+ " ADD CONSTRAINT `id` FOREIGN KEY (`user`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;";
|
||||
|
||||
private static final String SCOPE = "https://www.googleapis.com/auth/userinfo.email";
|
||||
private static final String USERINFO = "https://www.googleapis.com/oauth2/v2/userinfo";
|
||||
private static final String RL_REDIR = "https://runelite.net/logged-in";
|
||||
|
||||
private final Gson gson = RuneLiteAPI.GSON;
|
||||
private final Gson websocketGson = WebsocketGsonFactory.build();
|
||||
|
||||
private final Sql2o sql2o;
|
||||
private final String oauthClientId;
|
||||
private final String oauthClientSecret;
|
||||
private final String oauthCallback;
|
||||
private final AuthFilter auth;
|
||||
private final RedisPool jedisPool;
|
||||
|
||||
@Autowired
|
||||
public AccountService(
|
||||
@Qualifier("Runelite SQL2O") Sql2o sql2o,
|
||||
@Value("${oauth.client-id}") String oauthClientId,
|
||||
@Value("${oauth.client-secret}") String oauthClientSecret,
|
||||
@Value("${oauth.callback}") String oauthCallback,
|
||||
AuthFilter auth,
|
||||
RedisPool jedisPool
|
||||
)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
this.oauthClientId = oauthClientId;
|
||||
this.oauthClientSecret = oauthClientSecret;
|
||||
this.oauthCallback = oauthCallback;
|
||||
this.auth = auth;
|
||||
this.jedisPool = jedisPool;
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery(CREATE_SESSIONS)
|
||||
.executeUpdate();
|
||||
|
||||
con.createQuery(CREATE_USERS)
|
||||
.executeUpdate();
|
||||
|
||||
try
|
||||
{
|
||||
con.createQuery(SESSIONS_FK)
|
||||
.executeUpdate();
|
||||
}
|
||||
catch (Sql2oException ex)
|
||||
{
|
||||
// Ignore, happens when index already exists
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/login")
|
||||
public OAuthResponse login(@RequestParam UUID uuid)
|
||||
{
|
||||
State state = new State();
|
||||
state.setUuid(uuid);
|
||||
state.setApiVersion(RuneLiteAPI.getVersion());
|
||||
|
||||
OAuth20Service service = new ServiceBuilder(oauthClientId)
|
||||
.apiSecret(oauthClientSecret)
|
||||
.defaultScope(SCOPE)
|
||||
.callback(oauthCallback)
|
||||
.build(GoogleApi20.instance());
|
||||
|
||||
final Map<String, String> additionalParams = new HashMap<>();
|
||||
additionalParams.put("prompt", "select_account");
|
||||
|
||||
final String authorizationUrl = service.createAuthorizationUrlBuilder()
|
||||
.state(gson.toJson(state))
|
||||
.additionalParams(additionalParams)
|
||||
.build();
|
||||
|
||||
OAuthResponse lr = new OAuthResponse();
|
||||
lr.setOauthUrl(authorizationUrl);
|
||||
lr.setUid(uuid);
|
||||
|
||||
return lr;
|
||||
}
|
||||
|
||||
@GetMapping("/callback")
|
||||
public Object callback(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@RequestParam(required = false) String error,
|
||||
@RequestParam String code,
|
||||
@RequestParam("state") String stateStr
|
||||
) throws InterruptedException, ExecutionException, IOException
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
logger.info("Error in oauth callback: {}", error);
|
||||
return null;
|
||||
}
|
||||
|
||||
State state = gson.fromJson(stateStr, State.class);
|
||||
|
||||
logger.info("Got authorization code {} for uuid {}", code, state.getUuid());
|
||||
|
||||
OAuth20Service service = new ServiceBuilder(oauthClientId)
|
||||
.apiSecret(oauthClientSecret)
|
||||
.defaultScope(SCOPE)
|
||||
.callback(oauthCallback)
|
||||
.build(GoogleApi20.instance());
|
||||
|
||||
OAuth2AccessToken accessToken = service.getAccessToken(code);
|
||||
|
||||
// Access user info
|
||||
OAuthRequest orequest = new OAuthRequest(Verb.GET, USERINFO);
|
||||
service.signRequest(accessToken, orequest);
|
||||
|
||||
Response oresponse = service.execute(orequest);
|
||||
|
||||
if (oresponse.getCode() / 100 != 2)
|
||||
{
|
||||
// Could be a forged result
|
||||
return null;
|
||||
}
|
||||
|
||||
UserInfo userInfo = gson.fromJson(oresponse.getBody(), UserInfo.class);
|
||||
|
||||
logger.info("Got user info: {}", userInfo);
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("insert ignore into users (username) values (:username)")
|
||||
.addParameter("username", userInfo.getEmail())
|
||||
.executeUpdate();
|
||||
|
||||
UserEntry user = con.createQuery("select id from users where username = :username")
|
||||
.addParameter("username", userInfo.getEmail())
|
||||
.executeAndFetchFirst(UserEntry.class);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
logger.warn("Unable to find newly created user session");
|
||||
return null; // that's weird
|
||||
}
|
||||
|
||||
// insert session
|
||||
con.createQuery("insert ignore into sessions (user, uuid) values (:user, :uuid)")
|
||||
.addParameter("user", user.getId())
|
||||
.addParameter("uuid", state.getUuid().toString())
|
||||
.executeUpdate();
|
||||
|
||||
logger.info("Created session for user {}", userInfo.getEmail());
|
||||
}
|
||||
|
||||
response.sendRedirect(RL_REDIR);
|
||||
|
||||
notifySession(state.getUuid(), userInfo.getEmail());
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private void notifySession(UUID uuid, String username)
|
||||
{
|
||||
LoginResponse response = new LoginResponse();
|
||||
response.setUsername(username);
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.publish("session." + uuid, websocketGson.toJson(response, WebsocketMessage.class));
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/logout")
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
SessionEntry session = auth.handle(request, response);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auth.invalidate(session.getUuid());
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("delete from sessions where uuid = :uuid")
|
||||
.addParameter("uuid", session.getUuid().toString())
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/session-check")
|
||||
public void sessionCheck(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
auth.handle(request, response);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* 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.account;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import java.io.IOException;
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.service.account.beans.SessionEntry;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@Service
|
||||
public class AuthFilter
|
||||
{
|
||||
private final Sql2o sql2o;
|
||||
|
||||
private final Cache<UUID, SessionEntry> sessionCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(10000L)
|
||||
.expireAfterAccess(30, TimeUnit.MINUTES)
|
||||
.removalListener(this::removalListener)
|
||||
.build();
|
||||
|
||||
@Autowired
|
||||
public AuthFilter(@Qualifier("Runelite SQL2O") Sql2o sql2o)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
}
|
||||
|
||||
public SessionEntry handle(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
String runeliteAuth = request.getHeader(RuneLiteAPI.RUNELITE_AUTH);
|
||||
if (runeliteAuth == null)
|
||||
{
|
||||
response.sendError(401, "Access denied");
|
||||
return null;
|
||||
}
|
||||
|
||||
UUID uuid = UUID.fromString(runeliteAuth);
|
||||
SessionEntry sessionEntry = sessionCache.getIfPresent(uuid);
|
||||
if (sessionEntry != null)
|
||||
{
|
||||
return sessionEntry;
|
||||
}
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
sessionEntry = con.createQuery("select user, uuid, created, last_used as lastUsed from sessions where uuid = :uuid")
|
||||
.addParameter("uuid", uuid.toString())
|
||||
.executeAndFetchFirst(SessionEntry.class);
|
||||
}
|
||||
|
||||
if (sessionEntry == null)
|
||||
{
|
||||
response.sendError(401, "Access denied");
|
||||
return null;
|
||||
}
|
||||
|
||||
sessionCache.put(uuid, sessionEntry);
|
||||
|
||||
return sessionEntry;
|
||||
}
|
||||
|
||||
private void removalListener(RemovalNotification<UUID, SessionEntry> notification)
|
||||
{
|
||||
UUID uuid = notification.getKey();
|
||||
Instant now = Instant.now();
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("update sessions set last_used = :last_used where uuid = :uuid")
|
||||
.addParameter("last_used", Timestamp.from(now))
|
||||
.addParameter("uuid", uuid.toString())
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidate(UUID uuid)
|
||||
{
|
||||
// If we ever run multiple services, may need to publish something here to invalidate...
|
||||
sessionCache.invalidate(uuid);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* 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.account;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class State
|
||||
{
|
||||
private UUID uuid;
|
||||
private String apiVersion;
|
||||
|
||||
public UUID getUuid()
|
||||
{
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(UUID uuid)
|
||||
{
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getApiVersion()
|
||||
{
|
||||
return apiVersion;
|
||||
}
|
||||
|
||||
public void setApiVersion(String apiVersion)
|
||||
{
|
||||
this.apiVersion = apiVersion;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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.account;
|
||||
|
||||
public class UserInfo
|
||||
{
|
||||
private String email;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "UserInfo{" + "email=" + email + '}';
|
||||
}
|
||||
|
||||
public String getEmail()
|
||||
{
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email)
|
||||
{
|
||||
this.email = email;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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.account.beans;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SessionEntry
|
||||
{
|
||||
private int user;
|
||||
private UUID uuid;
|
||||
private Instant created;
|
||||
private Instant lastUsed;
|
||||
|
||||
public int getUser()
|
||||
{
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(int user)
|
||||
{
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public UUID getUuid()
|
||||
{
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(UUID uuid)
|
||||
{
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public Instant getCreated()
|
||||
{
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Instant created)
|
||||
{
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Instant getLastUsed()
|
||||
{
|
||||
return lastUsed;
|
||||
}
|
||||
|
||||
public void setLastUsed(Instant lastUsed)
|
||||
{
|
||||
this.lastUsed = lastUsed;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.account.beans;
|
||||
|
||||
public class UserEntry
|
||||
{
|
||||
private int id;
|
||||
private String username;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "UserEntry{" + "id=" + id + ", username=" + username + '}';
|
||||
}
|
||||
|
||||
public int getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername()
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username)
|
||||
{
|
||||
this.username = username;
|
||||
}
|
||||
}
|
||||
@@ -1,362 +0,0 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.imageio.ImageIO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.cache.ConfigType;
|
||||
import net.runelite.cache.IndexType;
|
||||
import net.runelite.cache.definitions.ItemDefinition;
|
||||
import net.runelite.cache.definitions.ModelDefinition;
|
||||
import net.runelite.cache.definitions.NpcDefinition;
|
||||
import net.runelite.cache.definitions.ObjectDefinition;
|
||||
import net.runelite.cache.definitions.SpriteDefinition;
|
||||
import net.runelite.cache.definitions.TextureDefinition;
|
||||
import net.runelite.cache.definitions.loaders.ItemLoader;
|
||||
import net.runelite.cache.definitions.loaders.ModelLoader;
|
||||
import net.runelite.cache.definitions.loaders.NpcLoader;
|
||||
import net.runelite.cache.definitions.loaders.ObjectLoader;
|
||||
import net.runelite.cache.definitions.loaders.SpriteLoader;
|
||||
import net.runelite.cache.definitions.loaders.TextureLoader;
|
||||
import net.runelite.cache.definitions.providers.ItemProvider;
|
||||
import net.runelite.cache.definitions.providers.ModelProvider;
|
||||
import net.runelite.cache.definitions.providers.SpriteProvider;
|
||||
import net.runelite.cache.definitions.providers.TextureProvider;
|
||||
import net.runelite.cache.fs.ArchiveFiles;
|
||||
import net.runelite.cache.fs.Container;
|
||||
import net.runelite.cache.fs.FSFile;
|
||||
import net.runelite.cache.item.ItemSpriteFactory;
|
||||
import net.runelite.http.api.cache.Cache;
|
||||
import net.runelite.http.api.cache.CacheArchive;
|
||||
import net.runelite.http.api.cache.CacheIndex;
|
||||
import net.runelite.http.service.cache.beans.ArchiveEntry;
|
||||
import net.runelite.http.service.cache.beans.CacheEntry;
|
||||
import net.runelite.http.service.cache.beans.IndexEntry;
|
||||
import net.runelite.http.service.util.exception.NotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/cache")
|
||||
@Slf4j
|
||||
public class CacheController
|
||||
{
|
||||
@Autowired
|
||||
private CacheService cacheService;
|
||||
|
||||
@GetMapping("/")
|
||||
public List<Cache> listCaches()
|
||||
{
|
||||
return cacheService.listCaches().stream()
|
||||
.map(entry -> new Cache(entry.getId(), entry.getRevision(), entry.getDate()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("{cacheId}")
|
||||
public List<CacheIndex> listIndexes(@PathVariable int cacheId)
|
||||
{
|
||||
CacheEntry cache = cacheService.findCache(cacheId);
|
||||
if (cache == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
List<IndexEntry> indexes = cacheService.findIndexesForCache(cache);
|
||||
|
||||
return indexes.stream()
|
||||
.map(entry -> new CacheIndex(entry.getIndexId(), entry.getRevision()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("{cacheId}/{indexId}")
|
||||
public List<CacheArchive> listArchives(@PathVariable int cacheId,
|
||||
@PathVariable int indexId)
|
||||
{
|
||||
CacheEntry cache = cacheService.findCache(cacheId);
|
||||
if (cache == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
IndexEntry indexEntry = cacheService.findIndexForCache(cache, indexId);
|
||||
if (indexEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
List<ArchiveEntry> archives = cacheService.findArchivesForIndex(indexEntry);
|
||||
|
||||
return archives.stream()
|
||||
.map(archive -> new CacheArchive(archive.getArchiveId(), archive.getNameHash(), archive.getRevision()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GetMapping("{cacheId}/{indexId}/{archiveId}")
|
||||
public CacheArchive getCacheArchive(@PathVariable int cacheId,
|
||||
@PathVariable int indexId,
|
||||
@PathVariable int archiveId)
|
||||
{
|
||||
CacheEntry cache = cacheService.findCache(cacheId);
|
||||
if (cache == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
IndexEntry indexEntry = cacheService.findIndexForCache(cache, indexId);
|
||||
if (indexEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ArchiveEntry archiveEntry = cacheService.findArchiveForIndex(indexEntry, archiveId);
|
||||
if (archiveEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new CacheArchive(archiveEntry.getArchiveId(),
|
||||
archiveEntry.getNameHash(), archiveEntry.getRevision());
|
||||
}
|
||||
|
||||
@GetMapping("{cacheId}/{indexId}/{archiveId}/data")
|
||||
public byte[] getArchiveData(
|
||||
@PathVariable int cacheId,
|
||||
@PathVariable int indexId,
|
||||
@PathVariable int archiveId
|
||||
)
|
||||
{
|
||||
CacheEntry cache = cacheService.findCache(cacheId);
|
||||
if (cache == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
IndexEntry indexEntry = cacheService.findIndexForCache(cache, indexId);
|
||||
if (indexEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ArchiveEntry archiveEntry = cacheService.findArchiveForIndex(indexEntry, archiveId);
|
||||
if (archiveEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return cacheService.getArchive(archiveEntry);
|
||||
}
|
||||
|
||||
private ArchiveEntry findConfig(ConfigType config)
|
||||
{
|
||||
CacheEntry cache = cacheService.findMostRecent();
|
||||
if (cache == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
IndexEntry indexEntry = cacheService.findIndexForCache(cache, IndexType.CONFIGS.getNumber());
|
||||
if (indexEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ArchiveEntry archiveEntry = cacheService.findArchiveForIndex(indexEntry, config.getId());
|
||||
if (archiveEntry == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return archiveEntry;
|
||||
}
|
||||
|
||||
@GetMapping("item/{itemId}")
|
||||
public ItemDefinition getItem(@PathVariable int itemId) throws IOException
|
||||
{
|
||||
ArchiveEntry archiveEntry = findConfig(ConfigType.ITEM);
|
||||
|
||||
ArchiveFiles archiveFiles = cacheService.getArchiveFiles(archiveEntry);
|
||||
if (archiveFiles == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
FSFile file = archiveFiles.findFile(itemId);
|
||||
if (file == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ItemDefinition itemdef = new ItemLoader().load(itemId, file.getContents());
|
||||
return itemdef;
|
||||
}
|
||||
|
||||
@GetMapping(path = "item/{itemId}/image", produces = "image/png")
|
||||
public ResponseEntity<byte[]> getItemImage(
|
||||
@PathVariable int itemId,
|
||||
@RequestParam(defaultValue = "1") int quantity,
|
||||
@RequestParam(defaultValue = "1") int border,
|
||||
@RequestParam(defaultValue = "3153952") int shadowColor
|
||||
) throws IOException
|
||||
{
|
||||
final CacheEntry cache = cacheService.findMostRecent();
|
||||
ItemProvider itemProvider = new ItemProvider()
|
||||
{
|
||||
@Override
|
||||
public ItemDefinition provide(int itemId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return getItem(itemId);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn(null, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
ModelProvider modelProvider = new ModelProvider()
|
||||
{
|
||||
@Override
|
||||
public ModelDefinition provide(int modelId) throws IOException
|
||||
{
|
||||
IndexEntry indexEntry = cacheService.findIndexForCache(cache, IndexType.MODELS.getNumber());
|
||||
ArchiveEntry archiveEntry = cacheService.findArchiveForIndex(indexEntry, modelId);
|
||||
byte[] archiveData = Container.decompress(cacheService.getArchive(archiveEntry), null).data;
|
||||
return new ModelLoader().load(modelId, archiveData);
|
||||
}
|
||||
};
|
||||
SpriteProvider spriteProvider = new SpriteProvider()
|
||||
{
|
||||
@Override
|
||||
public SpriteDefinition provide(int spriteId, int frameId)
|
||||
{
|
||||
try
|
||||
{
|
||||
IndexEntry indexEntry = cacheService.findIndexForCache(cache, IndexType.SPRITES.getNumber());
|
||||
ArchiveEntry archiveEntry = cacheService.findArchiveForIndex(indexEntry, spriteId);
|
||||
byte[] archiveData = Container.decompress(cacheService.getArchive(archiveEntry), null).data;
|
||||
SpriteDefinition[] defs = new SpriteLoader().load(spriteId, archiveData);
|
||||
return defs[frameId];
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.warn(null, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TextureProvider textureProvider2 = new TextureProvider()
|
||||
{
|
||||
@Override
|
||||
public TextureDefinition[] provide()
|
||||
{
|
||||
try
|
||||
{
|
||||
IndexEntry indexEntry = cacheService.findIndexForCache(cache, IndexType.TEXTURES.getNumber());
|
||||
ArchiveEntry archiveEntry = cacheService.findArchiveForIndex(indexEntry, 0);
|
||||
ArchiveFiles archiveFiles = cacheService.getArchiveFiles(archiveEntry);
|
||||
TextureLoader loader = new TextureLoader();
|
||||
TextureDefinition[] defs = new TextureDefinition[archiveFiles.getFiles().size()];
|
||||
int i = 0;
|
||||
for (FSFile file : archiveFiles.getFiles())
|
||||
{
|
||||
TextureDefinition def = loader.load(file.getFileId(), file.getContents());
|
||||
defs[i++] = def;
|
||||
}
|
||||
return defs;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.warn(null, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BufferedImage itemImage = ItemSpriteFactory.createSprite(itemProvider, modelProvider, spriteProvider, textureProvider2,
|
||||
itemId, quantity, border, shadowColor, false);
|
||||
ByteArrayOutputStream bao = new ByteArrayOutputStream();
|
||||
ImageIO.write(itemImage, "png", bao);
|
||||
return ResponseEntity.ok(bao.toByteArray());
|
||||
}
|
||||
|
||||
@GetMapping("object/{objectId}")
|
||||
public ObjectDefinition getObject(
|
||||
@PathVariable int objectId
|
||||
) throws IOException
|
||||
{
|
||||
ArchiveEntry archiveEntry = findConfig(ConfigType.OBJECT);
|
||||
|
||||
ArchiveFiles archiveFiles = cacheService.getArchiveFiles(archiveEntry);
|
||||
if (archiveFiles == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
FSFile file = archiveFiles.findFile(objectId);
|
||||
if (file == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
ObjectDefinition objectdef = new ObjectLoader().load(objectId, file.getContents());
|
||||
return objectdef;
|
||||
}
|
||||
|
||||
@GetMapping("npc/{npcId}")
|
||||
public NpcDefinition getNpc(
|
||||
@PathVariable int npcId
|
||||
) throws IOException
|
||||
{
|
||||
ArchiveEntry archiveEntry = findConfig(ConfigType.NPC);
|
||||
|
||||
ArchiveFiles archiveFiles = cacheService.getArchiveFiles(archiveEntry);
|
||||
if (archiveFiles == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
FSFile file = archiveFiles.findFile(npcId);
|
||||
if (file == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
NpcDefinition npcdef = new NpcLoader().load(npcId, file.getContents());
|
||||
return npcdef;
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import java.util.List;
|
||||
import net.runelite.cache.IndexType;
|
||||
import net.runelite.http.service.cache.beans.ArchiveEntry;
|
||||
import net.runelite.http.service.cache.beans.CacheEntry;
|
||||
import net.runelite.http.service.cache.beans.FileEntry;
|
||||
import net.runelite.http.service.cache.beans.IndexEntry;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Query;
|
||||
import org.sql2o.ResultSetIterable;
|
||||
|
||||
class CacheDAO
|
||||
{
|
||||
public List<CacheEntry> listCaches(Connection con)
|
||||
{
|
||||
return con.createQuery("select id, revision, date from cache")
|
||||
.executeAndFetch(CacheEntry.class);
|
||||
}
|
||||
|
||||
public CacheEntry findMostRecent(Connection con)
|
||||
{
|
||||
return con.createQuery("select id, revision, date from cache order by revision desc, date desc limit 1")
|
||||
.executeAndFetchFirst(CacheEntry.class);
|
||||
}
|
||||
|
||||
public List<IndexEntry> findIndexesForCache(Connection con, CacheEntry cache)
|
||||
{
|
||||
return con.createQuery("select id, indexId, crc, revision from `index` where cache = :cache")
|
||||
.addParameter("cache", cache.getId())
|
||||
.executeAndFetch(IndexEntry.class);
|
||||
}
|
||||
|
||||
public IndexEntry findIndexForCache(Connection con, CacheEntry cache, int indexId)
|
||||
{
|
||||
return con.createQuery("select id, indexId, crc, revision from `index` "
|
||||
+ "where cache = :id "
|
||||
+ "and indexId = :indexId")
|
||||
.addParameter("id", cache.getId())
|
||||
.addParameter("indexId", indexId)
|
||||
.executeAndFetchFirst(IndexEntry.class);
|
||||
}
|
||||
|
||||
public ResultSetIterable<ArchiveEntry> findArchivesForIndex(Connection con, IndexEntry indexEntry)
|
||||
{
|
||||
return con.createQuery("select archive.id, archive.archiveId, archive.nameHash,"
|
||||
+ " archive.crc, archive.revision, archive.hash from index_archive "
|
||||
+ "join archive on index_archive.archive = archive.id "
|
||||
+ "where index_archive.index = :id")
|
||||
.addParameter("id", indexEntry.getId())
|
||||
.executeAndFetchLazy(ArchiveEntry.class);
|
||||
}
|
||||
|
||||
public ArchiveEntry findArchiveForIndex(Connection con, IndexEntry indexEntry, int archiveId)
|
||||
{
|
||||
return con.createQuery("select archive.id, archive.archiveId, archive.nameHash,"
|
||||
+ " archive.crc, archive.revision, archive.hash from index_archive "
|
||||
+ "join archive on index_archive.archive = archive.id "
|
||||
+ "where index_archive.index = :id "
|
||||
+ "and archive.archiveId = :archiveId")
|
||||
.addParameter("id", indexEntry.getId())
|
||||
.addParameter("archiveId", archiveId)
|
||||
.executeAndFetchFirst(ArchiveEntry.class);
|
||||
}
|
||||
|
||||
public ArchiveEntry findArchiveByName(Connection con, CacheEntry cache, IndexType index, int nameHash)
|
||||
{
|
||||
return con.createQuery("select archive.id, archive.archiveId, archive.nameHash,"
|
||||
+ " archive.crc, archive.revision, archive.hash from archive "
|
||||
+ "join index_archive on index_archive.archive = archive.id "
|
||||
+ "join `index` on index.id = index_archive.index "
|
||||
+ "where index.cache = :cacheId "
|
||||
+ "and index.indexId = :indexId "
|
||||
+ "and archive.nameHash = :nameHash "
|
||||
+ "limit 1")
|
||||
.addParameter("cacheId", cache.getId())
|
||||
.addParameter("indexId", index.getNumber())
|
||||
.addParameter("nameHash", nameHash)
|
||||
.executeAndFetchFirst(ArchiveEntry.class);
|
||||
}
|
||||
|
||||
public ResultSetIterable<FileEntry> findFilesForArchive(Connection con, ArchiveEntry archiveEntry)
|
||||
{
|
||||
Query findFilesForArchive = con.createQuery("select id, fileId, nameHash from file "
|
||||
+ "where archive = :archive");
|
||||
|
||||
return findFilesForArchive
|
||||
.addParameter("archive", archiveEntry.getId())
|
||||
.executeAndFetchLazy(FileEntry.class);
|
||||
}
|
||||
|
||||
public CacheEntry findCache(Connection con, int cacheId)
|
||||
{
|
||||
return con.createQuery("select id, revision, date from cache "
|
||||
+ "where id = :cacheId")
|
||||
.addParameter("cacheId", cacheId)
|
||||
.executeAndFetchFirst(CacheEntry.class);
|
||||
}
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, 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.cache;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.errors.ErrorResponseException;
|
||||
import io.minio.errors.InsufficientDataException;
|
||||
import io.minio.errors.InternalException;
|
||||
import io.minio.errors.InvalidArgumentException;
|
||||
import io.minio.errors.InvalidBucketNameException;
|
||||
import io.minio.errors.InvalidEndpointException;
|
||||
import io.minio.errors.InvalidPortException;
|
||||
import io.minio.errors.InvalidResponseException;
|
||||
import io.minio.errors.NoResponseException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.cache.ConfigType;
|
||||
import net.runelite.cache.IndexType;
|
||||
import net.runelite.cache.definitions.ItemDefinition;
|
||||
import net.runelite.cache.definitions.loaders.ItemLoader;
|
||||
import net.runelite.cache.fs.ArchiveFiles;
|
||||
import net.runelite.cache.fs.Container;
|
||||
import net.runelite.cache.fs.FSFile;
|
||||
import net.runelite.http.service.cache.beans.ArchiveEntry;
|
||||
import net.runelite.http.service.cache.beans.CacheEntry;
|
||||
import net.runelite.http.service.cache.beans.FileEntry;
|
||||
import net.runelite.http.service.cache.beans.IndexEntry;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.ResultSetIterable;
|
||||
import org.sql2o.Sql2o;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class CacheService
|
||||
{
|
||||
@Autowired
|
||||
@Qualifier("Runelite Cache SQL2O")
|
||||
private Sql2o sql2o;
|
||||
|
||||
@Value("${minio.bucket}")
|
||||
private String minioBucket;
|
||||
|
||||
private final MinioClient minioClient;
|
||||
|
||||
@Autowired
|
||||
public CacheService(
|
||||
@Value("${minio.endpoint}") String minioEndpoint,
|
||||
@Value("${minio.accesskey}") String accessKey,
|
||||
@Value("${minio.secretkey}") String secretKey
|
||||
) throws InvalidEndpointException, InvalidPortException
|
||||
{
|
||||
this.minioClient = new MinioClient(minioEndpoint, accessKey, secretKey);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MinioClient minioClient()
|
||||
{
|
||||
return minioClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve archive from storage
|
||||
*
|
||||
* @param archiveEntry
|
||||
* @return
|
||||
*/
|
||||
public byte[] getArchive(ArchiveEntry archiveEntry)
|
||||
{
|
||||
String hashStr = BaseEncoding.base16().encode(archiveEntry.getHash());
|
||||
String path = new StringBuilder()
|
||||
.append(hashStr, 0, 2)
|
||||
.append('/')
|
||||
.append(hashStr.substring(2))
|
||||
.toString();
|
||||
|
||||
try (InputStream in = minioClient.getObject(minioBucket, path))
|
||||
{
|
||||
return ByteStreams.toByteArray(in);
|
||||
}
|
||||
catch (InvalidBucketNameException | NoSuchAlgorithmException | InsufficientDataException | IOException | InvalidKeyException | NoResponseException | XmlPullParserException | ErrorResponseException | InternalException | InvalidArgumentException | InvalidResponseException ex)
|
||||
{
|
||||
log.warn(null, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ArchiveFiles getArchiveFiles(ArchiveEntry archiveEntry) throws IOException
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
|
||||
try (Connection con = sql2o.open();
|
||||
ResultSetIterable<FileEntry> files = cacheDao.findFilesForArchive(con, archiveEntry))
|
||||
{
|
||||
byte[] archiveData = getArchive(archiveEntry);
|
||||
|
||||
if (archiveData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Container result = Container.decompress(archiveData, null);
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] decompressedData = result.data;
|
||||
|
||||
ArchiveFiles archiveFiles = new ArchiveFiles();
|
||||
for (FileEntry fileEntry : files)
|
||||
{
|
||||
FSFile file = new FSFile(fileEntry.getFileId());
|
||||
archiveFiles.addFile(file);
|
||||
file.setNameHash(fileEntry.getNameHash());
|
||||
}
|
||||
archiveFiles.loadContents(decompressedData);
|
||||
return archiveFiles;
|
||||
}
|
||||
}
|
||||
|
||||
public List<CacheEntry> listCaches()
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
return cacheDao.listCaches(con);
|
||||
}
|
||||
}
|
||||
|
||||
public CacheEntry findCache(int cacheId)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
return cacheDao.findCache(con, cacheId);
|
||||
}
|
||||
}
|
||||
|
||||
public CacheEntry findMostRecent()
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
return cacheDao.findMostRecent(con);
|
||||
}
|
||||
}
|
||||
|
||||
public List<IndexEntry> findIndexesForCache(CacheEntry cacheEntry)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
return cacheDao.findIndexesForCache(con, cacheEntry);
|
||||
}
|
||||
}
|
||||
|
||||
public IndexEntry findIndexForCache(CacheEntry cahceEntry, int indexId)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
return cacheDao.findIndexForCache(con, cahceEntry, indexId);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ArchiveEntry> findArchivesForIndex(IndexEntry indexEntry)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
ResultSetIterable<ArchiveEntry> archiveEntries = cacheDao.findArchivesForIndex(con, indexEntry);
|
||||
List<ArchiveEntry> archives = new ArrayList<>();
|
||||
Iterables.addAll(archives, archiveEntries);
|
||||
return archives;
|
||||
}
|
||||
}
|
||||
|
||||
public ArchiveEntry findArchiveForIndex(IndexEntry indexEntry, int archiveId)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
return cacheDao.findArchiveForIndex(con, indexEntry, archiveId);
|
||||
}
|
||||
}
|
||||
|
||||
public ArchiveEntry findArchiveForTypeAndName(CacheEntry cache, IndexType index, int nameHash)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
CacheDAO cacheDao = new CacheDAO();
|
||||
return cacheDao.findArchiveByName(con, cache, index, nameHash);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ItemDefinition> getItems() throws IOException
|
||||
{
|
||||
CacheEntry cache = findMostRecent();
|
||||
if (cache == null)
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
IndexEntry indexEntry = findIndexForCache(cache, IndexType.CONFIGS.getNumber());
|
||||
ArchiveEntry archiveEntry = findArchiveForIndex(indexEntry, ConfigType.ITEM.getId());
|
||||
ArchiveFiles archiveFiles = getArchiveFiles(archiveEntry);
|
||||
final ItemLoader itemLoader = new ItemLoader();
|
||||
final List<ItemDefinition> result = new ArrayList<>(archiveFiles.getFiles().size());
|
||||
for (FSFile file : archiveFiles.getFiles())
|
||||
{
|
||||
ItemDefinition itemDef = itemLoader.load(file.getFileId(), file.getContents());
|
||||
result.add(itemDef);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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.cache.beans;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ArchiveEntry
|
||||
{
|
||||
private int id;
|
||||
private int archiveId;
|
||||
private int nameHash;
|
||||
private int crc;
|
||||
private int revision;
|
||||
private byte[] hash;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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.cache.beans;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CacheEntry
|
||||
{
|
||||
private int id;
|
||||
private int revision;
|
||||
private Instant date;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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.cache.beans;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FileEntry
|
||||
{
|
||||
private int id;
|
||||
private int archiveId;
|
||||
private int fileId;
|
||||
private int nameHash;
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* 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.cache.beans;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class IndexEntry
|
||||
{
|
||||
private int id;
|
||||
private int indexId;
|
||||
private int crc;
|
||||
private int revision;
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.chat;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import net.runelite.http.api.chat.Duels;
|
||||
import net.runelite.http.api.chat.LayoutRoom;
|
||||
import net.runelite.http.api.chat.Task;
|
||||
import net.runelite.http.service.util.exception.NotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/chat")
|
||||
public class ChatController
|
||||
{
|
||||
private static final Pattern STRING_VALIDATION = Pattern.compile("[^a-zA-Z0-9' -]");
|
||||
private static final int STRING_MAX_LENGTH = 50;
|
||||
private static final int MAX_LAYOUT_ROOMS = 16;
|
||||
|
||||
private final Cache<KillCountKey, Integer> killCountCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(2, TimeUnit.MINUTES)
|
||||
.maximumSize(128L)
|
||||
.build();
|
||||
|
||||
@Autowired
|
||||
private ChatService chatService;
|
||||
|
||||
@PostMapping("/kc")
|
||||
public void submitKc(@RequestParam String name, @RequestParam String boss, @RequestParam int kc)
|
||||
{
|
||||
if (kc <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
chatService.setKc(name, boss, kc);
|
||||
killCountCache.put(new KillCountKey(name, boss), kc);
|
||||
}
|
||||
|
||||
@GetMapping("/kc")
|
||||
public int getKc(@RequestParam String name, @RequestParam String boss)
|
||||
{
|
||||
Integer kc = killCountCache.getIfPresent(new KillCountKey(name, boss));
|
||||
if (kc == null)
|
||||
{
|
||||
kc = chatService.getKc(name, boss);
|
||||
if (kc != null)
|
||||
{
|
||||
killCountCache.put(new KillCountKey(name, boss), kc);
|
||||
}
|
||||
}
|
||||
|
||||
if (kc == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return kc;
|
||||
}
|
||||
|
||||
@PostMapping("/qp")
|
||||
public void submitQp(@RequestParam String name, @RequestParam int qp)
|
||||
{
|
||||
if (qp < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
chatService.setQp(name, qp);
|
||||
}
|
||||
|
||||
@GetMapping("/qp")
|
||||
public int getQp(@RequestParam String name)
|
||||
{
|
||||
Integer kc = chatService.getQp(name);
|
||||
if (kc == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return kc;
|
||||
}
|
||||
|
||||
@PostMapping("/gc")
|
||||
public void submitGc(@RequestParam String name, @RequestParam int gc)
|
||||
{
|
||||
if (gc < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
chatService.setGc(name, gc);
|
||||
}
|
||||
|
||||
@GetMapping("/gc")
|
||||
public int getKc(@RequestParam String name)
|
||||
{
|
||||
Integer gc = chatService.getGc(name);
|
||||
if (gc == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return gc;
|
||||
}
|
||||
|
||||
@PostMapping("/task")
|
||||
public void submitTask(@RequestParam String name, @RequestParam("task") String taskName, @RequestParam int amount,
|
||||
@RequestParam int initialAmount, @RequestParam String location)
|
||||
{
|
||||
Matcher mTask = STRING_VALIDATION.matcher(taskName);
|
||||
Matcher mLocation = STRING_VALIDATION.matcher(location);
|
||||
if (mTask.find() || taskName.length() > STRING_MAX_LENGTH ||
|
||||
mLocation.find() || location.length() > STRING_MAX_LENGTH)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Task task = new Task();
|
||||
task.setTask(taskName);
|
||||
task.setAmount(amount);
|
||||
task.setInitialAmount(initialAmount);
|
||||
task.setLocation(location);
|
||||
|
||||
chatService.setTask(name, task);
|
||||
}
|
||||
|
||||
@GetMapping("/task")
|
||||
public Task getTask(@RequestParam String name)
|
||||
{
|
||||
return chatService.getTask(name);
|
||||
}
|
||||
|
||||
@PostMapping("/pb")
|
||||
public void submitPb(@RequestParam String name, @RequestParam String boss, @RequestParam int pb)
|
||||
{
|
||||
if (pb < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
chatService.setPb(name, boss, pb);
|
||||
}
|
||||
|
||||
@GetMapping("/pb")
|
||||
public int getPb(@RequestParam String name, @RequestParam String boss)
|
||||
{
|
||||
Integer pb = chatService.getPb(name, boss);
|
||||
if (pb == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return pb;
|
||||
}
|
||||
|
||||
@PostMapping("/duels")
|
||||
public void submitDuels(@RequestParam String name, @RequestParam int wins,
|
||||
@RequestParam int losses,
|
||||
@RequestParam int winningStreak, @RequestParam int losingStreak)
|
||||
{
|
||||
if (wins < 0 || losses < 0 || winningStreak < 0 || losingStreak < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Duels duels = new Duels();
|
||||
duels.setWins(wins);
|
||||
duels.setLosses(losses);
|
||||
duels.setWinningStreak(winningStreak);
|
||||
duels.setLosingStreak(losingStreak);
|
||||
|
||||
chatService.setDuels(name, duels);
|
||||
}
|
||||
|
||||
@GetMapping("/duels")
|
||||
public Duels getDuels(@RequestParam String name)
|
||||
{
|
||||
Duels duels = chatService.getDuels(name);
|
||||
if (duels == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return duels;
|
||||
}
|
||||
|
||||
@PostMapping("/layout")
|
||||
public void submitLayout(@RequestParam String name, @RequestBody LayoutRoom[] rooms)
|
||||
{
|
||||
if (rooms.length > MAX_LAYOUT_ROOMS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
chatService.setLayout(name, rooms);
|
||||
}
|
||||
|
||||
@GetMapping("/layout")
|
||||
public LayoutRoom[] getLayout(@RequestParam String name)
|
||||
{
|
||||
LayoutRoom[] layout = chatService.getLayout(name);
|
||||
|
||||
if (layout == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.chat;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.runelite.http.api.chat.LayoutRoom;
|
||||
import net.runelite.http.api.chat.Task;
|
||||
import net.runelite.http.api.chat.Duels;
|
||||
import net.runelite.http.service.util.redis.RedisPool;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
@Service
|
||||
public class ChatService
|
||||
{
|
||||
private static final Duration EXPIRE = Duration.ofMinutes(2);
|
||||
|
||||
private final RedisPool jedisPool;
|
||||
|
||||
@Autowired
|
||||
public ChatService(RedisPool jedisPool)
|
||||
{
|
||||
this.jedisPool = jedisPool;
|
||||
}
|
||||
|
||||
public Integer getKc(String name, String boss)
|
||||
{
|
||||
String value;
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
value = jedis.get("kc." + name + "." + boss);
|
||||
}
|
||||
return value == null ? null : Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public void setKc(String name, String boss, int kc)
|
||||
{
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.setex("kc." + name + "." + boss, (int) EXPIRE.getSeconds(), Integer.toString(kc));
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getQp(String name)
|
||||
{
|
||||
String value;
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
value = jedis.get("qp." + name);
|
||||
}
|
||||
return value == null ? null : Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public void setQp(String name, int qp)
|
||||
{
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.setex("qp." + name, (int) EXPIRE.getSeconds(), Integer.toString(qp));
|
||||
}
|
||||
}
|
||||
|
||||
public Task getTask(String name)
|
||||
{
|
||||
Map<String, String> map;
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
map = jedis.hgetAll("task." + name);
|
||||
}
|
||||
|
||||
if (map.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Task task = new Task();
|
||||
task.setTask(map.get("task"));
|
||||
task.setAmount(Integer.parseInt(map.get("amount")));
|
||||
task.setInitialAmount(Integer.parseInt(map.get("initialAmount")));
|
||||
task.setLocation(map.get("location"));
|
||||
return task;
|
||||
}
|
||||
|
||||
public void setTask(String name, Task task)
|
||||
{
|
||||
Map<String, String> taskMap = ImmutableMap.<String, String>builderWithExpectedSize(4)
|
||||
.put("task", task.getTask())
|
||||
.put("amount", Integer.toString(task.getAmount()))
|
||||
.put("initialAmount", Integer.toString(task.getInitialAmount()))
|
||||
.put("location", task.getLocation())
|
||||
.build();
|
||||
|
||||
String key = "task." + name;
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.hmset(key, taskMap);
|
||||
jedis.expire(key, (int) EXPIRE.getSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getPb(String name, String boss)
|
||||
{
|
||||
String value;
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
value = jedis.get("pb." + boss + "." + name);
|
||||
}
|
||||
return value == null ? null : Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public void setPb(String name, String boss, int pb)
|
||||
{
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.setex("pb." + boss + "." + name, (int) EXPIRE.getSeconds(), Integer.toString(pb));
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getGc(String name)
|
||||
{
|
||||
String value;
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
value = jedis.get("gc." + name);
|
||||
}
|
||||
return value == null ? null : Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public void setGc(String name, int gc)
|
||||
{
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.setex("gc." + name, (int) EXPIRE.getSeconds(), Integer.toString(gc));
|
||||
}
|
||||
}
|
||||
|
||||
public Duels getDuels(String name)
|
||||
{
|
||||
Map<String, String> map;
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
map = jedis.hgetAll("duels." + name);
|
||||
}
|
||||
|
||||
if (map.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Duels duels = new Duels();
|
||||
duels.setWins(Integer.parseInt(map.get("wins")));
|
||||
duels.setLosses(Integer.parseInt(map.get("losses")));
|
||||
duels.setWinningStreak(Integer.parseInt(map.get("winningStreak")));
|
||||
duels.setLosingStreak(Integer.parseInt(map.get("losingStreak")));
|
||||
return duels;
|
||||
}
|
||||
|
||||
public void setDuels(String name, Duels duels)
|
||||
{
|
||||
Map<String, String> duelsMap = ImmutableMap.<String, String>builderWithExpectedSize(4)
|
||||
.put("wins", Integer.toString(duels.getWins()))
|
||||
.put("losses", Integer.toString(duels.getLosses()))
|
||||
.put("winningStreak", Integer.toString(duels.getWinningStreak()))
|
||||
.put("losingStreak", Integer.toString(duels.getLosingStreak()))
|
||||
.build();
|
||||
|
||||
String key = "duels." + name;
|
||||
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.hmset(key, duelsMap);
|
||||
jedis.expire(key, (int) EXPIRE.getSeconds());
|
||||
}
|
||||
}
|
||||
|
||||
public LayoutRoom[] getLayout(String name)
|
||||
{
|
||||
String layout;
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
layout = jedis.get("layout." + name);
|
||||
}
|
||||
|
||||
if (layout == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> roomList = Splitter.on(' ').splitToList(layout);
|
||||
return roomList.stream()
|
||||
.map(LayoutRoom::valueOf)
|
||||
.toArray(LayoutRoom[]::new);
|
||||
}
|
||||
|
||||
public void setLayout(String name, LayoutRoom[] rooms)
|
||||
{
|
||||
try (Jedis jedis = jedisPool.getResource())
|
||||
{
|
||||
jedis.setex("layout." + name, (int) EXPIRE.getSeconds(), Joiner.on(' ').join(rooms));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.chat;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
class KillCountKey
|
||||
{
|
||||
private String username;
|
||||
private String boss;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.runelite.http.api.config.Configuration;
|
||||
import net.runelite.http.service.account.AuthFilter;
|
||||
import net.runelite.http.service.account.beans.SessionEntry;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/config")
|
||||
public class ConfigController
|
||||
{
|
||||
private final ConfigService configService;
|
||||
private final AuthFilter authFilter;
|
||||
|
||||
@Autowired
|
||||
public ConfigController(ConfigService configService, AuthFilter authFilter)
|
||||
{
|
||||
this.configService = configService;
|
||||
this.authFilter = authFilter;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Configuration get(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
SessionEntry session = authFilter.handle(request, response);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return configService.get(session.getUser());
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/{key:.+}", method = PUT)
|
||||
public void setKey(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@PathVariable String key,
|
||||
@RequestBody(required = false) String value
|
||||
) throws IOException
|
||||
{
|
||||
SessionEntry session = authFilter.handle(request, response);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configService.setKey(session.getUser(), key, value))
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/{key:.+}", method = DELETE)
|
||||
public void unsetKey(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@PathVariable String key
|
||||
) throws IOException
|
||||
{
|
||||
SessionEntry session = authFilter.handle(request, response);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!configService.unsetKey(session.getUser(), key))
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2019, 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.config;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import static com.mongodb.client.model.Filters.eq;
|
||||
import com.mongodb.client.model.IndexOptions;
|
||||
import com.mongodb.client.model.Indexes;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import static com.mongodb.client.model.Updates.set;
|
||||
import static com.mongodb.client.model.Updates.unset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.api.config.ConfigEntry;
|
||||
import net.runelite.http.api.config.Configuration;
|
||||
import org.bson.Document;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ConfigService
|
||||
{
|
||||
private static final int MAX_DEPTH = 8;
|
||||
private static final int MAX_VALUE_LENGTH = 262144;
|
||||
|
||||
private final Gson GSON = RuneLiteAPI.GSON;
|
||||
private final UpdateOptions upsertUpdateOptions = new UpdateOptions().upsert(true);
|
||||
|
||||
private final MongoCollection<Document> mongoCollection;
|
||||
|
||||
@Autowired
|
||||
public ConfigService(
|
||||
MongoClient mongoClient,
|
||||
@Value("${mongo.database}") String databaseName
|
||||
)
|
||||
{
|
||||
|
||||
MongoDatabase database = mongoClient.getDatabase(databaseName);
|
||||
MongoCollection<Document> collection = database.getCollection("config");
|
||||
this.mongoCollection = collection;
|
||||
|
||||
// Create unique index on _userId
|
||||
IndexOptions indexOptions = new IndexOptions().unique(true);
|
||||
collection.createIndex(Indexes.ascending("_userId"), indexOptions);
|
||||
}
|
||||
|
||||
private Document getConfig(int userId)
|
||||
{
|
||||
return mongoCollection.find(eq("_userId", userId)).first();
|
||||
}
|
||||
|
||||
public Configuration get(int userId)
|
||||
{
|
||||
Map<String, Object> configMap = getConfig(userId);
|
||||
|
||||
if (configMap == null || configMap.isEmpty())
|
||||
{
|
||||
return new Configuration(Collections.emptyList());
|
||||
}
|
||||
|
||||
List<ConfigEntry> config = new ArrayList<>();
|
||||
|
||||
for (String group : configMap.keySet())
|
||||
{
|
||||
// Reserved keys
|
||||
if (group.startsWith("_") || group.startsWith("$"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, Object> groupMap = (Map) configMap.get(group);
|
||||
|
||||
for (Map.Entry<String, Object> entry : groupMap.entrySet())
|
||||
{
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
if (value instanceof Map || value instanceof Collection)
|
||||
{
|
||||
value = GSON.toJson(entry.getValue());
|
||||
}
|
||||
else if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ConfigEntry configEntry = new ConfigEntry();
|
||||
configEntry.setKey(group + "." + key.replace(':', '.'));
|
||||
configEntry.setValue(value.toString());
|
||||
config.add(configEntry);
|
||||
}
|
||||
}
|
||||
|
||||
return new Configuration(config);
|
||||
}
|
||||
|
||||
public boolean setKey(
|
||||
int userId,
|
||||
String key,
|
||||
@Nullable String value
|
||||
)
|
||||
{
|
||||
if (key.startsWith("$") || key.startsWith("_"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] split = key.split("\\.", 2);
|
||||
if (split.length != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validateJson(value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Object jsonValue = parseJsonString(value);
|
||||
mongoCollection.updateOne(eq("_userId", userId),
|
||||
set(split[0] + "." + split[1].replace('.', ':'), jsonValue),
|
||||
upsertUpdateOptions);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean unsetKey(
|
||||
int userId,
|
||||
String key
|
||||
)
|
||||
{
|
||||
if (key.startsWith("$") || key.startsWith("_"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] split = key.split("\\.", 2);
|
||||
if (split.length != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
mongoCollection.updateOne(eq("_userId", userId),
|
||||
unset(split[0] + "." + split[1].replace('.', ':')));
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static Object parseJsonString(String value)
|
||||
{
|
||||
Object jsonValue;
|
||||
try
|
||||
{
|
||||
jsonValue = RuneLiteAPI.GSON.fromJson(value, Object.class);
|
||||
|
||||
if (jsonValue instanceof Double || jsonValue instanceof Float)
|
||||
{
|
||||
Number number = (Number) jsonValue;
|
||||
if (Math.floor(number.doubleValue()) == number.doubleValue() && !Double.isInfinite(number.doubleValue()))
|
||||
{
|
||||
// value is an int or long. 'number' might be truncated so parse it from 'value'
|
||||
try
|
||||
{
|
||||
jsonValue = Integer.parseInt(value);
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
jsonValue = Long.parseLong(value);
|
||||
}
|
||||
catch (NumberFormatException ex2)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JsonSyntaxException ex)
|
||||
{
|
||||
jsonValue = value;
|
||||
}
|
||||
return jsonValue;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean validateJson(String value)
|
||||
{
|
||||
try
|
||||
{
|
||||
// I couldn't figure out a better way to do this than a second json parse
|
||||
JsonElement jsonElement = RuneLiteAPI.GSON.fromJson(value, JsonElement.class);
|
||||
return validateObject(jsonElement, 1);
|
||||
}
|
||||
catch (JsonSyntaxException ex)
|
||||
{
|
||||
// the client submits the string representation of objects which is not always valid json,
|
||||
// eg. a value with a ':' in it. We just ignore it now. We can't json encode the values client
|
||||
// side due to them already being strings, which prevents gson from being able to convert them
|
||||
// to ints/floats/maps etc.
|
||||
return value.length() < MAX_VALUE_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean validateObject(JsonElement jsonElement, int depth)
|
||||
{
|
||||
if (depth >= MAX_DEPTH)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (jsonElement.isJsonObject())
|
||||
{
|
||||
JsonObject jsonObject = jsonElement.getAsJsonObject();
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet())
|
||||
{
|
||||
JsonElement element = entry.getValue();
|
||||
|
||||
if (!validateObject(element, depth + 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (jsonElement.isJsonArray())
|
||||
{
|
||||
JsonArray jsonArray = jsonElement.getAsJsonArray();
|
||||
|
||||
for (int i = 0; i < jsonArray.size(); ++i)
|
||||
{
|
||||
JsonElement element = jsonArray.get(i);
|
||||
|
||||
if (!validateObject(element, depth + 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (jsonElement.isJsonPrimitive())
|
||||
{
|
||||
JsonPrimitive jsonPrimitive = jsonElement.getAsJsonPrimitive();
|
||||
String value = jsonPrimitive.getAsString();
|
||||
if (value.length() >= MAX_VALUE_LENGTH)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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.examine;
|
||||
|
||||
import static net.runelite.http.service.examine.ExamineType.ITEM;
|
||||
import static net.runelite.http.service.examine.ExamineType.NPC;
|
||||
import static net.runelite.http.service.examine.ExamineType.OBJECT;
|
||||
import net.runelite.http.service.item.ItemEntry;
|
||||
import net.runelite.http.service.item.ItemService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import static org.springframework.web.bind.annotation.RequestMethod.POST;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/examine")
|
||||
public class ExamineController
|
||||
{
|
||||
private final ExamineService examineService;
|
||||
private final ItemService itemService;
|
||||
|
||||
@Autowired
|
||||
public ExamineController(ExamineService examineService, ItemService itemService)
|
||||
{
|
||||
this.examineService = examineService;
|
||||
this.itemService = itemService;
|
||||
}
|
||||
|
||||
@GetMapping("/npc/{id}")
|
||||
public String getNpc(@PathVariable int id)
|
||||
{
|
||||
return examineService.get(NPC, id);
|
||||
}
|
||||
|
||||
@GetMapping("/object/{id}")
|
||||
public String getObject(@PathVariable int id)
|
||||
{
|
||||
return examineService.get(OBJECT, id);
|
||||
}
|
||||
|
||||
@GetMapping("/item/{id}")
|
||||
public String getItem(@PathVariable int id)
|
||||
{
|
||||
// Tradeable item examine info is available from the Jagex item API
|
||||
ItemEntry item = itemService.getItem(id);
|
||||
if (item != null)
|
||||
{
|
||||
return item.getDescription();
|
||||
}
|
||||
|
||||
return examineService.get(ITEM, id);
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/npc/{id}", method = POST)
|
||||
public void submitNpc(@PathVariable int id, @RequestBody String examine)
|
||||
{
|
||||
examineService.insert(NPC, id, examine);
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/object/{id}", method = POST)
|
||||
public void submitObject(@PathVariable int id, @RequestBody String examine)
|
||||
{
|
||||
examineService.insert(OBJECT, id, examine);
|
||||
}
|
||||
|
||||
@RequestMapping(path = "/item/{id}", method = POST)
|
||||
public void submitItem(@PathVariable int id, @RequestBody String examine)
|
||||
{
|
||||
examineService.insert(ITEM, id, examine);
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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.examine;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public class ExamineEntry
|
||||
{
|
||||
private ExamineType type;
|
||||
private int id;
|
||||
private Instant time;
|
||||
private int count;
|
||||
private String text;
|
||||
|
||||
public ExamineType getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(ExamineType type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Instant getTime()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(Instant time)
|
||||
{
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public int getCount()
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count)
|
||||
{
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public String getText()
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.examine;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@Service
|
||||
public class ExamineService
|
||||
{
|
||||
private static final String CREATE_EXAMINE = "CREATE TABLE IF NOT EXISTS `examine` (\n"
|
||||
+ " `type` enum('OBJECT','NPC','ITEM') NOT NULL,\n"
|
||||
+ " `id` int(11) NOT NULL,\n"
|
||||
+ " `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n"
|
||||
+ " `count` int(11) NOT NULL,\n"
|
||||
+ " `text` tinytext NOT NULL,\n"
|
||||
+ " UNIQUE KEY `type` (`type`,`id`,`text`(64))\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
private final Sql2o sql2o;
|
||||
|
||||
@Autowired
|
||||
public ExamineService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery(CREATE_EXAMINE)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public String get(ExamineType type, int id)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
ExamineEntry entry = con.createQuery("select text from examine where type = :type and id = :id "
|
||||
+ "order by count desc limit 1")
|
||||
.addParameter("type", type.toString())
|
||||
.addParameter("id", id)
|
||||
.executeAndFetchFirst(ExamineEntry.class);
|
||||
|
||||
if (entry != null)
|
||||
{
|
||||
return entry.getText();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void insert(ExamineType type, int id, String examine)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("insert into examine (type, id, time, count, text) values "
|
||||
+ "(:type, :id, :time, :count, :text) on duplicate key update count = count + 1")
|
||||
.addParameter("type", type.toString())
|
||||
.addParameter("id", id)
|
||||
.addParameter("time", Timestamp.from(Instant.now()))
|
||||
.addParameter("count", 1)
|
||||
.addParameter("text", examine)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.examine;
|
||||
|
||||
public enum ExamineType
|
||||
{
|
||||
OBJECT,
|
||||
NPC,
|
||||
ITEM;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.feed;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.http.api.feed.FeedItem;
|
||||
import net.runelite.http.api.feed.FeedResult;
|
||||
import net.runelite.http.service.feed.blog.BlogService;
|
||||
import net.runelite.http.service.feed.osrsnews.OSRSNewsService;
|
||||
import net.runelite.http.service.feed.twitter.TwitterService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/feed")
|
||||
@Slf4j
|
||||
public class FeedController
|
||||
{
|
||||
private final BlogService blogService;
|
||||
private final TwitterService twitterService;
|
||||
private final OSRSNewsService osrsNewsService;
|
||||
|
||||
private static class MemoizedFeed
|
||||
{
|
||||
final FeedResult feedResult;
|
||||
final String hash;
|
||||
|
||||
MemoizedFeed(FeedResult feedResult)
|
||||
{
|
||||
this.feedResult = feedResult;
|
||||
|
||||
Hasher hasher = Hashing.sha256().newHasher();
|
||||
for (FeedItem itemPrice : feedResult.getItems())
|
||||
{
|
||||
hasher.putBytes(itemPrice.getTitle().getBytes()).putBytes(itemPrice.getContent().getBytes());
|
||||
}
|
||||
HashCode code = hasher.hash();
|
||||
hash = code.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private MemoizedFeed memoizedFeed;
|
||||
|
||||
@Autowired
|
||||
public FeedController(BlogService blogService, TwitterService twitterService, OSRSNewsService osrsNewsService)
|
||||
{
|
||||
this.blogService = blogService;
|
||||
this.twitterService = twitterService;
|
||||
this.osrsNewsService = osrsNewsService;
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 10 * 60 * 1000)
|
||||
public void updateFeed()
|
||||
{
|
||||
List<FeedItem> items = new ArrayList<>();
|
||||
|
||||
try
|
||||
{
|
||||
items.addAll(blogService.getBlogPosts());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("unable to fetch blogs", e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
items.addAll(twitterService.getTweets());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("unable to fetch tweets", e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
items.addAll(osrsNewsService.getNews());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("unable to fetch news", e);
|
||||
}
|
||||
|
||||
memoizedFeed = new MemoizedFeed(new FeedResult(items));
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<FeedResult> getFeed()
|
||||
{
|
||||
if (memoizedFeed == null)
|
||||
{
|
||||
return ResponseEntity.notFound()
|
||||
.build();
|
||||
}
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.eTag(memoizedFeed.hash)
|
||||
.cacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES).cachePublic())
|
||||
.body(memoizedFeed.feedResult);
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.feed.blog;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.api.feed.FeedItem;
|
||||
import net.runelite.http.api.feed.FeedItemType;
|
||||
import net.runelite.http.service.util.exception.InternalServerErrorException;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
@Service
|
||||
public class BlogService
|
||||
{
|
||||
private static final HttpUrl RSS_URL = HttpUrl.parse("https://runelite.net/atom.xml");
|
||||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
|
||||
|
||||
public List<FeedItem> getBlogPosts() throws IOException
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(RSS_URL)
|
||||
.build();
|
||||
|
||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
throw new IOException("Error getting blog posts: " + response);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
InputStream in = response.body().byteStream();
|
||||
Document document = DocumentBuilderFactory.newInstance()
|
||||
.newDocumentBuilder()
|
||||
.parse(in);
|
||||
|
||||
Element documentElement = document.getDocumentElement();
|
||||
NodeList documentItems = documentElement.getElementsByTagName("entry");
|
||||
|
||||
List<FeedItem> items = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < Math.min(documentItems.getLength(), 3); i++)
|
||||
{
|
||||
Node item = documentItems.item(i);
|
||||
NodeList children = item.getChildNodes();
|
||||
|
||||
String title = null;
|
||||
String summary = null;
|
||||
String link = null;
|
||||
long timestamp = -1;
|
||||
|
||||
for (int j = 0; j < children.getLength(); j++)
|
||||
{
|
||||
Node childItem = children.item(j);
|
||||
String nodeName = childItem.getNodeName();
|
||||
|
||||
switch (nodeName)
|
||||
{
|
||||
case "title":
|
||||
title = childItem.getTextContent();
|
||||
break;
|
||||
case "summary":
|
||||
summary = childItem.getTextContent().replace("\n", "").trim();
|
||||
break;
|
||||
case "link":
|
||||
link = childItem.getAttributes().getNamedItem("href").getTextContent();
|
||||
break;
|
||||
case "updated":
|
||||
timestamp = DATE_FORMAT.parse(childItem.getTextContent()).getTime();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (title == null || summary == null || link == null || timestamp == -1)
|
||||
{
|
||||
throw new InternalServerErrorException("Failed to find title, summary, link and/or timestamp in the blog post feed");
|
||||
}
|
||||
|
||||
items.add(new FeedItem(FeedItemType.BLOG_POST, title, summary, link, timestamp));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
catch (ParserConfigurationException | SAXException | ParseException e)
|
||||
{
|
||||
throw new InternalServerErrorException("Failed to parse blog posts: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.feed.osrsnews;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.api.feed.FeedItem;
|
||||
import net.runelite.http.api.feed.FeedItemType;
|
||||
import net.runelite.http.service.util.exception.InternalServerErrorException;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
@Service
|
||||
public class OSRSNewsService
|
||||
{
|
||||
private static final HttpUrl RSS_URL = HttpUrl.parse("https://services.runescape.com/m=news/latest_news.rss?oldschool=true");
|
||||
private static final SimpleDateFormat PUB_DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy '00:00:00 GMT'", Locale.US);
|
||||
|
||||
public List<FeedItem> getNews() throws IOException
|
||||
{
|
||||
Request request = new Request.Builder()
|
||||
.url(RSS_URL)
|
||||
.build();
|
||||
|
||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
throw new IOException("Error getting OSRS news: " + response);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
InputStream in = response.body().byteStream();
|
||||
Document document = DocumentBuilderFactory.newInstance()
|
||||
.newDocumentBuilder()
|
||||
.parse(in);
|
||||
|
||||
Element documentElement = document.getDocumentElement();
|
||||
NodeList documentItems = documentElement.getElementsByTagName("item");
|
||||
|
||||
List<FeedItem> items = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < documentItems.getLength(); i++)
|
||||
{
|
||||
Node item = documentItems.item(i);
|
||||
NodeList children = item.getChildNodes();
|
||||
|
||||
String title = null;
|
||||
String description = null;
|
||||
String link = null;
|
||||
long timestamp = -1;
|
||||
|
||||
for (int j = 0; j < children.getLength(); j++)
|
||||
{
|
||||
Node childItem = children.item(j);
|
||||
String nodeName = childItem.getNodeName();
|
||||
|
||||
switch (nodeName)
|
||||
{
|
||||
case "title":
|
||||
title = childItem.getTextContent();
|
||||
break;
|
||||
case "description":
|
||||
description = childItem.getTextContent().replace("\n", "").trim();
|
||||
break;
|
||||
case "link":
|
||||
link = childItem.getTextContent();
|
||||
break;
|
||||
case "pubDate":
|
||||
timestamp = PUB_DATE_FORMAT.parse(childItem.getTextContent()).getTime();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (title == null || description == null || link == null || timestamp == -1)
|
||||
{
|
||||
throw new InternalServerErrorException("Failed to find title, description, link and/or timestamp in the OSRS RSS feed");
|
||||
}
|
||||
|
||||
items.add(new FeedItem(FeedItemType.OSRS_NEWS, title, description, link, timestamp));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
catch (ParserConfigurationException | SAXException | ParseException e)
|
||||
{
|
||||
throw new InternalServerErrorException("Failed to parse OSRS news: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.feed.twitter;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
class TwitterOAuth2TokenResponse
|
||||
{
|
||||
@SerializedName("token_type")
|
||||
private String tokenType;
|
||||
|
||||
@SerializedName("access_token")
|
||||
private String token;
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.feed.twitter;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.api.feed.FeedItem;
|
||||
import net.runelite.http.api.feed.FeedItemType;
|
||||
import net.runelite.http.service.util.exception.InternalServerErrorException;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TwitterService
|
||||
{
|
||||
private static final HttpUrl AUTH_URL = HttpUrl.parse("https://api.twitter.com/oauth2/token");
|
||||
private static final HttpUrl LIST_STATUSES_URL = HttpUrl.parse("https://api.twitter.com/1.1/lists/statuses.json");
|
||||
|
||||
private final String credentials;
|
||||
private final String listId;
|
||||
|
||||
private String token;
|
||||
|
||||
@Autowired
|
||||
public TwitterService(
|
||||
@Value("${runelite.twitter.consumerkey}") String consumerKey,
|
||||
@Value("${runelite.twitter.secretkey}") String consumerSecret,
|
||||
@Value("${runelite.twitter.listid}") String listId
|
||||
)
|
||||
{
|
||||
this.credentials = consumerKey + ":" + consumerSecret;
|
||||
this.listId = listId;
|
||||
}
|
||||
|
||||
public List<FeedItem> getTweets() throws IOException
|
||||
{
|
||||
return getTweets(false);
|
||||
}
|
||||
|
||||
private List<FeedItem> getTweets(boolean hasRetried) throws IOException
|
||||
{
|
||||
if (token == null)
|
||||
{
|
||||
updateToken();
|
||||
}
|
||||
|
||||
HttpUrl url = LIST_STATUSES_URL.newBuilder()
|
||||
.addQueryParameter("list_id", listId)
|
||||
.addQueryParameter("count", "15")
|
||||
.addQueryParameter("include_entities", "false")
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.header("Authorization", "Bearer " + token)
|
||||
.build();
|
||||
|
||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
switch (HttpStatus.valueOf(response.code()))
|
||||
{
|
||||
case BAD_REQUEST:
|
||||
case UNAUTHORIZED:
|
||||
updateToken();
|
||||
if (!hasRetried)
|
||||
{
|
||||
return getTweets(true);
|
||||
}
|
||||
throw new InternalServerErrorException("Could not auth to Twitter after trying once: " + response);
|
||||
default:
|
||||
throw new IOException("Error getting Twitter list: " + response);
|
||||
}
|
||||
}
|
||||
|
||||
InputStream in = response.body().byteStream();
|
||||
Type listType = new TypeToken<List<TwitterStatusesResponseItem>>() {}.getType();
|
||||
List<TwitterStatusesResponseItem> statusesResponse = RuneLiteAPI.GSON
|
||||
.fromJson(new InputStreamReader(in), listType);
|
||||
|
||||
List<FeedItem> items = new ArrayList<>();
|
||||
|
||||
for (TwitterStatusesResponseItem i : statusesResponse)
|
||||
{
|
||||
items.add(new FeedItem(FeedItemType.TWEET,
|
||||
i.getUser().getProfileImageUrl(),
|
||||
i.getUser().getScreenName(),
|
||||
i.getText().replace("\n\n", " ").replaceAll("\n", " "),
|
||||
"https://twitter.com/" + i.getUser().getScreenName() + "/status/" + i.getId(),
|
||||
getTimestampFromSnowflake(i.getId())));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateToken() throws IOException
|
||||
{
|
||||
String encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes());
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(AUTH_URL)
|
||||
.header("Authorization", "Basic " + encodedCredentials)
|
||||
.post(new FormBody.Builder().add("grant_type", "client_credentials").build())
|
||||
.build();
|
||||
|
||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
throw new IOException("Error authing to Twitter: " + response);
|
||||
}
|
||||
|
||||
InputStream in = response.body().byteStream();
|
||||
TwitterOAuth2TokenResponse tokenResponse = RuneLiteAPI.GSON
|
||||
.fromJson(new InputStreamReader(in), TwitterOAuth2TokenResponse.class);
|
||||
|
||||
if (!tokenResponse.getTokenType().equals("bearer"))
|
||||
{
|
||||
throw new InternalServerErrorException("Returned token was not a bearer token");
|
||||
}
|
||||
|
||||
if (tokenResponse.getToken() == null)
|
||||
{
|
||||
throw new InternalServerErrorException("Returned token was null");
|
||||
}
|
||||
|
||||
token = tokenResponse.getToken();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the UTC timestamp from a Twitter snowflake as per
|
||||
* https://github.com/client9/snowflake2time/blob/master/python/snowflake.py#L24
|
||||
*/
|
||||
private long getTimestampFromSnowflake(long snowflake)
|
||||
{
|
||||
return (snowflake >> 22) + 1288834974657L;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.feed.twitter;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
class TwitterStatusesResponseItem
|
||||
{
|
||||
private long id;
|
||||
private String text;
|
||||
private TwitterStatusesResponseItemUser user;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.feed.twitter;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
class TwitterStatusesResponseItemUser
|
||||
{
|
||||
@SerializedName("screen_name")
|
||||
private String screenName;
|
||||
|
||||
@SerializedName("profile_image_url_https")
|
||||
private String profileImageUrl;
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.ge;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.runelite.http.api.ge.GrandExchangeTrade;
|
||||
import net.runelite.http.service.account.AuthFilter;
|
||||
import net.runelite.http.service.account.beans.SessionEntry;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/ge")
|
||||
public class GrandExchangeController
|
||||
{
|
||||
private final GrandExchangeService grandExchangeService;
|
||||
private final AuthFilter authFilter;
|
||||
|
||||
@Autowired
|
||||
public GrandExchangeController(GrandExchangeService grandExchangeService, AuthFilter authFilter)
|
||||
{
|
||||
this.grandExchangeService = grandExchangeService;
|
||||
this.authFilter = authFilter;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public void submit(HttpServletRequest request, HttpServletResponse response, @RequestBody GrandExchangeTrade grandExchangeTrade) throws IOException
|
||||
{
|
||||
SessionEntry session = authFilter.handle(request, response);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
grandExchangeService.add(session.getUser(), grandExchangeTrade);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Collection<GrandExchangeTrade> get(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam(required = false, defaultValue = "1024") int limit,
|
||||
@RequestParam(required = false, defaultValue = "0") int offset) throws IOException
|
||||
{
|
||||
SessionEntry session = authFilter.handle(request, response);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return grandExchangeService.get(session.getUser(), limit, offset).stream()
|
||||
.map(GrandExchangeController::convert)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static GrandExchangeTrade convert(TradeEntry tradeEntry)
|
||||
{
|
||||
GrandExchangeTrade grandExchangeTrade = new GrandExchangeTrade();
|
||||
grandExchangeTrade.setBuy(tradeEntry.getAction() == TradeAction.BUY);
|
||||
grandExchangeTrade.setItemId(tradeEntry.getItem());
|
||||
grandExchangeTrade.setQuantity(tradeEntry.getQuantity());
|
||||
grandExchangeTrade.setPrice(tradeEntry.getPrice());
|
||||
grandExchangeTrade.setTime(tradeEntry.getTime());
|
||||
return grandExchangeTrade;
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public void delete(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
SessionEntry session = authFilter.handle(request, response);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
grandExchangeService.delete(session.getUser());
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.ge;
|
||||
|
||||
import java.util.Collection;
|
||||
import net.runelite.http.api.ge.GrandExchangeTrade;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@Service
|
||||
public class GrandExchangeService
|
||||
{
|
||||
private static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS `ge_trades` (\n" +
|
||||
" `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
|
||||
" `user` int(11) NOT NULL,\n" +
|
||||
" `action` enum('BUY','SELL') NOT NULL,\n" +
|
||||
" `item` int(11) NOT NULL,\n" +
|
||||
" `quantity` int(11) NOT NULL,\n" +
|
||||
" `price` int(11) NOT NULL,\n" +
|
||||
" `time` timestamp NOT NULL DEFAULT current_timestamp(),\n" +
|
||||
" PRIMARY KEY (`id`),\n" +
|
||||
" KEY `user_time` (`user`, `time`),\n" +
|
||||
" KEY `time` (`time`),\n" +
|
||||
" CONSTRAINT `ge_trades_ibfk_1` FOREIGN KEY (`user`) REFERENCES `users` (`id`)\n" +
|
||||
") ENGINE=InnoDB;";
|
||||
|
||||
private final Sql2o sql2o;
|
||||
|
||||
@Autowired
|
||||
public GrandExchangeService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
|
||||
// Ensure necessary tables exist
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery(CREATE_TABLE).executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void add(int userId, GrandExchangeTrade grandExchangeTrade)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("insert into ge_trades (user, action, item, quantity, price) values (:user," +
|
||||
" :action, :item, :quantity, :price)")
|
||||
.addParameter("user", userId)
|
||||
.addParameter("action", grandExchangeTrade.isBuy() ? "BUY" : "SELL")
|
||||
.addParameter("item", grandExchangeTrade.getItemId())
|
||||
.addParameter("quantity", grandExchangeTrade.getQuantity())
|
||||
.addParameter("price", grandExchangeTrade.getPrice())
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<TradeEntry> get(int userId, int limit, int offset)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
return con.createQuery("select id, user, action, item, quantity, price, time from ge_trades where user = :user limit :limit offset :offset")
|
||||
.addParameter("user", userId)
|
||||
.addParameter("limit", limit)
|
||||
.addParameter("offset", offset)
|
||||
.executeAndFetch(TradeEntry.class);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(int userId)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("delete from ge_trades where user = :user")
|
||||
.addParameter("user", userId)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 60 * 60 * 1000)
|
||||
public void expire()
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("delete from ge_trades where time < current_timestamp - interval 1 month")
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.ge;
|
||||
|
||||
enum TradeAction
|
||||
{
|
||||
BUY,
|
||||
SELL;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.ge;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
class TradeEntry
|
||||
{
|
||||
private int id;
|
||||
private int user;
|
||||
private TradeAction action;
|
||||
private int item;
|
||||
private int quantity;
|
||||
private int price;
|
||||
private Instant time;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* 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.hiscore;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import net.runelite.http.api.hiscore.HiscoreEndpoint;
|
||||
import net.runelite.http.api.hiscore.HiscoreResult;
|
||||
import net.runelite.http.api.hiscore.HiscoreSkill;
|
||||
import net.runelite.http.api.hiscore.SingleHiscoreSkillResult;
|
||||
import net.runelite.http.api.hiscore.Skill;
|
||||
import net.runelite.http.service.util.HiscoreEndpointEditor;
|
||||
import net.runelite.http.service.xp.XpTrackerService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/hiscore")
|
||||
public class HiscoreController
|
||||
{
|
||||
@Autowired
|
||||
private HiscoreService hiscoreService;
|
||||
|
||||
@Autowired
|
||||
private XpTrackerService xpTrackerService;
|
||||
|
||||
@GetMapping("/{endpoint}")
|
||||
public HiscoreResult lookup(@PathVariable HiscoreEndpoint endpoint, @RequestParam String username) throws ExecutionException
|
||||
{
|
||||
HiscoreResult result = hiscoreService.lookupUsername(username, endpoint);
|
||||
|
||||
// Submit to xp tracker?
|
||||
switch (endpoint)
|
||||
{
|
||||
case NORMAL:
|
||||
case IRONMAN:
|
||||
case ULTIMATE_IRONMAN:
|
||||
case HARDCORE_IRONMAN:
|
||||
xpTrackerService.update(username, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@GetMapping("/{endpoint}/{skillName}")
|
||||
public SingleHiscoreSkillResult singleSkillLookup(@PathVariable HiscoreEndpoint endpoint, @PathVariable String skillName, @RequestParam String username) throws ExecutionException
|
||||
{
|
||||
HiscoreSkill skill = HiscoreSkill.valueOf(skillName.toUpperCase());
|
||||
|
||||
// RS api only supports looking up all stats
|
||||
HiscoreResult result = hiscoreService.lookupUsername(username, endpoint);
|
||||
|
||||
// Find the skill to return
|
||||
Skill requested = result.getSkill(skill);
|
||||
|
||||
SingleHiscoreSkillResult skillResult = new SingleHiscoreSkillResult();
|
||||
skillResult.setPlayer(username);
|
||||
skillResult.setSkillName(skillName);
|
||||
skillResult.setSkill(requested);
|
||||
|
||||
return skillResult;
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
public void initBinder(WebDataBinder binder)
|
||||
{
|
||||
binder.registerCustomEditor(HiscoreEndpoint.class, new HiscoreEndpointEditor());
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 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 HOLDER 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.hiscore;
|
||||
|
||||
import lombok.Value;
|
||||
import net.runelite.http.api.hiscore.HiscoreEndpoint;
|
||||
|
||||
@Value
|
||||
class HiscoreKey
|
||||
{
|
||||
String username;
|
||||
HiscoreEndpoint endpoint;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* 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.hiscore;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.runelite.http.api.hiscore.HiscoreClient;
|
||||
import net.runelite.http.api.hiscore.HiscoreEndpoint;
|
||||
import net.runelite.http.api.hiscore.HiscoreResult;
|
||||
import okhttp3.HttpUrl;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class HiscoreService
|
||||
{
|
||||
private final HiscoreClient hiscoreClient = new HiscoreClient();
|
||||
private final LoadingCache<HiscoreKey, HiscoreResult> hiscoreCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(128)
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build(
|
||||
new CacheLoader<HiscoreKey, HiscoreResult>()
|
||||
{
|
||||
@Override
|
||||
public HiscoreResult load(HiscoreKey key) throws IOException
|
||||
{
|
||||
return hiscoreClient.lookup(key.getUsername(), key.getEndpoint());
|
||||
}
|
||||
});
|
||||
|
||||
@VisibleForTesting
|
||||
HiscoreResult lookupUsername(String username, HttpUrl httpUrl) throws IOException
|
||||
{
|
||||
return hiscoreClient.lookup(username, httpUrl);
|
||||
}
|
||||
|
||||
public HiscoreResult lookupUsername(String username, HiscoreEndpoint endpoint) throws ExecutionException
|
||||
{
|
||||
return hiscoreCache.get(new HiscoreKey(username, endpoint));
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, 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.item;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.runelite.http.api.item.Item;
|
||||
import net.runelite.http.api.item.ItemPrice;
|
||||
import net.runelite.http.api.item.SearchResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/item")
|
||||
public class ItemController
|
||||
{
|
||||
private static final String RUNELITE_CACHE = "RuneLite-Cache";
|
||||
private static final int MAX_BATCH_LOOKUP = 1024;
|
||||
|
||||
private static class MemoizedPrices
|
||||
{
|
||||
final ItemPrice[] prices;
|
||||
final String hash;
|
||||
|
||||
MemoizedPrices(ItemPrice[] prices)
|
||||
{
|
||||
this.prices = prices;
|
||||
|
||||
Hasher hasher = Hashing.sha256().newHasher();
|
||||
for (ItemPrice itemPrice : prices)
|
||||
{
|
||||
hasher.putInt(itemPrice.getId()).putInt(itemPrice.getPrice());
|
||||
}
|
||||
HashCode code = hasher.hash();
|
||||
hash = code.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private final Cache<Integer, Integer> cachedEmpty = CacheBuilder.newBuilder()
|
||||
.maximumSize(1024L)
|
||||
.build();
|
||||
|
||||
private final ItemService itemService;
|
||||
|
||||
private final Supplier<MemoizedPrices> memoizedPrices;
|
||||
|
||||
@Autowired
|
||||
public ItemController(ItemService itemService)
|
||||
{
|
||||
this.itemService = itemService;
|
||||
|
||||
memoizedPrices = Suppliers.memoizeWithExpiration(() -> new MemoizedPrices(itemService.fetchPrices().stream()
|
||||
.map(priceEntry ->
|
||||
{
|
||||
ItemPrice itemPrice = new ItemPrice();
|
||||
itemPrice.setId(priceEntry.getItem());
|
||||
itemPrice.setName(priceEntry.getName());
|
||||
itemPrice.setPrice(priceEntry.getPrice());
|
||||
itemPrice.setTime(priceEntry.getTime());
|
||||
return itemPrice;
|
||||
})
|
||||
.toArray(ItemPrice[]::new)), 30, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@GetMapping("/{itemId}")
|
||||
public Item getItem(HttpServletResponse response, @PathVariable int itemId)
|
||||
{
|
||||
ItemEntry item = itemService.getItem(itemId);
|
||||
if (item != null)
|
||||
{
|
||||
return item.toItem();
|
||||
}
|
||||
|
||||
itemService.queueItem(itemId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{itemId}/icon", produces = "image/gif")
|
||||
public ResponseEntity<byte[]> getIcon(@PathVariable int itemId)
|
||||
{
|
||||
ItemEntry item = itemService.getItem(itemId);
|
||||
if (item != null && item.getIcon() != null)
|
||||
{
|
||||
return ResponseEntity.ok(item.getIcon());
|
||||
}
|
||||
|
||||
itemService.queueItem(itemId);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@GetMapping(path = "/{itemId}/icon/large", produces = "image/gif")
|
||||
public ResponseEntity<byte[]> getIconLarge(HttpServletResponse response, @PathVariable int itemId)
|
||||
{
|
||||
ItemEntry item = itemService.getItem(itemId);
|
||||
if (item != null && item.getIcon_large() != null)
|
||||
{
|
||||
return ResponseEntity.ok(item.getIcon_large());
|
||||
}
|
||||
|
||||
itemService.queueItem(itemId);
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
@GetMapping("/{itemId}/price")
|
||||
public ResponseEntity<ItemPrice> itemPrice(
|
||||
@PathVariable int itemId,
|
||||
@RequestParam(required = false) Instant time
|
||||
)
|
||||
{
|
||||
if (cachedEmpty.getIfPresent(itemId) != null)
|
||||
{
|
||||
return ResponseEntity.notFound()
|
||||
.header(RUNELITE_CACHE, "HIT")
|
||||
.build();
|
||||
}
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
||||
if (time != null && time.isAfter(now))
|
||||
{
|
||||
time = now;
|
||||
}
|
||||
|
||||
ItemEntry item = itemService.getItem(itemId);
|
||||
if (item == null)
|
||||
{
|
||||
itemService.queueItem(itemId); // queue lookup
|
||||
cachedEmpty.put(itemId, itemId); // cache empty
|
||||
return ResponseEntity.notFound()
|
||||
.header(RUNELITE_CACHE, "MISS")
|
||||
.build();
|
||||
}
|
||||
|
||||
PriceEntry priceEntry = itemService.getPrice(itemId, time);
|
||||
|
||||
if (time != null)
|
||||
{
|
||||
if (priceEntry == null)
|
||||
{
|
||||
// we maybe can't backfill this
|
||||
return ResponseEntity.notFound()
|
||||
.header(RUNELITE_CACHE, "MISS")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
else if (priceEntry == null)
|
||||
{
|
||||
// Price is unknown
|
||||
cachedEmpty.put(itemId, itemId);
|
||||
return ResponseEntity.notFound()
|
||||
.header(RUNELITE_CACHE, "MISS")
|
||||
.build();
|
||||
}
|
||||
|
||||
ItemPrice itemPrice = new ItemPrice();
|
||||
itemPrice.setId(item.getId());
|
||||
itemPrice.setName(item.getName());
|
||||
itemPrice.setPrice(priceEntry.getPrice());
|
||||
itemPrice.setTime(priceEntry.getTime());
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES).cachePublic())
|
||||
.body(itemPrice);
|
||||
}
|
||||
|
||||
@GetMapping("/search")
|
||||
public SearchResult search(@RequestParam String query)
|
||||
{
|
||||
List<ItemEntry> result = itemService.search(query);
|
||||
|
||||
itemService.queueSearch(query);
|
||||
|
||||
SearchResult searchResult = new SearchResult();
|
||||
searchResult.setItems(result.stream()
|
||||
.map(ItemEntry::toItem)
|
||||
.collect(Collectors.toList()));
|
||||
return searchResult;
|
||||
}
|
||||
|
||||
@GetMapping("/price")
|
||||
public ItemPrice[] prices(@RequestParam("id") int[] itemIds)
|
||||
{
|
||||
if (itemIds.length > MAX_BATCH_LOOKUP)
|
||||
{
|
||||
itemIds = Arrays.copyOf(itemIds, MAX_BATCH_LOOKUP);
|
||||
}
|
||||
|
||||
List<PriceEntry> prices = itemService.getPrices(itemIds);
|
||||
|
||||
return prices.stream()
|
||||
.map(priceEntry ->
|
||||
{
|
||||
ItemPrice itemPrice = new ItemPrice();
|
||||
itemPrice.setId(priceEntry.getItem());
|
||||
itemPrice.setName(priceEntry.getName());
|
||||
itemPrice.setPrice(priceEntry.getPrice());
|
||||
itemPrice.setTime(priceEntry.getTime());
|
||||
return itemPrice;
|
||||
})
|
||||
.toArray(ItemPrice[]::new);
|
||||
}
|
||||
|
||||
@GetMapping("/prices")
|
||||
public ResponseEntity<ItemPrice[]> prices()
|
||||
{
|
||||
MemoizedPrices memorizedPrices = this.memoizedPrices.get();
|
||||
return ResponseEntity.ok()
|
||||
.eTag(memorizedPrices.hash)
|
||||
.cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES).cachePublic())
|
||||
.body(memorizedPrices.prices);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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.item;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.Data;
|
||||
import net.runelite.http.api.item.Item;
|
||||
import net.runelite.http.api.item.ItemType;
|
||||
|
||||
@Data
|
||||
public class ItemEntry
|
||||
{
|
||||
private int id;
|
||||
private String name;
|
||||
private String description;
|
||||
private ItemType type;
|
||||
private byte[] icon;
|
||||
private byte[] icon_large;
|
||||
private Instant timestamp;
|
||||
|
||||
public Item toItem()
|
||||
{
|
||||
Item item = new Item();
|
||||
item.setId(id);
|
||||
item.setName(name);
|
||||
item.setDescription(description);
|
||||
item.setType(type);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -1,505 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, 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.item;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.cache.definitions.ItemDefinition;
|
||||
import net.runelite.http.api.RuneLiteAPI;
|
||||
import net.runelite.http.api.item.ItemType;
|
||||
import net.runelite.http.service.cache.CacheService;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Query;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ItemService
|
||||
{
|
||||
private static final String BASE = "https://services.runescape.com/m=itemdb_oldschool";
|
||||
private static final HttpUrl RS_ITEM_URL = HttpUrl.parse(BASE + "/api/catalogue/detail.json");
|
||||
private static final HttpUrl RS_PRICE_URL = HttpUrl.parse(BASE + "/api/graph");
|
||||
private static final HttpUrl RS_SEARCH_URL = HttpUrl.parse(BASE + "/api/catalogue/items.json?category=1");
|
||||
|
||||
private static final String CREATE_ITEMS = "CREATE TABLE IF NOT EXISTS `items` (\n"
|
||||
+ " `id` int(11) NOT NULL,\n"
|
||||
+ " `name` tinytext NOT NULL,\n"
|
||||
+ " `description` tinytext NOT NULL,\n"
|
||||
+ " `type` enum('DEFAULT') NOT NULL,\n"
|
||||
+ " `icon` blob,\n"
|
||||
+ " `icon_large` blob,\n"
|
||||
+ " `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n"
|
||||
+ " PRIMARY KEY (`id`),\n"
|
||||
+ " FULLTEXT idx_name (name)\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
private static final String CREATE_PRICES = "CREATE TABLE IF NOT EXISTS `prices` (\n"
|
||||
+ " `item` int(11) NOT NULL,\n"
|
||||
+ " `price` int(11) NOT NULL,\n"
|
||||
+ " `time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
|
||||
+ " `fetched_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n"
|
||||
+ " UNIQUE KEY `item_time` (`item`,`time`),\n"
|
||||
+ " KEY `item_fetched_time` (`item`,`fetched_time`)\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
private static final int MAX_PENDING = 512;
|
||||
|
||||
private final Sql2o sql2o;
|
||||
private final CacheService cacheService;
|
||||
|
||||
private final ConcurrentLinkedQueue<PendingLookup> pendingLookups = new ConcurrentLinkedQueue<PendingLookup>();
|
||||
private int[] tradeableItems;
|
||||
private final Random random = new Random();
|
||||
|
||||
@Autowired
|
||||
public ItemService(@Qualifier("Runelite SQL2O") Sql2o sql2o,
|
||||
CacheService cacheService)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
this.cacheService = cacheService;
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery(CREATE_ITEMS)
|
||||
.executeUpdate();
|
||||
|
||||
con.createQuery(CREATE_PRICES)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public ItemEntry getItem(int itemId)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
ItemEntry item = con.createQuery("select id, name, description, type, icon, icon_large from items where id = :id")
|
||||
.addParameter("id", itemId)
|
||||
.executeAndFetchFirst(ItemEntry.class);
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
private PriceEntry getPrice(Connection con, int itemId, Instant time)
|
||||
{
|
||||
if (time != null)
|
||||
{
|
||||
return con.createQuery("select item, name, price, time, fetched_time from prices t1 join items t2 on t1.item=t2.id where item = :item and time <= :time order by time desc limit 1")
|
||||
.addParameter("item", itemId)
|
||||
.addParameter("time", time.toString())
|
||||
.executeAndFetchFirst(PriceEntry.class);
|
||||
}
|
||||
else
|
||||
{
|
||||
return con.createQuery("select item, name, price, time, fetched_time from prices t1 join items t2 on t1.item=t2.id where item = :item order by time desc limit 1")
|
||||
.addParameter("item", itemId)
|
||||
.executeAndFetchFirst(PriceEntry.class);
|
||||
}
|
||||
}
|
||||
|
||||
public PriceEntry getPrice(int itemId, Instant time)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
return getPrice(con, itemId, time);
|
||||
}
|
||||
}
|
||||
|
||||
public List<PriceEntry> getPrices(int... itemIds)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
Set<Integer> seen = new HashSet<>();
|
||||
List<PriceEntry> priceEntries = new ArrayList<>(itemIds.length);
|
||||
|
||||
for (int itemId : itemIds)
|
||||
{
|
||||
if (seen.contains(itemId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
seen.add(itemId);
|
||||
|
||||
PriceEntry priceEntry = getPrice(con, itemId, null);
|
||||
|
||||
if (priceEntry == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
priceEntries.add(priceEntry);
|
||||
}
|
||||
|
||||
return priceEntries;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ItemEntry> search(String search)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
return con.createQuery("select id, name, description, type, match (name) against (:search) as score from items "
|
||||
+ "where match (name) against (:search) order by score desc limit 10")
|
||||
.throwOnMappingFailure(false) // otherwise it tries to map 'score'
|
||||
.addParameter("search", search)
|
||||
.executeAndFetch(ItemEntry.class);
|
||||
}
|
||||
}
|
||||
|
||||
public ItemEntry fetchItem(int itemId)
|
||||
{
|
||||
try
|
||||
{
|
||||
RSItem rsItem = fetchRSItem(itemId);
|
||||
byte[] icon = null, iconLarge = null;
|
||||
|
||||
try
|
||||
{
|
||||
icon = fetchImage(rsItem.getIcon());
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("error fetching image", ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
iconLarge = fetchImage(rsItem.getIcon_large());
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("error fetching image", ex);
|
||||
}
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("insert into items (id, name, description, type, icon, icon_large) values (:id,"
|
||||
+ " :name, :description, :type, :icon, :icon_large) ON DUPLICATE KEY UPDATE name = :name,"
|
||||
+ " description = :description, type = :type, icon = :icon, icon_large = :icon_large")
|
||||
.addParameter("id", rsItem.getId())
|
||||
.addParameter("name", rsItem.getName())
|
||||
.addParameter("description", rsItem.getDescription())
|
||||
.addParameter("type", rsItem.getType())
|
||||
.addParameter("icon", icon)
|
||||
.addParameter("icon_large", iconLarge)
|
||||
.executeUpdate();
|
||||
}
|
||||
|
||||
ItemEntry item = new ItemEntry();
|
||||
item.setId(itemId);
|
||||
item.setName(rsItem.getName());
|
||||
item.setDescription(rsItem.getDescription());
|
||||
item.setType(ItemType.of(rsItem.getType()));
|
||||
item.setIcon(icon);
|
||||
item.setIcon_large(iconLarge);
|
||||
return item;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("unable to fetch item {}", itemId, ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<PriceEntry> fetchPrice(int itemId)
|
||||
{
|
||||
RSPrices rsprice;
|
||||
try
|
||||
{
|
||||
rsprice = fetchRSPrices(itemId);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("unable to fetch price for item {}", itemId, ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
List<PriceEntry> entries = new ArrayList<>();
|
||||
Instant now = Instant.now();
|
||||
|
||||
Query query = con.createQuery("insert into prices (item, price, time, fetched_time) values (:item, :price, :time, :fetched_time) "
|
||||
+ "ON DUPLICATE KEY UPDATE price = VALUES(price), fetched_time = VALUES(fetched_time)");
|
||||
|
||||
for (Map.Entry<Long, Integer> entry : rsprice.getDaily().entrySet())
|
||||
{
|
||||
long ts = entry.getKey(); // ms since epoch
|
||||
int price = entry.getValue(); // gp
|
||||
|
||||
Instant time = Instant.ofEpochMilli(ts);
|
||||
|
||||
PriceEntry priceEntry = new PriceEntry();
|
||||
priceEntry.setItem(itemId);
|
||||
priceEntry.setPrice(price);
|
||||
priceEntry.setTime(time);
|
||||
priceEntry.setFetched_time(now);
|
||||
entries.add(priceEntry);
|
||||
|
||||
query
|
||||
.addParameter("item", itemId)
|
||||
.addParameter("price", price)
|
||||
.addParameter("time", time)
|
||||
.addParameter("fetched_time", now)
|
||||
.addToBatch();
|
||||
}
|
||||
|
||||
query.executeBatch();
|
||||
con.commit(false);
|
||||
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
public List<PriceEntry> fetchPrices()
|
||||
{
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
Query query = con.createQuery("select t2.item, t3.name, t2.time, prices.price, prices.fetched_time from (select t1.item as item, max(t1.time) as time from prices t1 group by item) t2 " +
|
||||
" join prices on t2.item=prices.item and t2.time=prices.time" +
|
||||
" join items t3 on t2.item=t3.id");
|
||||
return query.executeAndFetch(PriceEntry.class);
|
||||
}
|
||||
}
|
||||
|
||||
private RSItem fetchRSItem(int itemId) throws IOException
|
||||
{
|
||||
HttpUrl itemUrl = RS_ITEM_URL
|
||||
.newBuilder()
|
||||
.addQueryParameter("item", "" + itemId)
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(itemUrl)
|
||||
.build();
|
||||
|
||||
RSItemResponse itemResponse = fetchJson(request, RSItemResponse.class);
|
||||
return itemResponse.getItem();
|
||||
|
||||
}
|
||||
|
||||
private RSPrices fetchRSPrices(int itemId) throws IOException
|
||||
{
|
||||
HttpUrl priceUrl = RS_PRICE_URL
|
||||
.newBuilder()
|
||||
.addPathSegment(itemId + ".json")
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(priceUrl)
|
||||
.build();
|
||||
|
||||
return fetchJson(request, RSPrices.class);
|
||||
}
|
||||
|
||||
public RSSearch fetchRSSearch(String query) throws IOException
|
||||
{
|
||||
// rs api seems to require lowercase
|
||||
query = query.toLowerCase();
|
||||
|
||||
HttpUrl searchUrl = RS_SEARCH_URL
|
||||
.newBuilder()
|
||||
.addQueryParameter("alpha", query)
|
||||
.build();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(searchUrl)
|
||||
.build();
|
||||
|
||||
return fetchJson(request, RSSearch.class);
|
||||
}
|
||||
|
||||
private void batchInsertItems(RSSearch search)
|
||||
{
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
Query q = con.createQuery("insert into items (id, name, description, type) values (:id,"
|
||||
+ " :name, :description, :type) ON DUPLICATE KEY UPDATE name = :name,"
|
||||
+ " description = :description, type = :type");
|
||||
|
||||
for (RSItem rsItem : search.getItems())
|
||||
{
|
||||
q.addParameter("id", rsItem.getId())
|
||||
.addParameter("name", rsItem.getName())
|
||||
.addParameter("description", rsItem.getDescription())
|
||||
.addParameter("type", rsItem.getType())
|
||||
.addToBatch();
|
||||
}
|
||||
|
||||
q.executeBatch();
|
||||
con.commit(false);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T fetchJson(Request request, Class<T> clazz) throws IOException
|
||||
{
|
||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
throw new IOException("Unsuccessful http response: " + response);
|
||||
}
|
||||
|
||||
InputStream in = response.body().byteStream();
|
||||
return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), clazz);
|
||||
}
|
||||
catch (JsonParseException ex)
|
||||
{
|
||||
throw new IOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] fetchImage(String url) throws IOException
|
||||
{
|
||||
HttpUrl httpUrl = HttpUrl.parse(url);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(httpUrl)
|
||||
.build();
|
||||
|
||||
try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
|
||||
{
|
||||
if (!response.isSuccessful())
|
||||
{
|
||||
throw new IOException("Unsuccessful http response: " + response);
|
||||
}
|
||||
|
||||
return response.body().bytes();
|
||||
}
|
||||
}
|
||||
|
||||
public void queueSearch(String search)
|
||||
{
|
||||
if (pendingLookups.size() < MAX_PENDING)
|
||||
{
|
||||
pendingLookups.add(new PendingLookup(search, PendingLookup.Type.SEARCH));
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("Dropping pending search for {}", search);
|
||||
}
|
||||
}
|
||||
|
||||
public void queueItem(int itemId)
|
||||
{
|
||||
if (pendingLookups.size() < MAX_PENDING)
|
||||
{
|
||||
pendingLookups.add(new PendingLookup(itemId, PendingLookup.Type.ITEM));
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("Dropping pending item lookup for {}", itemId);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 5000)
|
||||
public void check()
|
||||
{
|
||||
PendingLookup pendingLookup = pendingLookups.poll();
|
||||
if (pendingLookup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (pendingLookup.getType())
|
||||
{
|
||||
case SEARCH:
|
||||
try
|
||||
{
|
||||
RSSearch reSearch = fetchRSSearch(pendingLookup.getSearch());
|
||||
|
||||
batchInsertItems(reSearch);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.warn("error while searching items", ex);
|
||||
}
|
||||
break;
|
||||
case ITEM:
|
||||
fetchItem(pendingLookup.getItemId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 20_000)
|
||||
public void crawlPrices()
|
||||
{
|
||||
if (tradeableItems == null || tradeableItems.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = random.nextInt(tradeableItems.length);
|
||||
int id = tradeableItems[idx];
|
||||
|
||||
if (getItem(id) == null)
|
||||
{
|
||||
// This is a new item..
|
||||
log.debug("Fetching new item {}", id);
|
||||
queueItem(id);
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Fetching price for {}", id);
|
||||
|
||||
fetchPrice(id);
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 1_8000_000) // 30 minutes
|
||||
public void reloadItems() throws IOException
|
||||
{
|
||||
List<ItemDefinition> items = cacheService.getItems();
|
||||
if (items.isEmpty())
|
||||
{
|
||||
log.warn("Failed to load any items from cache, item price updating will be disabled");
|
||||
}
|
||||
|
||||
tradeableItems = items.stream()
|
||||
.filter(item -> item.isTradeable)
|
||||
.mapToInt(item -> item.id)
|
||||
.toArray();
|
||||
|
||||
log.debug("Loaded {} tradeable items", tradeableItems.length);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.item;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
class PendingLookup
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
SEARCH,
|
||||
ITEM;
|
||||
}
|
||||
|
||||
private final int itemId;
|
||||
private final String search;
|
||||
private final Type type;
|
||||
|
||||
public PendingLookup(int itemId, Type type)
|
||||
{
|
||||
this.itemId = itemId;
|
||||
this.search = null;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public PendingLookup(String search, Type type)
|
||||
{
|
||||
this.itemId = -1;
|
||||
this.search = search;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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.item;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
class PriceEntry
|
||||
{
|
||||
private int item;
|
||||
private String name;
|
||||
private int price;
|
||||
private Instant time;
|
||||
private Instant fetched_time;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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.item;
|
||||
|
||||
import lombok.Data;
|
||||
import net.runelite.http.api.item.Item;
|
||||
import net.runelite.http.api.item.ItemType;
|
||||
|
||||
@Data
|
||||
public class RSItem
|
||||
{
|
||||
private int id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String type;
|
||||
private String icon;
|
||||
private String icon_large;
|
||||
|
||||
public Item toItem()
|
||||
{
|
||||
Item item = new Item();
|
||||
item.setId(id);
|
||||
item.setName(name);
|
||||
item.setType(ItemType.of(type));
|
||||
item.setDescription(description);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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.item;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RSItemResponse
|
||||
{
|
||||
private RSItem item;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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.item;
|
||||
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RSPrices
|
||||
{
|
||||
/**
|
||||
* unix time in ms to price in gp
|
||||
*/
|
||||
private Map<Long, Integer> daily;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* 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.item;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class RSSearch
|
||||
{
|
||||
private List<RSItem> items;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.loottracker;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.Data;
|
||||
import net.runelite.http.api.loottracker.LootRecordType;
|
||||
|
||||
@Data
|
||||
class LootResult
|
||||
{
|
||||
private int killId;
|
||||
private Instant time;
|
||||
private LootRecordType type;
|
||||
private String eventId;
|
||||
private int itemId;
|
||||
private int itemQuantity;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
|
||||
* Copyright (c) 2018, 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.loottracker;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import net.runelite.http.api.loottracker.LootRecord;
|
||||
import net.runelite.http.service.account.AuthFilter;
|
||||
import net.runelite.http.service.account.beans.SessionEntry;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/loottracker")
|
||||
public class LootTrackerController
|
||||
{
|
||||
@Autowired
|
||||
private LootTrackerService service;
|
||||
|
||||
@Autowired
|
||||
private AuthFilter auth;
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public void storeLootRecord(HttpServletRequest request, HttpServletResponse response, @RequestBody Collection<LootRecord> records) throws IOException
|
||||
{
|
||||
SessionEntry e = auth.handle(request, response);
|
||||
if (e == null)
|
||||
{
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
service.store(records, e.getUser());
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Collection<LootRecord> getLootRecords(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count, @RequestParam(value = "start", defaultValue = "0") int start) throws IOException
|
||||
{
|
||||
SessionEntry e = auth.handle(request, response);
|
||||
if (e == null)
|
||||
{
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return null;
|
||||
}
|
||||
|
||||
return service.get(e.getUser(), count, start);
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
public void deleteLoot(HttpServletRequest request, HttpServletResponse response,
|
||||
@RequestParam(required = false) String eventId) throws IOException
|
||||
{
|
||||
SessionEntry e = auth.handle(request, response);
|
||||
if (e == null)
|
||||
{
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
return;
|
||||
}
|
||||
|
||||
service.delete(e.getUser(), eventId);
|
||||
}
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
|
||||
* Copyright (c) 2018, 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.loottracker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import net.runelite.http.api.loottracker.GameItem;
|
||||
import net.runelite.http.api.loottracker.LootRecord;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.sql2o.Connection;
|
||||
import org.sql2o.Query;
|
||||
import org.sql2o.Sql2o;
|
||||
|
||||
@Service
|
||||
public class LootTrackerService
|
||||
{
|
||||
// Table for storing individual LootRecords
|
||||
private static final String CREATE_KILLS = "CREATE TABLE IF NOT EXISTS `kills` (\n"
|
||||
+ " `id` INT AUTO_INCREMENT UNIQUE,\n"
|
||||
+ " `time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),\n"
|
||||
+ " `accountId` INT NOT NULL,\n"
|
||||
+ " `type` enum('NPC', 'PLAYER', 'EVENT', 'UNKNOWN') NOT NULL,\n"
|
||||
+ " `eventId` VARCHAR(255) NOT NULL,\n"
|
||||
+ " PRIMARY KEY (id),\n"
|
||||
+ " FOREIGN KEY (accountId) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,\n"
|
||||
+ " INDEX idx_acc (accountId, time),"
|
||||
+ " INDEX idx_time (time)"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
// Table for storing Items received as loot for individual LootRecords
|
||||
private static final String CREATE_DROPS = "CREATE TABLE IF NOT EXISTS `drops` (\n"
|
||||
+ " `killId` INT NOT NULL,\n"
|
||||
+ " `itemId` INT NOT NULL,\n"
|
||||
+ " `itemQuantity` INT NOT NULL,\n"
|
||||
+ " FOREIGN KEY (killId) REFERENCES kills(id) ON DELETE CASCADE\n"
|
||||
+ ") ENGINE=InnoDB";
|
||||
|
||||
// Queries for inserting kills
|
||||
private static final String INSERT_KILL_QUERY = "INSERT INTO kills (accountId, type, eventId) VALUES (:accountId, :type, :eventId)";
|
||||
private static final String INSERT_DROP_QUERY = "INSERT INTO drops (killId, itemId, itemQuantity) VALUES (:killId, :itemId, :itemQuantity)";
|
||||
|
||||
private static final String SELECT_LOOT_QUERY = "SELECT killId,time,type,eventId,itemId,itemQuantity FROM kills JOIN drops ON drops.killId = kills.id WHERE accountId = :accountId ORDER BY TIME DESC LIMIT :limit OFFSET :offset";
|
||||
|
||||
private static final String DELETE_LOOT_ACCOUNT = "DELETE FROM kills WHERE accountId = :accountId";
|
||||
private static final String DELETE_LOOT_ACCOUNT_EVENTID = "DELETE FROM kills WHERE accountId = :accountId AND eventId = :eventId";
|
||||
|
||||
private final Sql2o sql2o;
|
||||
|
||||
@Autowired
|
||||
public LootTrackerService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
|
||||
{
|
||||
this.sql2o = sql2o;
|
||||
|
||||
// Ensure necessary tables exist
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery(CREATE_KILLS).executeUpdate();
|
||||
con.createQuery(CREATE_DROPS).executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store LootRecord
|
||||
*
|
||||
* @param records LootRecords to store
|
||||
* @param accountId runelite account id to tie data too
|
||||
*/
|
||||
public void store(Collection<LootRecord> records, int accountId)
|
||||
{
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
// Kill Entry Query
|
||||
Query killQuery = con.createQuery(INSERT_KILL_QUERY, true);
|
||||
|
||||
for (LootRecord record : records)
|
||||
{
|
||||
killQuery
|
||||
.addParameter("accountId", accountId)
|
||||
.addParameter("type", record.getType())
|
||||
.addParameter("eventId", record.getEventId())
|
||||
.addToBatch();
|
||||
}
|
||||
|
||||
killQuery.executeBatch();
|
||||
Object[] keys = con.getKeys();
|
||||
|
||||
if (keys.length != records.size())
|
||||
{
|
||||
throw new RuntimeException("Mismatch in keys vs records size");
|
||||
}
|
||||
|
||||
Query insertDrop = con.createQuery(INSERT_DROP_QUERY);
|
||||
|
||||
// Append all queries for inserting drops
|
||||
int idx = 0;
|
||||
for (LootRecord record : records)
|
||||
{
|
||||
for (GameItem drop : record.getDrops())
|
||||
{
|
||||
insertDrop
|
||||
.addParameter("killId", keys[idx])
|
||||
.addParameter("itemId", drop.getId())
|
||||
.addParameter("itemQuantity", drop.getQty())
|
||||
.addToBatch();
|
||||
}
|
||||
|
||||
++idx;
|
||||
}
|
||||
|
||||
insertDrop.executeBatch();
|
||||
con.commit(false);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<LootRecord> get(int accountId, int limit, int offset)
|
||||
{
|
||||
List<LootResult> lootResults;
|
||||
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
lootResults = con.createQuery(SELECT_LOOT_QUERY)
|
||||
.addParameter("accountId", accountId)
|
||||
.addParameter("limit", limit)
|
||||
.addParameter("offset", offset)
|
||||
.executeAndFetch(LootResult.class);
|
||||
}
|
||||
|
||||
LootResult current = null;
|
||||
List<LootRecord> lootRecords = new ArrayList<>();
|
||||
List<GameItem> gameItems = new ArrayList<>();
|
||||
|
||||
for (LootResult lootResult : lootResults)
|
||||
{
|
||||
if (current == null || current.getKillId() != lootResult.getKillId())
|
||||
{
|
||||
if (!gameItems.isEmpty())
|
||||
{
|
||||
LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime());
|
||||
lootRecords.add(lootRecord);
|
||||
|
||||
gameItems = new ArrayList<>();
|
||||
}
|
||||
|
||||
current = lootResult;
|
||||
}
|
||||
|
||||
GameItem gameItem = new GameItem(lootResult.getItemId(), lootResult.getItemQuantity());
|
||||
gameItems.add(gameItem);
|
||||
}
|
||||
|
||||
if (!gameItems.isEmpty())
|
||||
{
|
||||
LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime());
|
||||
lootRecords.add(lootRecord);
|
||||
}
|
||||
|
||||
return lootRecords;
|
||||
}
|
||||
|
||||
public void delete(int accountId, String eventId)
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
if (eventId == null)
|
||||
{
|
||||
con.createQuery(DELETE_LOOT_ACCOUNT)
|
||||
.addParameter("accountId", accountId)
|
||||
.executeUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
con.createQuery(DELETE_LOOT_ACCOUNT_EVENTID)
|
||||
.addParameter("accountId", accountId)
|
||||
.addParameter("eventId", eventId)
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 15 * 60 * 1000)
|
||||
public void expire()
|
||||
{
|
||||
try (Connection con = sql2o.open())
|
||||
{
|
||||
con.createQuery("delete from kills where time < current_timestamp() - interval 30 day")
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, AeonLucid <https://github.com/AeonLucid>
|
||||
* 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.osbuddy;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
class GrandExchangeEntry
|
||||
{
|
||||
private int item_id;
|
||||
private int buy_average;
|
||||
private int sell_average;
|
||||
private int overall_average;
|
||||
private Instant last_update;
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018, AeonLucid <https://github.com/AeonLucid>
|
||||
* 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.osbuddy;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/osb/ge")
|
||||
public class OSBGrandExchangeController
|
||||
{
|
||||
private final OSBGrandExchangeService grandExchangeService;
|
||||
|
||||
@Autowired
|
||||
public OSBGrandExchangeController(OSBGrandExchangeService grandExchangeService)
|
||||
{
|
||||
this.grandExchangeService = grandExchangeService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ResponseEntity<GrandExchangeEntry> get(@RequestParam("itemId") int itemId) throws ExecutionException
|
||||
{
|
||||
GrandExchangeEntry grandExchangeEntry = grandExchangeService.get(itemId);
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.cacheControl(CacheControl.maxAge(30, TimeUnit.MINUTES).cachePublic())
|
||||
.body(grandExchangeEntry);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user