Repository reorganisation

Unified kotlin and java into just src/main
Unified the rs09 and core packages
Took all content out of the core package, and placed it into the new content package
Reorganised all source code relating to content to be easier to find and explore
This commit is contained in:
Ceikry
2023-01-26 13:59:28 +00:00
committed by Ryan
parent e4b799c44a
commit a100affda2
3156 changed files with 24205 additions and 30585 deletions
+45
View File
@@ -0,0 +1,45 @@
package core.game.ge
import core.cache.def.impl.ItemDefinition
import org.rs09.consts.Items
class BotPrices {
companion object {
@JvmStatic
fun getPrice(id: Int): Int {
return getPriceOverrides(id) ?: ItemDefinition.forId(id).value
}
@JvmStatic
private fun getPriceOverrides(id: Int): Int? {
return when (id) {
Items.PURE_ESSENCE_7936 -> 50
Items.BOW_STRING_1777 -> 250
Items.MAGIC_LOGS_1513 -> 750
Items.COWHIDE_1739 -> 250
Items.DRAGON_BONES_536 -> 1250
Items.GREEN_DRAGONHIDE_1753 -> 550
Items.GRIMY_RANARR_207 -> 1214
Items.GRIMY_AVANTOE_211 -> 453
Items.GRIMY_CADANTINE_215 -> 232
Items.GRIMY_DWARF_WEED_217 -> 86
Items.GRIMY_GUAM_199 -> 50
Items.GRIMY_HARRALANDER_205 -> 115
Items.GRIMY_IRIT_209 -> 860
Items.GRIMY_KWUARM_213 -> 334
Items.GRIMY_LANTADYME_2485 -> 115
Items.GRIMY_MARRENTILL_201 -> 250
Items.LOBSTER_379 -> 268
Items.RAW_LOBSTER_377 -> 265
Items.LOOP_HALF_OF_A_KEY_987 -> 5250
Items.TOOTH_HALF_OF_A_KEY_985 -> 4263
Items.SWORDFISH_373 -> 400
Items.RAW_SWORDFISH_371 -> 390
Items.SHARK_385 -> 720
Items.RAW_SHARK_383 -> 710
else -> null
}
}
}
}
@@ -0,0 +1,35 @@
package core.game.ge
import core.api.StartupListener
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser
import core.ServerConstants
import java.io.File
import java.io.FileReader
class GEAutoStock : StartupListener {
override fun startup() {
autostock()
}
companion object {
// autostock format should be identical to the botoffers json format.
private val DB_PATH = "data" + File.separator + "eco" + File.separator + "autostock.json"
fun autostock() {
if(ServerConstants.GE_AUTOSTOCK_ENABLED) {
val parser = JSONParser()
val botReader: FileReader? = FileReader(DB_PATH)
val botSave = parser.parse(botReader) as JSONObject
if (botSave.containsKey("offers")) {
val offers = botSave["offers"] as JSONArray
for (offer in offers) {
val o = offer as JSONObject
GrandExchange.addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt())
}
}
}
}
}
}
+191
View File
@@ -0,0 +1,191 @@
package core.game.ge
import core.cache.def.impl.ItemDefinition
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser
import org.sqlite.SQLiteDataSource
import core.ServerConstants
import java.io.File
import java.io.FileReader
import java.sql.Connection
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
/**
* Collection of methods for interacting with the grand exchange databases
* @author Ceikry
*/
object GEDB {
private var pathString = ""
public var connection: Connection? = null
private var initialized = false
private var connectionRefs = 0
private var obtainConnectionLock = ReentrantLock()
private var dbRunLock = ReentrantLock()
fun init() {
init(File(ServerConstants.GRAND_EXCHANGE_DATA_PATH + "grandexchange.db").absolutePath)
}
//This needs to be a separate method, so we can call it after the server config has been parsed
fun init(path: String)
{
if(initialized) return
pathString = path
//Check if the grandexchange.db file already exists. If not, create it and create the tables.
if(!File(path).exists())
generateAndTransfer()
}
@JvmStatic fun run(closure: (conn: Connection) -> Unit) {
dbRunLock.tryLock(10000L, TimeUnit.MILLISECONDS)
connectionRefs++
val con = connect()
closure.invoke(con)
connectionRefs--
if(connectionRefs == 0) {
con.close()
}
dbRunLock.unlock()
}
private fun connect(): Connection
{
obtainConnectionLock.tryLock(10000L, TimeUnit.MILLISECONDS)
if (connection == null || connection!!.isClosed)
{
val ds = SQLiteDataSource()
ds.url = "jdbc:sqlite:$pathString"
connection = ds.connection
}
obtainConnectionLock.unlock()
return connection!!
}
private fun convertJsonArray(arr: JSONArray): String
{
val sb = StringBuilder()
for ((index, data) in arr.withIndex())
{
val item = data as JSONObject
sb.append("$index")
sb.append(",")
sb.append(item["id"])
sb.append(",")
sb.append(item["amount"])
if(index + 1 < arr.size)
sb.append(":")
}
return sb.toString()
}
private fun generateAndTransfer()
{
val conn = connect() //for sqlite jdbc, attempting to connect to a non-existing .db file creates it.
val statement = conn.createStatement()
//table for tracking player offers - replaces offer_dispatch.json
statement.execute(
"CREATE TABLE player_offers(" +
"uid INTEGER PRIMARY KEY ASC," +
"player_uid INTEGER," +
"item_id INTEGER," +
"amount_total INTEGER," +
"amount_complete INTEGER," +
"offered_value INTEGER," +
"time_stamp INTEGER," +
"offer_state INTEGER," +
"is_sale INTEGER," +
"withdraw_items STRING ," +
"slot_index INTEGER," +
"total_coin_xc INTEGER)"
)
//table for tracking bot offers - replaces bot_offers.json
statement.execute(
"CREATE TABLE bot_offers(" +
"item_id INTEGER," +
"amount INTEGER)"
)
//table for tracking price index data - replaces gedb.xml
statement.execute(
"CREATE TABLE price_index(" +
"item_id INTEGER," +
"value INTEGER," +
"total_value INTEGER," +
"unique_trades INTEGER," +
"last_update INTEGER)"
)
//check if the old .json and .xml files exist, and if they do, read them into the sqlite database and remove them.
val playerJson = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "offer_dispatch.json"
val botJson = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "bot_offers.json"
if(File(playerJson).exists())
{
val parser = JSONParser()
val reader = FileReader(playerJson)
val saveFile = parser.parse(reader) as JSONObject
if (saveFile.containsKey("offers")) {
val offers = saveFile["offers"] as JSONArray
for (offer in offers) {
val o = offer as JSONObject
statement.execute("insert into player_offers(player_uid,item_id,amount_total,amount_complete,offered_value,time_stamp,offer_state,is_sale,withdraw_items,total_coin_xc,slot_index) " +
"values(" +
"${o["playerUID"]}," +
"${o["itemId"]}," +
"${o["amount"]}," +
"${o["completedAmount"]}," +
"${o["offeredValue"]}," +
"${o["timeStamp"]}," +
"${o["offerState"]}," +
"${if (o["sale"] as Boolean) 1 else 0}," +
"'" + convertJsonArray(o["withdrawItems"] as JSONArray) + "'," +
"${o["totalCoinExchange"]}," +
"-1)"
)
}
}
reader.close()
//File(playerJson).delete()
}
if(File(botJson).exists())
{
val parser = JSONParser()
val reader = FileReader(botJson)
val saveFile = parser.parse(reader) as JSONObject
if (saveFile.containsKey("offers")) {
val offers = saveFile["offers"] as JSONArray
for (offer in offers) {
val o = offer as JSONObject
statement.execute("insert into bot_offers " +
"values(${o["item"]},${o["qty"]})")
}
}
reader.close()
}
statement.close()
//price index isn't worth transferring, so we're just going to make a new one.
ItemDefinition.getDefinitions().values.forEach { def ->
if(def.isTradeable){
PriceIndex.allowItem(def.id)
}
}
}
}
@@ -0,0 +1,147 @@
package core.game.ge;
import core.game.component.Component;
import core.game.node.entity.player.Player;
import core.tools.StringUtils;
/**
* Represents the glass used to open the guide prices for the different type of
* trade(herbs, logs, runes, etc...)
* @author 'Vexia
* @date 30/11/2013
*/
public final class GEGuidePrice {
/**
* Represents the guide price component.
*/
private static final Component COMPONENT = new Component(642);
/**
* Method used to open a Grand Exchange guide price type.
* @param player the player.
* @param type the type.
*/
public static final void open(final Player player, final GuideType type) {
player.getInterfaceManager().open(COMPONENT);
type.display(player);
}
/**
* Method used to clear the current items being showed.
* @param player the player.
*/
public static final void clear(final Player player) {
for (int i = 135; i < 165; i++) {
player.getPacketDispatch().sendInterfaceConfig(COMPONENT.getId(), i, true);
}
}
/**
* Represents a guide item shown on the Guide Price interface.
* @author 'Vexia
* @date 01/12/2013
*/
public static class GuideItem {
/**
* Represents the item of this type.
*/
private final int item;
/**
* Represents the unlock child.
*/
private final int[] childData;
/**
* Constructs a new {@code GEGuidePrice} {@code Object}.
* @param item the item.
* @param childData the child data.
*/
public GuideItem(final int item, final int... childData) {
this.item = item;
this.childData = childData;
}
/**
* Gets the item.
* @return The item.
*/
public int getItem() {
return item;
}
/**
* Gets the childData.
* @return The childData.
*/
public int[] getChildData() {
return childData;
}
}
/**
* Represents a guide price type of trade(herbs, logs, etc...)
* @author 'Vexia
* @date 30/11/2013
*/
public enum GuideType {
LOGS(new int[] { 0, 0 }, new GuideItem(1511, 155), new GuideItem(2862, 156), new GuideItem(1521, 157), new GuideItem(1519, 158), new GuideItem(6333, 159), new GuideItem(1517, 160), new GuideItem(6332, 161), new GuideItem(12581, 162), new GuideItem(1515, 163), new GuideItem(1513, 164)), ORES(new int[] { 40, 44 }, new GuideItem(436, 33), new GuideItem(438, 34), new GuideItem(440, 35), new GuideItem(442, 36), new GuideItem(453, 37), new GuideItem(444, 38), new GuideItem(447, 39), new GuideItem(449, 40), new GuideItem(451, 41)), RUNES(new int[] { 215, 216 }, new GuideItem(1436, 183), new GuideItem(7936, 184), new GuideItem(556, 185), new GuideItem(558, 186), new GuideItem(555, 187), new GuideItem(557, 188), new GuideItem(554, 189), new GuideItem(559, 190), new GuideItem(564, 191), new GuideItem(562, 192), new GuideItem(9075, 193), new GuideItem(561, 194), new GuideItem(563, 195), new GuideItem(560, 196), new GuideItem(565, 197), new GuideItem(566, 198)), HERBS(new int[] { 130, 135 }, new GuideItem(249, 119), new GuideItem(251, 120), new GuideItem(253, 121), new GuideItem(255, 122), new GuideItem(257, 123), new GuideItem(2998, 124), new GuideItem(259, 125), new GuideItem(12172, 126), new GuideItem(261, 127), new GuideItem(263, 128), new GuideItem(3000, 129), new GuideItem(265, 130), new GuideItem(2481, 131), new GuideItem(267, 132), new GuideItem(269, 133)), WEAPONS_AND_ARMOUR(new int[] { 88, 89 }, new GuideItem(11834, 73), new GuideItem(11838, 74), new GuideItem(11842, 75), new GuideItem(11864, 76), new GuideItem(11870, 77), new GuideItem(11846, 78), new GuideItem(11848, 79), new GuideItem(11850, 80), new GuideItem(11856, 81), new GuideItem(11732, 82), new GuideItem(4151, 83), new GuideItem(11235, 84), new GuideItem(6739, 85), new GuideItem(4587, 86), new GuideItem(4153, 87));
/**
* Constructs a new {@code GEGuidePrice} {@code Object}.
* @param childData the childData.
* @param items the guide items.
*/
GuideType(final int[] childData, final GuideItem... items) {
this.childData = childData;
this.items = items;
}
/**
* Represents the guide items.
*/
private final GuideItem[] items;
/**
* Represents the child data for the guide type.
*/
private final int childData[];
/**
* Gets the items.
* @return The items.
*/
public GuideItem[] getItems() {
return items;
}
/**
* Gets the childData.
* @return The childData.
*/
public int[] getChildData() {
return childData;
}
/**
* Method used to display the guide type.
* @param player the player.
*/
public void display(final Player player) {
player.setAttribute("guide-price", this);
if (this != LOGS) {
clear(player);
}
player.getPacketDispatch().sendString("Guide Prices: " + StringUtils.formatDisplayName(name()), COMPONENT.getId(), 14);
for (int i = getChildData()[0]; i < getChildData()[1]; i++) {
player.getPacketDispatch().sendInterfaceConfig(642, i, false);
}
for (GuideItem item : getItems()) {
player.getPacketDispatch().sendString("" + GrandExchange.getRecommendedPrice(item.item, false) + " gp", COMPONENT.getId(), item.getChildData()[0]);
}
}
}
}
@@ -0,0 +1,94 @@
package core.game.ge;
import core.game.node.item.Item;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Holds the Grand Exchange item sets.
* @author Emperor
*/
public enum GEItemSet {
BRONZE_L(11814, 1155, 1117, 1075, 1189), BRONZE_SK(11816, 1155, 1117, 1087, 1189), IRON_L(11818, 1153, 1115, 1067, 1191), IRON_SK(11820, 1153, 1115, 1081, 1191), STEEL_L(11822, 1157, 1119, 1069, 1193), STEEL_SK(11824, 1157, 1119, 1083, 1193), BLACK_L(11826, 1165, 1125, 1077, 1195), BLACK_SK(11828, 1165, 1125, 1089, 1195), MITHRIL_L(11830, 1159, 1121, 1071, 1197), MITHRIL_SK(11832, 1159, 1121, 1085, 1197), ADAMANT_L(11834, 1161, 1123, 1073, 1199), ADAMANT_SK(11836, 1161, 1123, 1091, 1199), RUNE_L(11838, 1163, 1127, 1079, 1201), RUNE_SK(11840, 1163, 1127, 1093, 1201), DRAGON_L(11842, 1149, 3140, 4087), DRAGON_SK(11844, 1149, 3140, 4585),
// NULL_1(-1), //This would create the spaces between the sets (uncomment
// when needed)
AHRIMS(11846, 4708, 4712, 4714, 4710), DHAROKS(11848, 4716, 4720, 4722, 4718), GUTHANS(11850, 4724, 4728, 4730, 4726), KARILS(11852, 4732, 4736, 4738, 4734), TORAGS(11854, 4745, 4749, 4751, 4747), VERACS(11856, 4753, 4757, 4759, 4755), THIRD_AGE_MELEE(11858, 10350, 10348, 10346, 10352), THIRD_AGE_RANGE(11860, 10334, 10330, 10332, 10336), THIRD_AGE_MAGE(11862, 10342, 10338, 10340, 10344), GREEN_DHIDE(11864, 1135, 1099, 1065), BLUE_DHIDE(11866, 2499, 2493, 2487), RED_DHIDE(11868, 2501, 2495, 2489), BLACK_DHIDE(11870, 2503, 2497, 2491), MYSTIC(11872, 4089, 4091, 4093, 4095, 4097), LIGHT_MYSTIC(11960, 4109, 4111, 4113, 4115, 4117), DARK_MYSTIC(11962, 4099, 4101, 4103, 4105, 4107), INFINITY(11874, 6918, 6916, 6924, 6922, 6920), SPLITBARK(11876, 3385, 3387, 3389, 3391, 3393), BLACK_TRIMMED_L(11878, 2587, 2583, 2585, 2589), BLACK_TRIMMED_SK(11880, 2587, 2583, 3472, 2589), BLACK_GOLD_TRIMMED_L(11882, 2595, 2591, 2593, 2597), BLACK_GOLD_TRIMMED_SK(11884, 2595, 2591, 3473, 2597), ADAMANT_TRIMMED_L(11886, 2605, 2599, 2601, 2603), ADAMANT_TRIMMED_SK(11888, 2605, 2599, 3474), ADAMANT_GOLD_TRIMMED_L(11890, 2613, 2607, 2609, 2611), ADAMANT_GOLD_TRIMMED_SK(11892, 2613, 3475, 2611), RUNE_TRIMMED_L(11894, 2627, 2623, 2625, 2629), RUNE_TRIMMED_SK(11896, 2627, 2623, 3477, 2629), RUNE_GOLD_TRIMMED_L(11898, 2619, 2615, 2617, 2621), RUNE_GOLD_TRIMMED_SK(11900, 2619, 2615, 3476, 2621), ENCHANTED(11902, 7400, 7399, 7398), TRIMMED_BLUE_WIZARD(11904, 7396, 7392, 7388), GOLD_TRIMMED_BLUE_WIZARD(11906, 7394, 7390, 7386), TRIMMED_LEATHER(11908, 7364, 7368), GOLD_TRIMMED_LEATHER(11910, 7362, 7366), GREEN_DHIDE_T(11912, 7372, 7380), GREEN_DHIDE_G(11914, 7370, 7378), BLUE_DHIDE_T(11916, 7376, 7384), BLUE_DHIDE_G(11918, 7374, 7382), GREEN_DHIDE_BLESSED(11920, 10382, 10378, 10380, 10376), BLUE_DHIDE_BLESSED(11922, 10390, 10386, 10388, 10384), RED_DHIDE_BLESSED(11924, 10374, 10370, 10372, 10368), GUTHIX_L(11926, 2673, 2669, 2671, 2675), SARADOMIN_L(11928, 2665, 2661, 2663, 2667), ZAMORAK_L(11930, 2657, 2653, 2655, 2659), GUTHIX_SK(11932, 2673, 2669, 3480, 2675), SARADOMIN_SK(11934, 2665, 2661, 3479, 2667), ZAMORAK_SK(11936, 2657, 2653, 3478, 2659), GILDED_L(11938, 3486, 3481, 3483, 3488), GILDED_SK(11940, 3486, 3481, 3485, 3488), ROCKSHELL(11942, 6128, 6129, 6130, 6151, 6145), SPINED(11944, 6131, 6133, 6135, 6149, 6143), SKELETAL(11946, 6137, 6139, 6141, 6153, 6147), CANNON(11967, 6, 8, 10, 12);
/**
* The item sets mapping.
*/
private static final Map<Integer, GEItemSet> ITEM_SETS = new HashMap<>();
/**
* The items array.
*/
private static Item[] itemArray;
/**
* Populate the mapping.
*/
static {
itemArray = new Item[values().length];
for (int i = 0; i < itemArray.length; i++) {
GEItemSet set = values()[i];
itemArray[i] = set.itemId == -1 ? new Item() : new Item(set.itemId);
ITEM_SETS.put(set.itemId, set);
}
}
/**
* Gets the item set for the given id.
* @param setId The set item id.
* @return The item set object.
*/
public static GEItemSet forId(int setId) {
return ITEM_SETS.get(setId);
}
/**
* The item id.
*/
private int itemId;
/**
* The components.
*/
private int[] components;
/**
* Constructs a new {@code GEItemSet} {@code Object}.
* @param itemId The item id.
* @param components The components.
*/
private GEItemSet(int itemId, int... components) {
this.itemId = itemId;
this.components = components;
}
/**
* Gets the itemId.
* @return The itemId.
*/
public int getItemId() {
return itemId;
}
/**
* Gets the components.
* @return The components.
*/
public int[] getComponents() {
return Arrays.copyOf(components, components.length);
}
/**
* Gets the item array.
* @return The item array.
*/
public static Item[] getItemArray() {
return Arrays.copyOf(itemArray, itemArray.length);
}
}
@@ -0,0 +1,364 @@
package core.game.ge
import core.api.*
import core.game.node.entity.player.Player
import core.game.node.entity.player.info.PlayerDetails
import core.game.node.entity.player.link.audio.Audio
import core.game.system.task.Pulse
import core.ServerConstants
import core.tools.SystemLogger
import core.game.system.command.Privilege
import core.game.system.config.ItemConfigParser
import core.game.world.GameWorld
import core.game.world.repository.Repository
import core.tools.colorize
import java.lang.Integer.max
import java.util.concurrent.LinkedBlockingDeque
/**
* Handles the exchanging of offers, offer update thread, etc.
* @author Ceikry
*/
class GrandExchange : StartupListener, Commands {
/**
* Fallback safety check to make sure we don't start the GE twice under any circumstance
*/
var isRunning = false
/**
* Initializes the offer manager and spawns an update thread.
* @param local whether or not the GE should be the local in-code server rather than some hypothetical remote implementation.
*/
fun boot(){
if(isRunning) return
SystemLogger.logGE("Initializing GE Update Worker")
Thread {
Thread.currentThread().name = "GE Update Worker"
while(true) {
var offer = pendingOffers.takeFirst()
offer.writeNew()
offer = getOfferByUid(offer.uid) ?: continue
selectPotentialMatches(offer).asSequence()
.sortedBy { if (offer.sell) -it.offeredValue else it.offeredValue }
.filter { if (offer.sell) it.offeredValue >= offer.offeredValue else it.offeredValue <= offer.offeredValue }
.forEach { match -> exchange(offer, match) }
if (!offer.isBot && offer.amountLeft > 0 && !offer.sell)
tryExchangeWithBots(offer)
}
}.start()
isRunning = true
}
private fun tryExchangeWithBots(offer: GrandExchangeOffer) {
GEDB.run { conn ->
val query = conn.prepareStatement(GET_MATCH_FROM_BOT_OFFERS)
query.setInt(1, offer.itemID)
val res = query.executeQuery()
if (res.next()) {
exchange(offer, GrandExchangeOffer.fromBotQuery(res).also { it.timeStamp = offer.timeStamp - 1L })
}
}
}
private fun selectPotentialMatches(offer: GrandExchangeOffer): List<GrandExchangeOffer> {
val matches = ArrayList<GrandExchangeOffer>()
GEDB.run { conn ->
val query = conn.prepareStatement(GET_MATCHES_FROM_PLAYER_OFFERS)
query.setInt(1, offer.itemID)
query.setBoolean(2, !offer.sell)
val res = query.executeQuery()
while (res.next()) {
matches.add(GrandExchangeOffer.fromQuery(res))
}
}
return matches
}
override fun defineCommands() {
define("addbotoffer", Privilege.ADMIN) {player, strings ->
val id = strings[1].toInt()
val amount = strings[2].toInt()
addBotOffer(id, amount)
notify(player, "Added ${amount}x ${getItemName(id)} to the bot offers.")
}
define("bange", Privilege.ADMIN) {player, strings ->
val id = strings[1].toInt()
PriceIndex.banItem(id)
notify(player, "Banned ${getItemName(id)} from GE trade.")
}
define("allowge", Privilege.ADMIN) {player, strings ->
val id = strings[1].toInt()
PriceIndex.allowItem(id)
notify(player, "Allowed ${getItemName(id)} for GE trade.")
}
define("geprivacy", Privilege.STANDARD) {player, _ ->
val current = getAttribute(player, "ge-exclude", false)
val new = !current
notify(player, "Your name is now ${if (new) colorize("%RHIDDEN") else colorize("%RSHOWN")}.")
setAttribute(player, "/save:ge-exclude", new)
}
}
companion object {
val pendingOffers = LinkedBlockingDeque<GrandExchangeOffer>()
private val GET_SPECIFIC_OFFER_BY_UID = "SELECT * FROM player_offers WHERE uid = ?;"
private val GET_MATCHES_FROM_PLAYER_OFFERS = "SELECT * FROM player_offers WHERE item_id = ? AND is_sale = ? AND offer_state < 4 AND NOT offer_state = 2;"
private val GET_MATCH_FROM_BOT_OFFERS = "SELECT * FROM bot_offers WHERE item_id = ?;"
private fun getOfferByUid(uid: Long): GrandExchangeOffer? {
var offer: GrandExchangeOffer? = null
GEDB.run { conn ->
val query = conn.prepareStatement(GET_SPECIFIC_OFFER_BY_UID)
query.setLong(1, uid)
val res = query.executeQuery()
if (res.next())
offer = GrandExchangeOffer.fromQuery(res)
}
return offer
}
@JvmStatic
fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int {
var base = max(PriceIndex.getValue(itemID), getItemDefPrice(itemID))
if (from_bot) base = (max(BotPrices.getPrice(itemID), base) * 1.10).toInt()
return base
}
private fun getItemDefPrice(itemID: Int): Int {
return max(itemDefinition(itemID).getConfiguration(ItemConfigParser.GE_PRICE) ?: 0, itemDefinition(itemID).value)
}
@JvmStatic
fun getOfferStats(itemID: Int, sale: Boolean) : String
{
val sb = StringBuilder()
GEDB.run { conn ->
var foundOffers = 0
var totalAmount = 0
var bestPrice = 0
var stmt = conn.createStatement()
if (!sale) {
var botAmt = 0
var botPrice = 0
val player_offers = stmt.executeQuery("SELECT * from player_offers where item_id = $itemID AND is_sale = 1 AND offer_state < 4 AND NOT offer_state = 2")
while (player_offers.next()) {
val o = GrandExchangeOffer.fromQuery(player_offers)
++foundOffers
totalAmount += o.amountLeft
if (o.offeredValue < bestPrice || bestPrice == 0)
bestPrice = o.offeredValue
}
stmt.close()
stmt = conn.createStatement()
val bot_offers = stmt.executeQuery("SELECT * from bot_offers where item_id = $itemID")
if (bot_offers.next()) {
val o = GrandExchangeOffer.fromBotQuery(bot_offers)
botAmt = o.amount
botPrice = getRecommendedPrice(itemID, true)
}
sb.append("Player Stock: <col=FFFFFF>$totalAmount ")
sb.append("</col> Lowest Price: <col=FFFFFF>$bestPrice<br>")
sb.append("-".repeat(50))
sb.append("</col><br>Bot Stock: <col=FFFFFF>$botAmt ")
sb.append("</col> Bot Price: <col=FFFFFF>$botPrice")
} else {
val buy_offers = stmt.executeQuery("SELECT * from player_offers where item_id = $itemID AND is_sale = 0 AND offer_state < 4 AND NOT offer_state = 2")
while (buy_offers.next()) {
val o = GrandExchangeOffer.fromQuery(buy_offers)
++foundOffers
totalAmount += o.amountLeft
if (o.offeredValue > bestPrice)
bestPrice = o.offeredValue
}
sb.append("Buy Offers: <col=FFFFFF>$totalAmount ")
sb.append("</col>Highest Offer: <col=FFFFFF>$bestPrice</col>")
}
stmt.close()
}
return sb.toString()
}
fun addBotOffer(itemID: Int, amount: Int): Boolean
{
if (!PriceIndex.canTrade(itemID))
return false
val offer = GrandExchangeOffer.createBotOffer(itemID, amount)
pendingOffers.addLast(offer)
return true
}
fun dispatch(player: Player, offer: GrandExchangeOffer) : Boolean
{
if ( offer.amount < 1 )
sendMessage(player, "You must choose the quantity you wish to buy!").also { return false }
if ( offer.offeredValue < 1 )
sendMessage(player, "You must choose the price you wish to buy for!").also { return false }
if ( offer.offerState != OfferState.PENDING || offer.uid != 0L )
{
SystemLogger.logWarn(this::class.java, "[GE] DISPATCH FAILURE: ${offer.offerState.name}, UID: ${offer.uid}")
return false
}
if ( player.isArtificial )
offer.playerUID = PlayerDetails.getDetails("2009scape").uid.also { offer.isBot = true }
else
offer.playerUID = player.details.uid
offer.offerState = OfferState.REGISTERED
//GrandExchangeRecords.getInstance(player).update(offer)
if (offer.sell && !player.isArtificial) {
val username = if (getAttribute(player, "ge-exclude", false)) "?????" else player.username
Repository.sendNews(username + " just offered " + offer.amount + " " + getItemName(offer.itemID) + " on the GE.")
}
if (ServerConstants.I_AM_A_CHEATER) {
val otherO = GrandExchangeOffer()
otherO.itemID = offer.itemID
otherO.amount = offer.amount
otherO.sell = !offer.sell
otherO.offeredValue = offer.offeredValue
offer.writeNew()
GameWorld.Pulser.submit(object : Pulse(5) {
override fun pulse(): Boolean {
val offer2 = getOfferByUid(offer.uid) ?: return false
exchange(offer2, otherO)
return true
}
})
return true
}
pendingOffers.add(offer)
return true
}
fun exchange(offer: GrandExchangeOffer, other: GrandExchangeOffer)
{
if(offer.sell == other.sell) return //Don't exchange if they are both buy/sell offers
val amount = Integer.min(offer.amount - offer.completedAmount, other.amount - other.completedAmount)
if (amount == 0) return
val seller = if(offer.sell) offer else other
val buyer = if(offer == seller) other else offer
val sellerBias = seller.timeStamp > buyer.timeStamp
//If the buyer is buying for less than the seller is selling for, don't exchange
if(seller.offeredValue > buyer.offeredValue) return
seller.completedAmount += amount
buyer.completedAmount += amount
if(seller.amountLeft < 1 && seller.player != null)
seller.player!!.audioManager.send(Audio(4042,1,1))
seller.addWithdrawItem(995, amount * if(sellerBias) buyer.offeredValue else seller.offeredValue)
buyer.addWithdrawItem(seller.itemID, amount)
if(!sellerBias)
buyer.addWithdrawItem(995, amount * (buyer.offeredValue - seller.offeredValue))
if(seller.amountLeft < 1)
seller.offerState = OfferState.COMPLETED
if(buyer.amountLeft < 1)
buyer.offerState = OfferState.COMPLETED
val totalCoinXC = (if(sellerBias) buyer.offeredValue else seller.offeredValue) * amount
seller.totalCoinExchange += totalCoinXC
buyer.totalCoinExchange += totalCoinXC
if(canUpdatePriceIndex(seller, buyer))
PriceIndex.addTrade(offer.itemID, amount, (totalCoinXC / amount))
/*
if (seller.amountLeft > 0) {
Discord.postOfferUpdate(true, seller.itemID, seller.offeredValue, seller.amountLeft)
}
if (buyer.amountLeft > 0) {
Discord.postOfferUpdate(false, buyer.itemID, buyer.offeredValue, buyer.amountLeft)
}
*/
seller.update()
val sellerPlayer = Repository.uid_map[seller.playerUID]
sellerPlayer?.let { GrandExchangeRecords.getInstance(sellerPlayer).visualizeRecords() }
buyer.update()
val buyerPlayer = Repository.uid_map[buyer.playerUID]
buyerPlayer?.let { GrandExchangeRecords.getInstance(buyerPlayer).visualizeRecords() }
}
private fun canUpdatePriceIndex(seller: GrandExchangeOffer, buyer: GrandExchangeOffer): Boolean {
if(seller.playerUID == buyer.playerUID) return false
if(!ServerConstants.BOTS_INFLUENCE_PRICE_INDEX && (seller.isBot || buyer.isBot)) return false;
return true
}
fun getValidOffers(): List<GrandExchangeOffer>
{
val offers = ArrayList<GrandExchangeOffer>()
GEDB.run { conn ->
val stmt = conn.createStatement()
val results =
stmt.executeQuery("SELECT * FROM player_offers WHERE offer_state < 4 AND NOT offer_state = 2")
while (results.next()) {
val o = GrandExchangeOffer.fromQuery(results)
offers.add(o)
}
stmt.close()
}
return offers
}
fun getBotOffers(): List<GrandExchangeOffer>
{
val offers = ArrayList<GrandExchangeOffer>()
GEDB.run { conn ->
val stmt = conn.createStatement()
val results = stmt.executeQuery("SELECT item_id,amount FROM bot_offers WHERE amount > 0")
while (results.next()) {
val o = GrandExchangeOffer.fromBotQuery(results)
offers.add(o)
}
stmt.close()
}
return offers
}
}
override fun startup(){
GEDB.init()
boot()
}
}
@@ -0,0 +1,240 @@
package core.game.ge
import core.api.getAttribute
import core.cache.def.impl.ItemDefinition
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.net.packet.PacketRepository
import core.net.packet.context.ContainerContext
import core.net.packet.context.GrandExchangeContext
import core.net.packet.out.ContainerPacket
import core.net.packet.out.GrandExchangePacket
import core.integrations.discord.Discord
import core.game.world.repository.Repository
import java.sql.ResultSet
/**
* A struct holding all the data for grand exchange offers.
*
* @author Ceikry
*/
class GrandExchangeOffer() {
var itemID = 0
var amount = 0
var completedAmount = 0
var offeredValue = 0
var index = 0
var sell = false
var offerState = OfferState.PENDING
var uid: Long = 0
var timeStamp: Long = 0
var withdraw = arrayOfNulls<Item>(2)
var totalCoinExchange = 0
var player: Player? = null
var playerUID = 0
var isLimitation = false
var isBot = false
/**
* Gets the total amount of money entered.
* @return The total value.
*/
val totalValue: Int
get() = offeredValue * amount
/**
* Gets the amount of this item left to buy.
* @return The amount.
*/
val amountLeft: Int
get() = amount - completedAmount
/**
* Checks if this offer is still active for dispatching.
* @return `True` if so.
*/
val isActive: Boolean
get() = offerState != OfferState.ABORTED && offerState != OfferState.PENDING && offerState != OfferState.COMPLETED && offerState != OfferState.REMOVED
fun addWithdrawItem(id: Int, amount: Int)
{
if(amount == 0) return
//loop checking if the item is already present first
for(item in withdraw)
if(item != null && item.id == id)
{
item.amount += amount
return
}
//if we make it to this point, the item was not present. Loop to find first null slot and stick item there.
for((index,item) in withdraw.withIndex())
if(item == null)
{
withdraw[index] = Item(id, amount)
return
}
//send container update packet to player if they exist (are online)
if ( player != null )
visualize(player)
}
fun visualize(player: Player?)
{
player ?: return
PacketRepository.send(
GrandExchangePacket::class.java,
GrandExchangeContext(player, index.toByte(), offerState.ordinal.toByte(), itemID.toShort(),
sell, offeredValue, amount, completedAmount, totalCoinExchange)
)
PacketRepository.send(ContainerPacket::class.java, ContainerContext(player, -1, -1757, 523 + index, withdraw, false))
}
fun update()
{
GEDB.run { conn ->
if (isBot) {
val stmt = conn.prepareStatement("UPDATE bot_offers SET amount = ? WHERE item_id = ?")
stmt.setInt(1, amountLeft)
stmt.setInt(2, itemID)
stmt.executeUpdate()
stmt.close()
} else {
val stmt = conn.prepareStatement("UPDATE player_offers SET amount_complete = ?, offer_state = ?, total_coin_xc = ?, withdraw_items = ?, slot_index = ? WHERE uid = ?")
stmt.setInt(1, completedAmount)
stmt.setInt(2, offerState.ordinal)
stmt.setInt(3, totalCoinExchange)
stmt.setString(4, encodeWithdraw())
stmt.setInt(5, index)
stmt.setLong(6, uid)
stmt.executeUpdate()
stmt.close()
}
}
}
/** Called when writing a brand new offer to the database. Should not be used under any other circumstance **/
fun writeNew()
{
GEDB.run { conn ->
if (isBot) {
val stmt = conn.createStatement()
val result = stmt.executeQuery("SELECT * from bot_offers where item_id = $itemID")
val isExists = result.next()
if (isExists) {
val oldAmount = result.getInt("amount")
stmt.executeUpdate("UPDATE bot_offers set amount = ${oldAmount + amount} where item_id = $itemID")
} else
stmt.executeUpdate("INSERT INTO bot_offers(item_id,amount) values($itemID,$amount)")
stmt.close()
} else {
val stmt = conn.createStatement()
stmt.executeUpdate(
"INSERT INTO player_offers(player_uid, item_id, amount_total, offered_value, time_stamp, offer_state, is_sale, slot_index) " +
"values($playerUID,$itemID,$amount,$offeredValue,${System.currentTimeMillis()},${offerState.ordinal},${if (sell) 1 else 0}, $index)"
)
val nowuid = stmt.executeQuery("SELECT last_insert_rowid()")
uid = nowuid.getLong(1)
GrandExchangeRecords.getInstance(player).offerRecords[index] = GrandExchangeRecords.OfferRecord(uid, index)
visualize(player)
stmt.close()
val username = if (getAttribute(player ?: return@run, "ge-exclude", false)) "?????"
else player?.username ?: "?????"
Discord.postNewOffer(sell, itemID, offeredValue, amount, username)
}
}
}
private fun encodeWithdraw() : String
{
val sb = StringBuilder()
for((index, item) in withdraw.withIndex())
{
sb.append(index)
sb.append(",")
if(item == null)
sb.append("null")
else
sb.append(item.id)
sb.append(",")
if(item == null)
sb.append("null")
else
sb.append(item.amount)
if(index + 1 < withdraw.size)
sb.append(":")
}
return sb.toString()
}
override fun toString(): String {
return "[name=" + ItemDefinition.forId(itemID).name + ", itemId=" + itemID + ", amount=" + amount + ", completedAmount=" + completedAmount + ", offeredValue=" + offeredValue + ", index=" + index + ", sell=" + sell + ", state=" + offerState + ", withdraw=" + withdraw.contentToString() + ", totalCoinExchange=" + totalCoinExchange + ", playerUID=" + playerUID + "]"
}
companion object {
fun fromQuery(result: ResultSet): GrandExchangeOffer
{
val o = GrandExchangeOffer()
o.itemID = result.getInt("item_id")
o.amount = result.getInt("amount_total")
o.completedAmount = result.getInt("amount_complete")
o.offeredValue = result.getInt("offered_value")
o.sell = result.getInt("is_sale") == 1
o.offerState = OfferState.values()[result.getInt("offer_state")]
o.uid = result.getLong("uid")
o.timeStamp = result.getLong("time_stamp")
val itemString = result.getString("withdraw_items")
if(itemString != null && itemString.isNotEmpty()) {
val items = itemString.split(":")
for (item in items) {
if(item.isEmpty()) continue
val tokens = item.split(",")
val index = tokens[0].toInt()
if (tokens[1] == "null") continue //Skip null slots
o.withdraw[index] = Item(tokens[1].toInt(), tokens[2].toInt())
}
}
o.totalCoinExchange = result.getInt("total_coin_xc")
o.playerUID = result.getInt("player_uid")
o.index = result.getInt("slot_index")
o.player = Repository.players.firstOrNull { it.details.accountInfo.uid == o.playerUID }
return o
}
fun fromBotQuery(result: ResultSet): GrandExchangeOffer
{
val o = GrandExchangeOffer()
o.sell = true
o.amount = result.getInt("amount")
o.offerState = OfferState.REGISTERED
o.itemID = result.getInt("item_id")
o.offeredValue = GrandExchange.getRecommendedPrice(o.itemID, true)
o.isBot = true
o.timeStamp = System.currentTimeMillis()
return o
}
fun createBotOffer(itemId: Int, amount: Int, sale: Boolean = true) : GrandExchangeOffer
{
val o = GrandExchangeOffer()
o.sell = sale
o.itemID = itemId
o.amount = amount
o.offeredValue = GrandExchange.getRecommendedPrice(itemId, true)
o.offerState = OfferState.REGISTERED
o.isBot = true
return o
}
}
}
@@ -0,0 +1,254 @@
package core.game.ge
import core.api.*
import core.cache.def.impl.ItemDefinition
import core.game.component.Component
import core.game.node.entity.player.Player
import org.json.simple.JSONArray
import org.json.simple.JSONObject
import org.rs09.consts.Components
import core.tools.SystemLogger
import java.text.NumberFormat
import java.util.*
/**
* Handles the grand exchange interfaces for the player.
*
* @author Emperor
* @author Angle
*/
class GrandExchangeRecords(private val player: Player? = null) : PersistPlayer, LoginListener {
var history = arrayOfNulls<GrandExchangeOffer>(5)
val offerRecords = arrayOfNulls<OfferRecord>(6)
override fun login(player: Player) {
val instance = GrandExchangeRecords(player)
player.setAttribute("ge-records", instance)
}
override fun parsePlayer(player: Player, data: JSONObject) {
/**
* Parse history from JSON
*/
val historyRaw = data["ge-history"]
if(historyRaw != null){
val history = historyRaw as JSONArray
for (i in history.indices) {
val offer = history[i] as JSONObject
val o = GrandExchangeOffer()
o.itemID = offer["itemId"].toString().toInt()
o.sell = offer["isSell"] as Boolean
o.totalCoinExchange = (offer["totalCoinExchange"].toString().toInt())
o.completedAmount = (offer["completedAmount"].toString().toInt())
getInstance(player).history[i] = o
}
}
/**
* Read offers from the database
*/
val needsIndex = ArrayDeque<GrandExchangeOffer>()
val instance = getInstance(player)
GEDB.run { conn ->
val stmt = conn.createStatement()
val offer_records = stmt.executeQuery("SELECT * from player_offers where player_uid = ${player.details.uid} AND offer_state < 6")
while (offer_records.next()) {
val offer = GrandExchangeOffer.fromQuery(offer_records)
if (offer.index == -1) //used to index old (converted from JSON) offers
needsIndex.push(offer)
else
instance.offerRecords[offer.index] = OfferRecord(offer.uid, offer.index)
}
stmt.close()
}
if (needsIndex.isNotEmpty()) {
for ((index, offer) in offerRecords.withIndex()) {
if (offer == null) {
val o = needsIndex.pop()
o.index = index
instance.offerRecords[o.index] = OfferRecord(o.uid, o.index)
o.update() //write the new index to the database
}
}
while(needsIndex.isNotEmpty()) //If we enter this loop, there were more offers than can fit inside the 6 slots. This should never happen - at least, not anymore. Can't speak for JSON offers.
{
val o = needsIndex.pop()
SystemLogger.logGE("[WARN] PLAYER HAD EXTRA OFFER - RECOMMEND IMMEDIATE REFUND OF CONTENTS -> OFFER UID: ${o.uid}")
SystemLogger.logGE("[WARN] AS PER ABOVE MESSAGE, REFUND CONTENTS OF OFFER AND MANUALLY SET offer_state = 6")
}
}
instance.init()
}
override fun savePlayer(player: Player, save: JSONObject) {
/**
* Save history to JSON
*/
val history = JSONArray()
getInstance(player).history.map {
if(it != null){
val historyEntry = JSONObject()
historyEntry["isSell"] = it.sell
historyEntry["itemId"] = it.itemID.toString()
historyEntry["totalCoinExchange"] = it.totalCoinExchange.toString()
historyEntry["completedAmount"] = it.completedAmount.toString()
history.add(historyEntry)
}
}
save["ge-history"] = history
}
/**
* Opens the collection box.
*/
fun openCollectionBox() {
if (!player!!.bankPinManager.isUnlocked) {
player.bankPinManager.openType(3)
return
}
player.interfaceManager.openComponent(Components.STOCKCOLLECT_109)
player.packetDispatch.sendIfaceSettings(6, 18, 109, 0, 2)
player.packetDispatch.sendIfaceSettings(6, 23, 109, 0, 2)
player.packetDispatch.sendIfaceSettings(6, 28, 109, 0, 2)
player.packetDispatch.sendIfaceSettings(6, 36, 109, 0, 2)
player.packetDispatch.sendIfaceSettings(6, 44, 109, 0, 2)
player.packetDispatch.sendIfaceSettings(6, 52, 109, 0, 2)
visualizeRecords()
}
fun visualizeRecords()
{
GEDB.run { conn ->
val stmt = conn.createStatement()
for (record in offerRecords) {
if (record != null) {
val offer_raw = stmt.executeQuery("select * from player_offers where uid = ${record.uid}")
if (offer_raw.next()) {
val offer = GrandExchangeOffer.fromQuery(offer_raw)
if (offer.offerState == OfferState.REMOVED) continue
offer.index = record.slot
offer.visualize(player)
}
}
}
stmt.close()
}
}
fun getOffer(index: Int) : GrandExchangeOffer?
{
if(index == -1) return getOffer(null)
return getOffer(offerRecords[index])
}
fun getOffer(record: OfferRecord?) : GrandExchangeOffer?
{
record ?: return null
var offerToReturn: GrandExchangeOffer? = null
GEDB.run { conn ->
val stmt = conn.createStatement()
val offer_raw = stmt.executeQuery("select * from player_offers where uid = ${record.uid}")
if (offer_raw.next()) {
val offer = GrandExchangeOffer.fromQuery(offer_raw)
offer.index = record.slot
stmt.close()
offerToReturn = offer
}
stmt.close()
}
return offerToReturn
}
/**
* Opens the history log.
*
* @param p The player to open it for.
*/
fun openHistoryLog(p: Player) {
p.interfaceManager.open(Component(643))
for (i in history.indices) {
val o: GrandExchangeOffer? = history[i]
if (o == null) {
for (j in 0..3) {
p.packetDispatch.sendString("-", 643, 25 + i + j * 5)
}
continue
}
p.packetDispatch.sendString(if (o.sell) "You sold" else "You bought", 643, 25 + i)
p.packetDispatch.sendString(
NumberFormat.getNumberInstance(Locale.US).format(o.completedAmount.toLong()),
643,
30 + i
)
p.packetDispatch.sendString(ItemDefinition.forId(o.itemID).name, 643, 35 + i)
p.packetDispatch.sendString(
NumberFormat.getNumberInstance(Locale.US).format(o.totalCoinExchange.toLong()) + " gp", 643, 40 + i
)
}
}
/**
* Initializes the grand exchange.
*/
fun init() {
// Were trades made while gone?
var updated = false
for (record in offerRecords) {
if (record != null) {
val offer = getOffer(record) ?: continue
if (!updated && (offer.withdraw[0] != null || offer.withdraw[1] != null)) {
updated = true
}
offer.visualize(player)
}
}
if (updated) {
sendMessage(player!!, "You have items from the Grand Exchange waiting in your collection box.")
}
}
fun hasActiveOffer(): Boolean {
for (i in offerRecords) {
if (i != null)
return true
}
return false
}
/**
* Formats the grand exchange.
*
* @return the formatted offer for the SQL database.
*/
fun format(): String? {
var log = ""
for (record in offerRecords) {
if (record != null) {
val offer = getOffer(record) ?: continue
log += offer.itemID.toString() + "," + offer.amount + "," + offer.sell + "|"
}
}
if (log.isNotEmpty() && log[log.length - 1] == '|') {
log = log.substring(0, log.length - 1)
}
return log
}
data class OfferRecord(val uid: Long, val slot: Int)
companion object {
@JvmStatic fun getInstance(player: Player? = null): GrandExchangeRecords
{
return player?.getAttribute("ge-records", GrandExchangeRecords()) ?: GrandExchangeRecords()
}
}
}
@@ -0,0 +1,43 @@
package core.game.ge;
/**
* Represents the state of a Grand Exchange offer.
* @author Emperor
*/
public enum OfferState {
/**
* The player is still constructing the offer.
*/
PENDING,
/**
* The player has confirmed the offer (the offer is being dispatched).
*/
REGISTERED,
/**
* The offer has been aborted.
*/
ABORTED,
/**
* The offer has been updated.
*/
UPDATED,
/**
* The offer is completed.
*/
COMPLETED,
/**
* The offer is outdated.
*/
OUTDATED,
/**
* The offer has been removed.
*/
REMOVED;
}
+137
View File
@@ -0,0 +1,137 @@
package core.game.ge
import core.api.itemDefinition
import java.sql.ResultSet
object PriceIndex {
@JvmStatic fun canTrade(id: Int): Boolean {
var canTrade = false
GEDB.run { conn ->
val stmt = conn.prepareStatement(EXISTS_QUERY)
stmt.setInt(1, id)
val res = stmt.executeQuery()
if (res.next()) {
canTrade = res.getInt(1) == 1
}
}
return canTrade
}
fun banItem(id: Int) {
GEDB.run { conn ->
val stmt = conn.prepareStatement(REMOVE_QUERY)
stmt.setInt(1, id)
stmt.execute()
}
}
fun allowItem(id: Int) {
if(canTrade(id)) return
GEDB.run { conn ->
val stmt = conn.prepareStatement(INSERT_QUERY)
stmt.setInt(1, id)
stmt.setInt(2, itemDefinition(id).getAlchemyValue(true))
stmt.setInt(3, itemDefinition(id).getAlchemyValue(true))
stmt.setInt(4, 0)
stmt.setInt(5, 0)
stmt.execute()
}
}
fun getValue(id: Int): Int {
var value = itemDefinition(id).getAlchemyValue(true)
GEDB.run { conn ->
val stmt = conn.prepareStatement(GET_VALUE_QUERY)
stmt.setInt(1, id)
val res = stmt.executeQuery()
if (res.next()) {
value = res.getInt(1)
}
}
return value
}
fun addTrade(id: Int, amount: Int, pricePerUnit: Int) {
val oldInfo = getInfo(id) ?: return
val newInfo = oldInfo.copy()
val volumeResetThreshold =
if (pricePerUnit >= 1000000) 500
else if (pricePerUnit >= 100000) 1000
else if (pricePerUnit >= 25000) 2500
else 10000
newInfo.totalValue += (amount * pricePerUnit)
newInfo.uniqueTrades += amount
newInfo.lastUpdate = System.currentTimeMillis()
newInfo.currentValue = (newInfo.totalValue / newInfo.uniqueTrades).toInt()
if (newInfo.uniqueTrades > volumeResetThreshold) {
val newAmt = (0.1 * volumeResetThreshold).toInt()
newInfo.uniqueTrades = newAmt
newInfo.totalValue = newAmt.toLong() * newInfo.currentValue
}
updateInfo(newInfo)
}
private fun updateInfo(newInfo: PriceInfo) {
GEDB.run { conn ->
val stmt = conn.prepareStatement(UPDATE_QUERY)
stmt.setInt(1, newInfo.currentValue)
stmt.setLong(2, newInfo.totalValue)
stmt.setInt(3, newInfo.uniqueTrades)
stmt.setLong(4, newInfo.lastUpdate)
stmt.setInt(5, newInfo.itemId)
stmt.execute()
}
}
private fun getInfo(id: Int): PriceInfo? {
var priceInfo: PriceInfo? = null
GEDB.run { conn ->
val stmt = conn.prepareStatement(SELECT_QUERY)
stmt.setInt(1, id)
val res = stmt.executeQuery()
if (res.next()) {
priceInfo = PriceInfo.fromQuery(res)
}
}
return priceInfo
}
const val SELECT_QUERY = "SELECT * FROM price_index WHERE item_id = ?;"
const val UPDATE_QUERY = "UPDATE price_index SET value = ?, total_value = ?, unique_trades = ?, last_update = ? WHERE item_id = ?;"
const val EXISTS_QUERY = "SELECT EXISTS(SELECT 1 FROM price_index WHERE item_id = ?);"
const val REMOVE_QUERY = "DELETE FROM price_index WHERE item_id = ?;"
const val INSERT_QUERY = "INSERT INTO price_index (item_id, value, total_value, unique_trades, last_update) VALUES (?,?,?,?,?);"
const val GET_VALUE_QUERY = "SELECT value FROM price_index WHERE item_id = ?;"
}
data class PriceInfo(
var itemId: Int,
var currentValue: Int,
var totalValue: Long,
var uniqueTrades: Int,
var lastUpdate: Long
) {
fun copy() : PriceInfo {
return PriceInfo(itemId, currentValue, totalValue, uniqueTrades, lastUpdate)
}
companion object {
fun fromQuery(result: ResultSet) : PriceInfo {
return PriceInfo(
result.getInt(1),
result.getInt(2),
result.getLong(3),
result.getInt(4),
result.getLong(5)
)
}
}
}