diff --git a/http-api/pom.xml b/http-api/pom.xml
index cb7ad265e0..120db17783 100644
--- a/http-api/pom.xml
+++ b/http-api/pom.xml
@@ -50,6 +50,12 @@
slf4j-api
1.7.12
+
+ org.projectlombok
+ lombok
+ 1.16.18
+ provided
+
junit
diff --git a/http-api/src/main/java/net/runelite/http/api/xp/XpClient.java b/http-api/src/main/java/net/runelite/http/api/xp/XpClient.java
new file mode 100644
index 0000000000..47a7ce31a4
--- /dev/null
+++ b/http-api/src/main/java/net/runelite/http/api/xp/XpClient.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018, Adam
+ * 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.api.xp;
+
+import java.io.IOException;
+import net.runelite.http.api.RuneLiteAPI;
+import okhttp3.HttpUrl;
+import okhttp3.Request;
+
+public class XpClient
+{
+ public void update(String username) throws IOException
+ {
+ HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
+ .addPathSegment("xp")
+ .addPathSegment("update")
+ .addQueryParameter("username", username)
+ .build();
+
+ Request request = new Request.Builder()
+ .url(url)
+ .build();
+
+ RuneLiteAPI.CLIENT.newCall(request).execute().close();
+ }
+}
diff --git a/http-api/src/main/java/net/runelite/http/api/xp/XpData.java b/http-api/src/main/java/net/runelite/http/api/xp/XpData.java
new file mode 100644
index 0000000000..33217bafe0
--- /dev/null
+++ b/http-api/src/main/java/net/runelite/http/api/xp/XpData.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018, Adam
+ * 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.api.xp;
+
+import java.time.Instant;
+import lombok.Data;
+
+@Data
+public class XpData
+{
+ private Instant time;
+
+ private int attack_xp;
+ private int defence_xp;
+ private int strength_xp;
+ private int hitpoints_xp;
+ private int ranged_xp;
+ private int prayer_xp;
+ private int magic_xp;
+ private int cooking_xp;
+ private int woodcutting_xp;
+ private int fletching_xp;
+ private int fishing_xp;
+ private int firemaking_xp;
+ private int crafting_xp;
+ private int smithing_xp;
+ private int mining_xp;
+ private int herblore_xp;
+ private int agility_xp;
+ private int thieving_xp;
+ private int slayer_xp;
+ private int farming_xp;
+ private int runecraft_xp;
+ private int hunter_xp;
+ private int construction_xp;
+
+ private int attack_rank;
+ private int defence_rank;
+ private int strength_rank;
+ private int hitpoints_rank;
+ private int ranged_rank;
+ private int prayer_rank;
+ private int magic_rank;
+ private int cooking_rank;
+ private int woodcutting_rank;
+ private int fletching_rank;
+ private int fishing_rank;
+ private int firemaking_rank;
+ private int crafting_rank;
+ private int smithing_rank;
+ private int mining_rank;
+ private int herblore_rank;
+ private int agility_rank;
+ private int thieving_rank;
+ private int slayer_rank;
+ private int farming_rank;
+ private int runecraft_rank;
+ private int hunter_rank;
+ private int construction_rank;
+}
diff --git a/http-service/pom.xml b/http-service/pom.xml
index b24e62999a..c7d52c1a51 100644
--- a/http-service/pom.xml
+++ b/http-service/pom.xml
@@ -38,6 +38,7 @@
1.5.6.RELEASE
1.16.18
+ 1.2.0.Final
@@ -64,6 +65,11 @@
true
+
+ org.mapstruct
+ mapstruct-jdk8
+ ${mapstruct.version}
+
org.projectlombok
lombok
@@ -141,6 +147,25 @@
runelite-${project.version}
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
org.springframework.boot
spring-boot-maven-plugin
diff --git a/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java b/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java
index 319ecaee9c..f04d4a4a54 100644
--- a/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java
+++ b/http-service/src/main/java/net/runelite/http/service/SpringBootWebApplication.java
@@ -31,7 +31,6 @@ import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
-
import net.runelite.http.service.util.InstantConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@@ -69,6 +68,15 @@ public class SpringBootWebApplication extends SpringBootServletInitializer
return new Sql2o(dataSource, new NoQuirks(converters));
}
+ @Bean("Runelite XP Tracker SQL2O")
+ Sql2o trackerSql2o() throws NamingException
+ {
+ DataSource dataSource = (DataSource) getContext().lookup("jdbc/runelite-tracker");
+ Map converters = new HashMap<>();
+ converters.put(Instant.class, new InstantConverter());
+ return new Sql2o(dataSource, new NoQuirks(converters));
+ }
+
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
diff --git a/http-service/src/main/java/net/runelite/http/service/hiscore/HiscoreController.java b/http-service/src/main/java/net/runelite/http/service/hiscore/HiscoreController.java
index faccbf06a3..9b581dcd87 100644
--- a/http-service/src/main/java/net/runelite/http/service/hiscore/HiscoreController.java
+++ b/http-service/src/main/java/net/runelite/http/service/hiscore/HiscoreController.java
@@ -31,6 +31,7 @@ import net.runelite.http.api.hiscore.HiscoreSkill;
import net.runelite.http.api.hiscore.SingleHiscoreSkillResult;
import net.runelite.http.api.hiscore.Skill;
import net.runelite.http.service.util.HiscoreEndpointEditor;
+import net.runelite.http.service.xp.XpTrackerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
@@ -46,12 +47,25 @@ public class HiscoreController
@Autowired
private HiscoreService hiscoreService;
+ @Autowired
+ private XpTrackerService xpTrackerService;
+
@RequestMapping("/{endpoint}")
public HiscoreResult lookup(@PathVariable HiscoreEndpoint endpoint, @RequestParam String username) throws IOException
{
HiscoreResultBuilder resultBuilder = hiscoreService.lookupUsername(username, endpoint);
HiscoreResult result = resultBuilder.build();
+ // Submit to xp tracker?
+ switch (endpoint)
+ {
+ case NORMAL:
+ case IRONMAN:
+ case ULTIMATE_IRONMAN:
+ case HARDCORE_IRONMAN:
+ xpTrackerService.update(username, result);
+ }
+
return result;
}
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/XpMapper.java b/http-service/src/main/java/net/runelite/http/service/xp/XpMapper.java
new file mode 100644
index 0000000000..0e23aa3959
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/xp/XpMapper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, Adam
+ * 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.xp;
+
+import net.runelite.http.api.xp.XpData;
+import net.runelite.http.service.xp.beans.XpEntity;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface XpMapper
+{
+ XpMapper INSTANCE = Mappers.getMapper(XpMapper.class);
+
+ XpData xpEntityToXpData(XpEntity xpEntity);
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerController.java b/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerController.java
new file mode 100644
index 0000000000..7606d4e4ee
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerController.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018, Adam
+ * 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.xp;
+
+import java.io.IOException;
+import java.time.Instant;
+import net.runelite.http.api.xp.XpData;
+import net.runelite.http.service.xp.beans.XpEntity;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/xp")
+public class XpTrackerController
+{
+ @Autowired
+ private XpTrackerService xpTrackerService;
+
+ @RequestMapping("/update")
+ public void update(@RequestParam String username) throws IOException
+ {
+ xpTrackerService.update(username);
+ }
+
+ @RequestMapping("/get")
+ public XpData get(@RequestParam String username, @RequestParam(required = false) Instant time) throws IOException
+ {
+ if (time == null)
+ {
+ time = Instant.now();
+ }
+ XpEntity xpEntity = xpTrackerService.findXpAtTime(username, time);
+ return XpMapper.INSTANCE.xpEntityToXpData(xpEntity);
+ }
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerService.java b/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerService.java
new file mode 100644
index 0000000000..7e79f1de80
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/xp/XpTrackerService.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2018, Adam
+ * 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.xp;
+
+import java.io.IOException;
+import java.time.Instant;
+import net.runelite.http.api.hiscore.HiscoreEndpoint;
+import net.runelite.http.api.hiscore.HiscoreResult;
+import net.runelite.http.service.hiscore.HiscoreResultBuilder;
+import net.runelite.http.service.hiscore.HiscoreService;
+import net.runelite.http.service.xp.beans.PlayerEntity;
+import net.runelite.http.service.xp.beans.XpEntity;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+import org.sql2o.Connection;
+import org.sql2o.Sql2o;
+
+@Service
+public class XpTrackerService
+{
+ @Autowired
+ @Qualifier("Runelite XP Tracker SQL2O")
+ private Sql2o sql2o;
+
+ @Autowired
+ private HiscoreService hiscoreService;
+
+ public void update(String username) throws IOException
+ {
+ HiscoreResultBuilder hiscoreResultBuilder = hiscoreService.lookupUsername(username, HiscoreEndpoint.NORMAL);
+ HiscoreResult hiscoreResult = hiscoreResultBuilder.build();
+ update(username, hiscoreResult);
+ }
+
+ public void update(String username, HiscoreResult hiscoreResult)
+ {
+ try (Connection con = sql2o.open())
+ {
+ PlayerEntity playerEntity = findOrCreatePlayer(username);
+
+ con.createQuery("insert into xp (player,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,attack_rank,defence_rank,strength_rank,hitpoints_rank,ranged_rank,prayer_rank,magic_rank,"
+ + "cooking_rank,woodcutting_rank,fletching_rank,fishing_rank,firemaking_rank,crafting_rank,smithing_rank,mining_rank,herblore_rank,"
+ + "agility_rank,thieving_rank,slayer_rank,farming_rank,runecraft_rank,hunter_rank,construction_rank,overall_rank) values (:player,: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,:attack_rank,:defence_rank,:strength_rank,:hitpoints_rank,:ranged_rank,:prayer_rank,:magic_rank,:cooking_rank,"
+ + ":woodcutting_rank,:fletching_rank,:fishing_rank,:firemaking_rank,:crafting_rank,:smithing_rank,:mining_rank,:herblore_rank,"
+ + ":agility_rank,:thieving_rank,:slayer_rank,:farming_rank,:runecraft_rank,:hunter_rank,:construction_rank,:overall_rank)")
+ .addParameter("player", playerEntity.getId())
+ .addParameter("attack_xp", hiscoreResult.getAttack().getExperience())
+ .addParameter("defence_xp", hiscoreResult.getDefence().getExperience())
+ .addParameter("strength_xp", hiscoreResult.getStrength().getExperience())
+ .addParameter("hitpoints_xp", hiscoreResult.getHitpoints().getExperience())
+ .addParameter("ranged_xp", hiscoreResult.getRanged().getExperience())
+ .addParameter("prayer_xp", hiscoreResult.getPrayer().getExperience())
+ .addParameter("magic_xp", hiscoreResult.getMagic().getExperience())
+ .addParameter("cooking_xp", hiscoreResult.getCooking().getExperience())
+ .addParameter("woodcutting_xp", hiscoreResult.getWoodcutting().getExperience())
+ .addParameter("fletching_xp", hiscoreResult.getFletching().getExperience())
+ .addParameter("fishing_xp", hiscoreResult.getFishing().getExperience())
+ .addParameter("firemaking_xp", hiscoreResult.getFiremaking().getExperience())
+ .addParameter("crafting_xp", hiscoreResult.getCrafting().getExperience())
+ .addParameter("smithing_xp", hiscoreResult.getSmithing().getExperience())
+ .addParameter("mining_xp", hiscoreResult.getMining().getExperience())
+ .addParameter("herblore_xp", hiscoreResult.getHerblore().getExperience())
+ .addParameter("agility_xp", hiscoreResult.getAgility().getExperience())
+ .addParameter("thieving_xp", hiscoreResult.getThieving().getExperience())
+ .addParameter("slayer_xp", hiscoreResult.getSlayer().getExperience())
+ .addParameter("farming_xp", hiscoreResult.getFarming().getExperience())
+ .addParameter("runecraft_xp", hiscoreResult.getRunecraft().getExperience())
+ .addParameter("hunter_xp", hiscoreResult.getHunter().getExperience())
+ .addParameter("construction_xp", hiscoreResult.getConstruction().getExperience())
+ .addParameter("attack_rank", hiscoreResult.getAttack().getRank())
+ .addParameter("defence_rank", hiscoreResult.getDefence().getRank())
+ .addParameter("strength_rank", hiscoreResult.getStrength().getRank())
+ .addParameter("hitpoints_rank", hiscoreResult.getHitpoints().getRank())
+ .addParameter("ranged_rank", hiscoreResult.getRanged().getRank())
+ .addParameter("prayer_rank", hiscoreResult.getPrayer().getRank())
+ .addParameter("magic_rank", hiscoreResult.getMagic().getRank())
+ .addParameter("cooking_rank", hiscoreResult.getCooking().getRank())
+ .addParameter("woodcutting_rank", hiscoreResult.getWoodcutting().getRank())
+ .addParameter("fletching_rank", hiscoreResult.getFletching().getRank())
+ .addParameter("fishing_rank", hiscoreResult.getFishing().getRank())
+ .addParameter("firemaking_rank", hiscoreResult.getFiremaking().getRank())
+ .addParameter("crafting_rank", hiscoreResult.getCrafting().getRank())
+ .addParameter("smithing_rank", hiscoreResult.getSmithing().getRank())
+ .addParameter("mining_rank", hiscoreResult.getMining().getRank())
+ .addParameter("herblore_rank", hiscoreResult.getHerblore().getRank())
+ .addParameter("agility_rank", hiscoreResult.getAgility().getRank())
+ .addParameter("thieving_rank", hiscoreResult.getThieving().getRank())
+ .addParameter("slayer_rank", hiscoreResult.getSlayer().getRank())
+ .addParameter("farming_rank", hiscoreResult.getFarming().getRank())
+ .addParameter("runecraft_rank", hiscoreResult.getRunecraft().getRank())
+ .addParameter("hunter_rank", hiscoreResult.getHunter().getRank())
+ .addParameter("construction_rank", hiscoreResult.getConstruction().getRank())
+ .addParameter("overall_rank", hiscoreResult.getOverall().getRank())
+ .executeUpdate();
+ }
+ }
+
+ private synchronized PlayerEntity findOrCreatePlayer(String username)
+ {
+ try (Connection con = sql2o.open())
+ {
+ PlayerEntity playerEntity = con.createQuery("select * from player where name = :name")
+ .addParameter("name", username)
+ .executeAndFetchFirst(PlayerEntity.class);
+ if (playerEntity != null)
+ {
+ return playerEntity;
+ }
+
+ Instant now = Instant.now();
+
+ int id = con.createQuery("insert into player (name, tracked_since) values (:name, :tracked_since)")
+ .addParameter("name", username)
+ .addParameter("tracked_since", now)
+ .executeUpdate()
+ .getKey(int.class);
+
+ playerEntity = new PlayerEntity();
+ playerEntity.setId(id);
+ playerEntity.setName(username);
+ playerEntity.setTracked_since(now);
+ return playerEntity;
+ }
+ }
+
+ public XpEntity findXpAtTime(String username, Instant time)
+ {
+ try (Connection con = sql2o.open())
+ {
+ return con.createQuery("select * from xp join player on player.id=xp.player where player.name = :username and time <= :time order by time desc limit 1")
+ .throwOnMappingFailure(false)
+ .addParameter("username", username)
+ .addParameter("time", time)
+ .executeAndFetchFirst(XpEntity.class);
+ }
+ }
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/beans/PlayerEntity.java b/http-service/src/main/java/net/runelite/http/service/xp/beans/PlayerEntity.java
new file mode 100644
index 0000000000..d07d1d4640
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/xp/beans/PlayerEntity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2018, Adam
+ * 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.xp.beans;
+
+import java.time.Instant;
+import lombok.Data;
+
+@Data
+public class PlayerEntity
+{
+ private Integer id;
+ private String name;
+ private Instant tracked_since;
+}
diff --git a/http-service/src/main/java/net/runelite/http/service/xp/beans/XpEntity.java b/http-service/src/main/java/net/runelite/http/service/xp/beans/XpEntity.java
new file mode 100644
index 0000000000..acab775873
--- /dev/null
+++ b/http-service/src/main/java/net/runelite/http/service/xp/beans/XpEntity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018, Adam
+ * 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.xp.beans;
+
+import java.time.Instant;
+import lombok.Data;
+
+@Data
+public class XpEntity
+{
+ private Integer id;
+ private Instant time;
+ private Integer player;
+ private int overall_xp;
+ private int attack_xp;
+ private int defence_xp;
+ private int strength_xp;
+ private int hitpoints_xp;
+ private int ranged_xp;
+ private int prayer_xp;
+ private int magic_xp;
+ private int cooking_xp;
+ private int woodcutting_xp;
+ private int fletching_xp;
+ private int fishing_xp;
+ private int firemaking_xp;
+ private int crafting_xp;
+ private int smithing_xp;
+ private int mining_xp;
+ private int herblore_xp;
+ private int agility_xp;
+ private int thieving_xp;
+ private int slayer_xp;
+ private int farming_xp;
+ private int runecraft_xp;
+ private int hunter_xp;
+ private int construction_xp;
+
+ private int overall_rank;
+ private int attack_rank;
+ private int defence_rank;
+ private int strength_rank;
+ private int hitpoints_rank;
+ private int ranged_rank;
+ private int prayer_rank;
+ private int magic_rank;
+ private int cooking_rank;
+ private int woodcutting_rank;
+ private int fletching_rank;
+ private int fishing_rank;
+ private int firemaking_rank;
+ private int crafting_rank;
+ private int smithing_rank;
+ private int mining_rank;
+ private int herblore_rank;
+ private int agility_rank;
+ private int thieving_rank;
+ private int slayer_rank;
+ private int farming_rank;
+ private int runecraft_rank;
+ private int hunter_rank;
+ private int construction_rank;
+}
diff --git a/http-service/src/main/resources/net/runelite/http/service/xp/schema.sql b/http-service/src/main/resources/net/runelite/http/service/xp/schema.sql
new file mode 100644
index 0000000000..80465fd48a
--- /dev/null
+++ b/http-service/src/main/resources/net/runelite/http/service/xp/schema.sql
@@ -0,0 +1,132 @@
+-- MySQL dump 10.16 Distrib 10.2.9-MariaDB, for Linux (x86_64)
+--
+-- Host: localhost Database: xptracker
+-- ------------------------------------------------------
+-- Server version 10.2.9-MariaDB
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
+/*!40103 SET TIME_ZONE='+00:00' */;
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
+/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `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(),
+ 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`),
+ 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 2018-01-20 18:37:09
diff --git a/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java b/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java
index 45e5ed35f3..0edc296898 100644
--- a/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java
+++ b/http-service/src/test/java/net/runelite/http/service/SpringBootWebApplicationTest.java
@@ -28,7 +28,6 @@ import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import javax.naming.NamingException;
-
import net.runelite.http.service.util.InstantConverter;
import org.junit.Ignore;
import org.junit.Test;
@@ -47,7 +46,7 @@ public class SpringBootWebApplicationTest
{
Map converters = new HashMap<>();
converters.put(Instant.class, new InstantConverter());
- return new Sql2o("jdbc:mysql://localhost/runelite", "root", "", new NoQuirks(converters));
+ return new Sql2o("jdbc:mysql://192.168.1.2/runelite", "runelite", "runelite", new NoQuirks(converters));
}
@Bean("Runelite Cache SQL2O")
@@ -55,7 +54,15 @@ public class SpringBootWebApplicationTest
{
Map converters = new HashMap<>();
converters.put(Instant.class, new InstantConverter());
- return new Sql2o("jdbc:mysql://localhost/cache", "root", "", new NoQuirks(converters));
+ return new Sql2o("jdbc:mysql://192.168.1.2/cache", "runelite", "runelite", new NoQuirks(converters));
+ }
+
+ @Bean("Runelite XP Tracker SQL2O")
+ Sql2o xpSql2o() throws NamingException
+ {
+ Map converters = new HashMap<>();
+ converters.put(Instant.class, new InstantConverter());
+ return new Sql2o("jdbc:mysql://192.168.1.2/xptracker", "runelite", "runelite", new NoQuirks(converters));
}
@Test