http-services(-pro): Add back http-service and move custom http-service
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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("RuneLitePlus 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("RuneLitePlus API stopped");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.runeliteplus")
|
||||
@Bean("dataSourceRuneLite")
|
||||
public DataSourceProperties dataSourceProperties()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.runeliteplus-cache")
|
||||
@Bean("dataSourceRuneLiteCache")
|
||||
public DataSourceProperties dataSourcePropertiesCache()
|
||||
{
|
||||
return new DataSourceProperties();
|
||||
}
|
||||
|
||||
@ConfigurationProperties(prefix = "datasource.runeliteplus-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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
31
http-service-plus/src/main/resources/application-dev.yaml
Normal file
31
http-service-plus/src/main/resources/application-dev.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
# 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
|
||||
39
http-service-plus/src/main/resources/application.yaml
Normal file
39
http-service-plus/src/main/resources/application.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
datasource:
|
||||
runeliteplus:
|
||||
jndiName: java:comp/env/jdbc/runelite
|
||||
runeliteplus-cache:
|
||||
jndiName: java:comp/env/jdbc/runelite-cache2
|
||||
runeliteplus-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
|
||||
@@ -0,0 +1,135 @@
|
||||
-- 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
|
||||
110
http-service-plus/src/main/templates/markdown.hbs
Normal file
110
http-service-plus/src/main/templates/markdown.hbs
Normal file
@@ -0,0 +1,110 @@
|
||||
{{#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}}
|
||||
71
http-service-plus/src/main/templates/operation.hbs
Normal file
71
http-service-plus/src/main/templates/operation.hbs
Normal file
@@ -0,0 +1,71 @@
|
||||
{{#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}}
|
||||
88
http-service-plus/src/main/templates/security.hbs
Normal file
88
http-service-plus/src/main/templates/security.hbs
Normal file
@@ -0,0 +1,88 @@
|
||||
{{#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}}
|
||||
49
http-service-plus/src/main/templates/template.html.hbs
Normal file
49
http-service-plus/src/main/templates/template.html.hbs
Normal file
@@ -0,0 +1,49 @@
|
||||
<!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>
|
||||
32
http-service-plus/src/main/webapp/WEB-INF/web.xml
Normal file
32
http-service-plus/src/main/webapp/WEB-INF/web.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<!--
|
||||
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>
|
||||
17
http-service-plus/src/test/resources/application-test.yaml
Normal file
17
http-service-plus/src/test/resources/application-test.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
# 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.
Reference in New Issue
Block a user