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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user