Merge branch 'ge-improvements' into 'master'

GE Improvements

See merge request 2009scape/2009scape!158
This commit is contained in:
Ceikry 2021-07-13 15:26:20 +00:00
commit bdaf707983
10 changed files with 523 additions and 486 deletions

View file

@ -3,6 +3,7 @@ package core.game.ge;
import core.game.component.Component; import core.game.component.Component;
import core.game.node.entity.player.Player; import core.game.node.entity.player.Player;
import core.tools.StringUtils; import core.tools.StringUtils;
import rs09.game.ge.OfferManager;
/** /**
* Represents the glass used to open the guide prices for the different type of * Represents the glass used to open the guide prices for the different type of
@ -139,7 +140,7 @@ public final class GEGuidePrice {
player.getPacketDispatch().sendInterfaceConfig(642, i, false); player.getPacketDispatch().sendInterfaceConfig(642, i, false);
} }
for (GuideItem item : getItems()) { for (GuideItem item : getItems()) {
player.getPacketDispatch().sendString("" + GrandExchangeDatabase.getDatabase().get(item.getItem()).getValue() + " gp", COMPONENT.getId(), item.getChildData()[0]); player.getPacketDispatch().sendString("" + OfferManager.getRecommendedPrice(item.item, false) + " gp", COMPONENT.getId(), item.getChildData()[0]);
} }
} }
} }

View file

@ -22,6 +22,7 @@ import core.plugin.Initializable;
import core.plugin.Plugin; import core.plugin.Plugin;
import kotlin.Unit; import kotlin.Unit;
import rs09.game.ge.GrandExchangeOffer; import rs09.game.ge.GrandExchangeOffer;
import rs09.game.ge.OfferManager;
import rs09.game.ge.PlayerGrandExchange; import rs09.game.ge.PlayerGrandExchange;
import rs09.game.interaction.npc.BogrogPouchSwapper; import rs09.game.interaction.npc.BogrogPouchSwapper;
import rs09.game.world.GameWorld; import rs09.game.world.GameWorld;
@ -69,7 +70,7 @@ public class GrandExchangeInterface extends ComponentPlugin {
if (offer == null || value < 1 || offer.getOfferState() != OfferState.PENDING) { if (offer == null || value < 1 || offer.getOfferState() != OfferState.PENDING) {
return; return;
} }
if (value == GrandExchangeDatabase.getDatabase().get(offer.getItemID()).getValue()) { if (value == OfferManager.getRecommendedPrice(offer.getItemID(), false)) {
player.getAudioManager().send(new Audio(4043, 1, 1)); player.getAudioManager().send(new Audio(4043, 1, 1));
} else if (value > offer.getOfferedValue()) { } else if (value > offer.getOfferedValue()) {
player.getAudioManager().send(new Audio(4041, 1, 1)); player.getAudioManager().send(new Audio(4041, 1, 1));
@ -77,7 +78,7 @@ public class GrandExchangeInterface extends ComponentPlugin {
player.getAudioManager().send(new Audio(4045, 1, 1)); player.getAudioManager().send(new Audio(4045, 1, 1));
} }
offer.setOfferedValue(value); offer.setOfferedValue(value);
player.getConfigManager().send(1111, offer.getOfferedValue()); player.varpManager.get(1111).setVarbit(0,offer.getOfferedValue()).send(player);
} }
@Override @Override
@ -348,14 +349,14 @@ public class GrandExchangeInterface extends ComponentPlugin {
return false; return false;
case 180: case 180:
if (offer != null) { if (offer != null) {
setOfferValue(player, offer, GrandExchangeDatabase.getDatabase().get(offer.getItemID()).getValue()); setOfferValue(player, offer, OfferManager.getRecommendedPrice(offer.getItemID(), false));
return true; return true;
} }
return false; return false;
case 177: // mid - 5% value case 177: // mid - 5% value
case 183: // mid + 5% value case 183: // mid + 5% value
if (offer != null) { if (offer != null) {
setOfferValue(player, offer, (int) (GrandExchangeDatabase.getDatabase().get(offer.getItemID()).getValue() * (button == 177 ? 0.95 : 1.05))); setOfferValue(player, offer, (int) (OfferManager.getRecommendedPrice(offer.getItemID(), false) * (button == 177 ? 0.95 : 1.05)));
return true; return true;
} }
return false; return false;

View file

@ -19,6 +19,7 @@ import rs09.game.ai.AIPlayer;
import rs09.game.ai.AIRepository; import rs09.game.ai.AIRepository;
import rs09.game.ai.general.GeneralBotCreator; import rs09.game.ai.general.GeneralBotCreator;
import rs09.game.content.global.NPCDropTable; import rs09.game.content.global.NPCDropTable;
import rs09.game.ge.OfferManager;
import rs09.game.system.config.ItemConfigParser; import rs09.game.system.config.ItemConfigParser;
import rs09.game.world.repository.Repository; import rs09.game.world.repository.Repository;
@ -177,7 +178,7 @@ public final class NPCDropTables {
} }
} }
player.sendMessage(player.getInterfaceManager().isResizable()+""); player.sendMessage(player.getInterfaceManager().isResizable()+"");
int price = item.getName().endsWith("charm") ? 100 : GrandExchangeDatabase.getDatabase().get(itemId).getValue(); int price = item.getName().endsWith("charm") ? 100 : OfferManager.getRecommendedPrice(itemId, false);
looter.getGlobalData().setLootSharePoints(looter.getGlobalData().getLootSharePoints() - (price) + ((price / looters.size()))); looter.getGlobalData().setLootSharePoints(looter.getGlobalData().getLootSharePoints() - (price) + ((price / looters.size())));
looter.sendMessage((player.getInterfaceManager().isResizable() ? "<col=32CD32>" : "<col=009900>") + "You received: " + item.getAmount() + " " + item.getName()); looter.sendMessage((player.getInterfaceManager().isResizable() ? "<col=32CD32>" : "<col=009900>") + "You received: " + item.getAmount() + " " + item.getName());
for (Player p : looters) { for (Player p : looters) {

View file

@ -8,6 +8,7 @@ import core.game.interaction.Interaction;
import core.game.interaction.OptionHandler; import core.game.interaction.OptionHandler;
import core.game.node.Node; import core.game.node.Node;
import core.game.node.entity.combat.equipment.DegradableEquipment; import core.game.node.entity.combat.equipment.DegradableEquipment;
import rs09.game.ge.OfferManager;
/** /**
* Represents an item. * Represents an item.
@ -99,10 +100,7 @@ public class Item extends Node{
*/ */
public long getValue() { public long getValue() {
long value = 1; long value = 1;
GrandExchangeEntry entry = GrandExchangeDatabase.getDatabase().get(getId()); value = OfferManager.getRecommendedPrice(getId(), false);
if (entry != null) {
value = entry.getValue();
}
if (definition.getValue() > value) { if (definition.getValue() > value) {
value = definition.getValue(); value = definition.getValue();
} }

View file

@ -161,7 +161,7 @@ public class GrandExchangeTab extends ConsoleTab {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
} }
for (GrandExchangeOffer o : OfferManager.Companion.getOffersForItem(itemId)) { for (GrandExchangeOffer o : OfferManager.getOffersForItem(itemId)) {
if (o == null) { if (o == null) {
continue; continue;
} }
@ -184,7 +184,7 @@ public class GrandExchangeTab extends ConsoleTab {
JOptionPane.showMessageDialog(null, "Error! No data in DB yet. Press load."); JOptionPane.showMessageDialog(null, "Error! No data in DB yet. Press load.");
return; return;
} }
for (GrandExchangeOffer offer : OfferManager.Companion.getOFFER_MAPPING().values()) { for (GrandExchangeOffer offer : OfferManager.getOFFER_MAPPING().values()) {
model.addElement(offer); model.addElement(offer);
} }
} }

View file

@ -0,0 +1,46 @@
package rs09.game.ge
import core.game.world.callback.CallBack
import rs09.game.system.SystemLogger
import rs09.tools.secondsToTicks
object GrandExchange : CallBack {
/**
* 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(local: Boolean){
if(isRunning) return
if(!local){
TODO("Remote GE server stuff")
}
SystemLogger.logGE("Initializing GE...")
OfferManager.init()
SystemLogger.logGE("GE Initialized.")
SystemLogger.logGE("Initializing GE Update Worker")
val t = Thread {
Thread.currentThread().name = "GE Update Worker"
while(true) {
SystemLogger.logGE("Updating offers...")
OfferManager.update()
Thread.sleep(60_000) //sleep for 60 seconds
}
}.start()
isRunning = true
}
override fun call(): Boolean {
boot(true)
return true
}
}

View file

@ -1,5 +1,6 @@
package rs09.game.ge package rs09.game.ge
import api.ContentAPI
import rs09.ServerConstants import rs09.ServerConstants
import core.cache.def.impl.ItemDefinition import core.cache.def.impl.ItemDefinition
import core.game.content.eco.EcoStatus import core.game.content.eco.EcoStatus
@ -23,524 +24,507 @@ import org.json.simple.JSONArray
import org.json.simple.JSONObject import org.json.simple.JSONObject
import org.json.simple.parser.JSONParser import org.json.simple.parser.JSONParser
import rs09.game.ai.AIPlayer import rs09.game.ai.AIPlayer
import rs09.game.system.config.ItemConfigParser
import java.io.File import java.io.File
import java.io.FileReader import java.io.FileReader
import java.io.FileWriter import java.io.FileWriter
import java.io.IOException import java.io.IOException
import java.lang.Integer.max
import java.lang.Integer.min import java.lang.Integer.min
import java.util.* import java.util.*
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import javax.script.ScriptEngineManager import javax.script.ScriptEngineManager
import kotlin.collections.ArrayList
class OfferManager : Pulse(), CallBack { object OfferManager {
/** /**
* How often in ticks should ge offers that hit the buying limit be reprocessed? * The update notification.
*/ */
private val RESET_BUYING_LIMIT_INTERVAL = 24000 private const val UPDATE_NOTIFICATION = "One or more of your grand exchange offers have been updated."
/** /**
* How often should the database be saved to disk? * The database path.
*/ */
private val SAVE_EVERY = ServerConstants.GE_AUTOSAVE_FREQUENCY private val DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "offer_dispatch.json"
override fun call(): Boolean { /**
init() * Bot DB path
delay = 1 */
GameWorld.Pulser.submit(this) private val BOT_DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "bot_offers.json"
/**
* The offset of the offer UIDs.
*/
private var offsetUID: Long = 1
/**
* The mapping of all current offers. Stored in multiple maps.
*
* First map is offsetID, Offer
* Second is itemID, offsetID, Offer
* Final is playerID, offsetID, Offer
*/
@JvmStatic
val OFFER_MAPPING: MutableMap<Long, GrandExchangeOffer> = HashMap()
val OFFERS_BY_ITEMID: MutableMap<Int, MutableList<GrandExchangeOffer>> = HashMap()
private val GE_OFFER_LOCK = ReentrantLock()
/**
* Bot offers are sorted by itemID.
* the second int shows the offer amount. Negative is buying positive selling.
*/
public val BOT_OFFERS: HashMap<Int, Int> = HashMap()
/**
* If the database should be dumped.
*/
public var dumpDatabase = false
/**
* Initializes the Grand Exchange.
*/
fun init() {
GE_OFFER_LOCK.lock()
val file = File(DB_PATH)
if(file.exists() && file.length() != 0L) {
val parser = JSONParser()
val reader: FileReader? = FileReader(DB_PATH)
val saveFile = parser.parse(reader) as JSONObject
offsetUID = saveFile["offsetUID"].toString().toLong()
if (saveFile.containsKey("offers")) {
val offers = saveFile["offers"] as JSONArray
for (offer in offers) {
val o = offer as JSONObject
// Copy all the bot offers from the file
if (o["playerUID"].toString().toInt() == 0) {
addBotOffer(o["itemId"].toString().toInt(), o["amount"].toString().toInt() - o["completedAmount"].toString().toInt())
}
val no = GrandExchangeOffer()
no.itemID = o["itemId"].toString().toInt()
no.sell = o["sale"] as Boolean
no.offeredValue = o["offeredValue"].toString().toInt()
no.amount = o["amount"].toString().toInt()
no.timeStamp = o["timeStamp"].toString().toLong()
no.uid = o["uid"].toString().toLong()
no.completedAmount = o["completedAmount"].toString().toInt()
no.playerUID = o["playerUID"].toString().toInt()
no.offerState = OfferState.values()[o["offerState"].toString().toInt()]
no.totalCoinExchange = o["totalCoinExchange"].toString().toInt()
val withdrawData = o["withdrawItems"] as JSONArray
for ((index, data) in withdrawData.withIndex()) {
val item = data as JSONObject
val it = Item(item["id"].toString().toInt(), item["amount"].toString().toInt())
no.withdraw[index] = it
}
addEntry(no)
}
}
}
if(File(BOT_DB_PATH).exists()) {
try {
val botReader: FileReader? = FileReader(BOT_DB_PATH)
val botSave = JSONParser().parse(botReader) as JSONObject
if (botSave.containsKey("offers")) {
val offers = botSave["offers"] as JSONArray
for (offer in offers) {
val o = offer as JSONObject
addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt())
}
}
} catch (e: IOException) {
SystemLogger.logWarn("Unable to load bot offers. Perhaps it doesn't exist?")
}
}
GE_OFFER_LOCK.unlock()
}
fun update(){
for (offer in OFFER_MAPPING.values) {
if (offer.isActive) {
updateOffer(offer)
}
}
}
fun buyFromBots(offer: GrandExchangeOffer) {
if (BOT_OFFERS[offer.itemID] == null) {
return
}
val botPrice = getRecommendedPrice(offer.itemID, true)
if (offer.offeredValue < botPrice) {
return
}
val amount = min(BOT_OFFERS[offer.itemID]!!, getBuylimitAmount(offer))
val botOffer = GrandExchangeOffer()
botOffer.sell = true
botOffer.amount = amount
botOffer.offerState = OfferState.REGISTERED
botOffer.offeredValue = botPrice
exchange(offer, botOffer)
BOT_OFFERS[offer.itemID] = BOT_OFFERS[offer.itemID]!! - amount
}
private fun buyFromBotsWithItem(itemID: Int) {
if (OFFERS_BY_ITEMID[itemID] == null || BOT_OFFERS[itemID] == null) {
return
}
for (trade in OFFERS_BY_ITEMID[itemID]!!) {
if (!trade.sell) {
buyFromBots(trade)
}
}
}
fun addBotOffer(itemID: Int, qty: Int): Boolean {
if (GrandExchangeDatabase.getDatabase()[itemID] == null) {
SystemLogger.logWarn("Bot attempted to sell invalid item $itemID")
return false
}
if (BOT_OFFERS[itemID] == null) {
BOT_OFFERS[itemID] = qty
} else {
BOT_OFFERS[itemID] = (qty + BOT_OFFERS[itemID]!!)
}
buyFromBotsWithItem(itemID)
return true return true
} }
override fun pulse(): Boolean { fun amtBotsSelling(itemID: Int): Int {
// TODO: Update offers code if (BOT_OFFERS[itemID] == null) {
if (GameWorld.ticks % RESET_BUYING_LIMIT_INTERVAL == 0) { return 0
BuyingLimitation.clear()
for (offer in OFFER_MAPPING.values) {
if (offer.isActive && offer.isLimitation) {
updateOffer(offer)
}
}
} }
if (BOT_OFFERS[itemID]!! <= 0) {
if (dumpDatabase && (GameWorld.ticks % SAVE_EVERY == 0)) { return 0
//save()
dumpDatabase = false
} }
return BOT_OFFERS[itemID]!!
return false
} }
fun setIndex(offerID: Long, idx: Int) {
if (!OFFER_MAPPING.containsKey(offerID)) {
println("ERROR. GE Entry $offerID not found in database. Playerdata may be corrupted.")
return
}
OFFER_MAPPING[offerID]!!.index = idx
}
companion object { fun removeEntry(offer: GrandExchangeOffer): Boolean{
/** println("REMOVING ENTRY of ID " + offer.itemID)
* The update notification. GE_OFFER_LOCK.lock()
*/ if (!OFFER_MAPPING.containsKey(offer.uid)){
private const val UPDATE_NOTIFICATION = "One or more of your grand exchange offers have been updated."
/**
* The database path.
*/
private val DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "offer_dispatch.json"
/**
* Bot DB path
*/
private val BOT_DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "bot_offers.json"
/**
* The offset of the offer UIDs.
*/
private var offsetUID: Long = 1
/**
* The mapping of all current offers. Stored in multiple maps.
*
* First map is offsetID, Offer
* Second is itemID, offsetID, Offer
* Final is playerID, offsetID, Offer
*/
val OFFER_MAPPING: MutableMap<Long, GrandExchangeOffer> = HashMap()
val OFFERS_BY_ITEMID: MutableMap<Int, MutableList<GrandExchangeOffer>> = HashMap()
private val GE_OFFER_LOCK = ReentrantLock()
/**
* Bot offers are sorted by itemID.
* the second int shows the offer amount. Negative is buying positive selling.
*/
public val BOT_OFFERS: HashMap<Int, Int> = HashMap()
/**
* If the database should be dumped.
*/
public var dumpDatabase = false
/**
* Initializes the Grand Exchange.
*/
fun init() {
GE_OFFER_LOCK.lock()
val file = File(DB_PATH)
if(file.exists() && file.length() != 0L) {
val parser = JSONParser()
val reader: FileReader? = FileReader(DB_PATH)
val saveFile = parser.parse(reader) as JSONObject
offsetUID = saveFile["offsetUID"].toString().toLong()
if (saveFile.containsKey("offers")) {
val offers = saveFile["offers"] as JSONArray
for (offer in offers) {
val o = offer as JSONObject
// Copy all the bot offers from the file
if (o["playerUID"].toString().toInt() == 0) {
addBotOffer(o["itemId"].toString().toInt(), o["amount"].toString().toInt() - o["completedAmount"].toString().toInt())
}
val no = GrandExchangeOffer()
no.itemID = o["itemId"].toString().toInt()
no.sell = o["sale"] as Boolean
no.offeredValue = o["offeredValue"].toString().toInt()
no.amount = o["amount"].toString().toInt()
no.timeStamp = o["timeStamp"].toString().toLong()
no.uid = o["uid"].toString().toLong()
no.completedAmount = o["completedAmount"].toString().toInt()
no.playerUID = o["playerUID"].toString().toInt()
no.offerState = OfferState.values()[o["offerState"].toString().toInt()]
no.totalCoinExchange = o["totalCoinExchange"].toString().toInt()
val withdrawData = o["withdrawItems"] as JSONArray
for ((index, data) in withdrawData.withIndex()) {
val item = data as JSONObject
val it = Item(item["id"].toString().toInt(), item["amount"].toString().toInt())
no.withdraw[index] = it
}
addEntry(no)
}
}
}
if(File(BOT_DB_PATH).exists()) {
try {
val botReader: FileReader? = FileReader(BOT_DB_PATH)
val botSave = JSONParser().parse(botReader) as JSONObject
if (botSave.containsKey("offers")) {
val offers = botSave["offers"] as JSONArray
for (offer in offers) {
val o = offer as JSONObject
addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt())
}
}
} catch (e: IOException) {
SystemLogger.logWarn("Unable to load bot offers. Perhaps it doesn't exist?")
}
}
GE_OFFER_LOCK.unlock() GE_OFFER_LOCK.unlock()
return false
} }
OFFER_MAPPING.remove(offer.uid)
OFFERS_BY_ITEMID[offer.itemID]!!.remove(offer)
GE_OFFER_LOCK.unlock()
return true
}
fun buyFromBots(offer: GrandExchangeOffer) { fun addEntry(offer: GrandExchangeOffer){
if (BOT_OFFERS[offer.itemID] == null) { GE_OFFER_LOCK.lock()
return OFFER_MAPPING[offer.uid] = offer
} if (!OFFERS_BY_ITEMID.containsKey(offer.itemID)) {
val botPrice = BotPrices.getPrice(offer.itemID) OFFERS_BY_ITEMID[offer.itemID] = mutableListOf()
if (offer.offeredValue < botPrice) {
return
}
val amount = min(BOT_OFFERS[offer.itemID]!!, getBuylimitAmount(offer))
val botOffer = GrandExchangeOffer()
botOffer.sell = true
botOffer.amount = amount
botOffer.offerState = OfferState.REGISTERED
botOffer.offeredValue = botPrice
exchange(offer, botOffer)
BOT_OFFERS[offer.itemID] = BOT_OFFERS[offer.itemID]!! - amount
} }
OFFERS_BY_ITEMID[offer.itemID]!!.add(offer)
GE_OFFER_LOCK.unlock()
}
private fun buyFromBotsWithItem(itemID: Int) { fun getQuantitySoldForItem(item: Int): Int {
if (OFFERS_BY_ITEMID[itemID] == null || BOT_OFFERS[itemID] == null) { var qty = 0
return val offs = getOffersForItem(item)
} for (o in offs) {
for (trade in OFFERS_BY_ITEMID[itemID]!!) { if (o.sell) {
if (!trade.sell) { qty += o.amountLeft
buyFromBots(trade)
}
} }
} }
qty += amtBotsSelling(item)
return qty
}
fun addBotOffer(itemID: Int, qty: Int): Boolean { @JvmStatic
if (GrandExchangeDatabase.getDatabase()[itemID] == null) { fun getOffersForItem(item: Int): MutableList<GrandExchangeOffer> {
SystemLogger.logWarn("Bot attempted to sell invalid item $itemID") if (OFFERS_BY_ITEMID.containsKey(item)) {
return false return OFFERS_BY_ITEMID[item]!!
} }
return mutableListOf()
}
if (BOT_OFFERS[itemID] == null) { @JvmStatic
BOT_OFFERS[itemID] = qty fun save(){
} else { GE_OFFER_LOCK.lock()
BOT_OFFERS[itemID] = (qty + BOT_OFFERS[itemID]!!) val root = JSONObject()
} val offers = JSONArray()
buyFromBotsWithItem(itemID)
return true if(OFFER_MAPPING.isEmpty() && BOT_OFFERS.isEmpty()){
return
} }
fun amtBotsSelling(itemID: Int): Int { for(entry in OFFER_MAPPING){
if (BOT_OFFERS[itemID] == null) { val offer = entry.value
return 0 if (offer.offerState == OfferState.REMOVED || entry.value.playerUID == PlayerDetails.getDetails("2009scape").uid) {
continue
} }
if (BOT_OFFERS[itemID]!! <= 0) { val o = JSONObject()
return 0 o["uid"] = entry.key.toString()
o["itemId"] = offer.itemID.toString()
o["sale"] = offer.sell
o["amount"] = offer.amount.toString()
o["completedAmount"] = offer.completedAmount.toString()
o["offeredValue"] = offer.offeredValue.toString()
o["timeStamp"] = offer.timeStamp.toString()
o["offerState"] = offer.offerState.ordinal.toString()
o["totalCoinExchange"] = offer.totalCoinExchange.toString()
o["playerUID"] = offer.playerUID.toString()
val withdrawItems = JSONArray()
for(item in offer.withdraw){
item ?: continue
val it = JSONObject()
it["id"] = item.id.toString()
it["amount"] = item.amount.toString()
withdrawItems.add(it)
} }
return BOT_OFFERS[itemID]!! o["withdrawItems"] = withdrawItems
offers.add(o)
} }
root["offsetUID"] = offsetUID.toString()
root["offers"] = offers
fun setIndex(offerID: Long, idx: Int) { val manager = ScriptEngineManager()
if (!OFFER_MAPPING.containsKey(offerID)) { val scriptEngine = manager.getEngineByName("JavaScript")
println("ERROR. GE Entry $offerID not found in database. Playerdata may be corrupted.") scriptEngine.put("jsonString", root.toJSONString())
return scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
} val prettyPrintedJson = scriptEngine["result"] as String
OFFER_MAPPING[offerID]!!.index = idx
val botRoot = JSONObject()
val botOffers = JSONArray()
for ((item, qty) in BOT_OFFERS) {
val o = JSONObject()
o["item"] = item
o["qty"] = qty
botOffers.add(o)
} }
botRoot["offers"] = botOffers
fun removeEntry(offer: GrandExchangeOffer): Boolean{ scriptEngine.put("jsonString", botRoot.toJSONString())
println("REMOVING ENTRY of ID " + offer.itemID) scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
GE_OFFER_LOCK.lock() val botJson = scriptEngine["result"] as String
if (!OFFER_MAPPING.containsKey(offer.uid)){
GE_OFFER_LOCK.unlock()
return false try {
FileWriter(DB_PATH).use { file ->
file.write(prettyPrintedJson)
file.flush()
file.close()
} }
OFFER_MAPPING.remove(offer.uid) FileWriter(BOT_DB_PATH).use { file ->
OFFERS_BY_ITEMID[offer.itemID]!!.remove(offer) file.write(botJson)
GE_OFFER_LOCK.unlock() file.flush()
return true file.close()
}
} catch (e: Exception) {
e.printStackTrace()
} }
GE_OFFER_LOCK.unlock()
}
fun addEntry(offer: GrandExchangeOffer){ /**
GE_OFFER_LOCK.lock() * Dispatches an offer.
OFFER_MAPPING[offer.uid] = offer * @param player The player.
if (!OFFERS_BY_ITEMID.containsKey(offer.itemID)) { * @param offer The grand exchange offer.
OFFERS_BY_ITEMID[offer.itemID] = mutableListOf() * @return `True` if successful.
} */
OFFERS_BY_ITEMID[offer.itemID]!!.add(offer) @JvmStatic
GE_OFFER_LOCK.unlock() fun dispatch(player: Player, offer: GrandExchangeOffer): Boolean {
if (offer.amount < 1) {
player.packetDispatch.sendMessage("You must choose the quantity you wish to buy!")
println("amountthing")
return false
} }
if (offer.offeredValue < 1) {
fun getQuantitySoldForItem(item: Int): Int { player.packetDispatch.sendMessage("You must choose the price you wish to buy for!")
var qty = 0 println("pricethng")
val offs = getOffersForItem(item) return false
for (o in offs) {
if (o.sell) {
qty += o.amountLeft
}
}
qty += amtBotsSelling(item)
return qty
} }
if (offer.offerState != OfferState.PENDING || offer.uid != 0L) {
fun getOffersForItem(item: Int): MutableList<GrandExchangeOffer> { println("pendingthing")
if (OFFERS_BY_ITEMID.containsKey(item)) { return false
return OFFERS_BY_ITEMID[item]!!
}
return mutableListOf()
} }
if (player.isArtificial) {
@JvmStatic offer.playerUID = PlayerDetails.getDetails("2009scape").uid
fun save(){ // Repository.sendNews("2009scape wants " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " for " + offer.offeredValue + "each.")
GE_OFFER_LOCK.lock() } else {
val root = JSONObject() offer.playerUID = player.details.uid
val offers = JSONArray()
if(OFFER_MAPPING.isEmpty() && BOT_OFFERS.isEmpty()){
return
}
for(entry in OFFER_MAPPING){
val offer = entry.value
if (offer.offerState == OfferState.REMOVED || entry.value.playerUID == PlayerDetails.getDetails("2009scape").uid) {
continue
}
val o = JSONObject()
o["uid"] = entry.key.toString()
o["itemId"] = offer.itemID.toString()
o["sale"] = offer.sell
o["amount"] = offer.amount.toString()
o["completedAmount"] = offer.completedAmount.toString()
o["offeredValue"] = offer.offeredValue.toString()
o["timeStamp"] = offer.timeStamp.toString()
o["offerState"] = offer.offerState.ordinal.toString()
o["totalCoinExchange"] = offer.totalCoinExchange.toString()
o["playerUID"] = offer.playerUID.toString()
val withdrawItems = JSONArray()
for(item in offer.withdraw){
item ?: continue
val it = JSONObject()
it["id"] = item.id.toString()
it["amount"] = item.amount.toString()
withdrawItems.add(it)
}
o["withdrawItems"] = withdrawItems
offers.add(o)
}
root["offsetUID"] = offsetUID.toString()
root["offers"] = offers
val manager = ScriptEngineManager()
val scriptEngine = manager.getEngineByName("JavaScript")
scriptEngine.put("jsonString", root.toJSONString())
scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
val prettyPrintedJson = scriptEngine["result"] as String
val botRoot = JSONObject()
val botOffers = JSONArray()
for ((item, qty) in BOT_OFFERS) {
val o = JSONObject()
o["item"] = item
o["qty"] = qty
botOffers.add(o)
}
botRoot["offers"] = botOffers
scriptEngine.put("jsonString", botRoot.toJSONString())
scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)")
val botJson = scriptEngine["result"] as String
try {
FileWriter(DB_PATH).use { file ->
file.write(prettyPrintedJson)
file.flush()
file.close()
}
FileWriter(BOT_DB_PATH).use { file ->
file.write(botJson)
file.flush()
file.close()
}
} catch (e: Exception) {
e.printStackTrace()
}
GE_OFFER_LOCK.unlock()
} }
offer.uid = nextUID()
/** offer.offerState = OfferState.REGISTERED
* Dispatches an offer. addEntry(offer)
* @param player The player. offer.timeStamp = System.currentTimeMillis()
* @param offer The grand exchange offer. player.playerGrandExchange.update(offer)
* @return `True` if successful. if (offer.sell) {
*/ Repository.sendNews(player.username + " just offered " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " on the GE.")
@JvmStatic
fun dispatch(player: Player, offer: GrandExchangeOffer): Boolean {
if (offer.amount < 1) {
player.packetDispatch.sendMessage("You must choose the quantity you wish to buy!")
println("amountthing")
return false
}
if (offer.offeredValue < 1) {
player.packetDispatch.sendMessage("You must choose the price you wish to buy for!")
println("pricethng")
return false
}
if (offer.offerState != OfferState.PENDING || offer.uid != 0L) {
println("pendingthing")
return false
}
if (player.isArtificial) {
offer.playerUID = PlayerDetails.getDetails("2009scape").uid
// Repository.sendNews("2009scape wants " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " for " + offer.offeredValue + "each.")
} else {
offer.playerUID = player.details.uid
}
offer.uid = nextUID()
offer.offerState = OfferState.REGISTERED
addEntry(offer)
offer.timeStamp = System.currentTimeMillis()
player.playerGrandExchange.update(offer)
if (offer.sell) {
Repository.sendNews(player.username + " just offered " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " on the GE.")
}
if(player !is AIPlayer) {
SystemLogger.logTrade("[GE] ${player.username} ${if (offer.sell) "listed for sale" else "listed buy offer for"} ${offer.amount} ${ItemDefinition.forId(offer.itemID).name.toLowerCase()}")
}
dumpDatabase = true
return true
} }
if(player !is AIPlayer) {
/** SystemLogger.logTrade("[GE] ${player.username} ${if (offer.sell) "listed for sale" else "listed buy offer for"} ${offer.amount} ${ItemDefinition.forId(offer.itemID).name.toLowerCase()}")
* Updates the offer.
* @param offer The G.E. offer to update.
*/
@JvmStatic
fun updateOffer(offer: GrandExchangeOffer) {
if (!offer.isActive) {
return
}
GE_OFFER_LOCK.lock()
for (o in OFFERS_BY_ITEMID[offer.itemID]!!) {
if (o.sell != offer.sell && o.isActive) {
exchange(offer, o)
if (offer.offerState == OfferState.COMPLETED) {
break
}
}
}
buyFromBots(offer)
GE_OFFER_LOCK.unlock()
} }
dumpDatabase = true
return true
}
private fun getBuylimitAmount(offer: GrandExchangeOffer): Int { /**
var left = offer.amountLeft * Updates the offer.
if (!offer.sell && left > 0) { * @param offer The G.E. offer to update.
val maximum = BuyingLimitation.getMaximumBuy(offer.itemID, offer.playerUID) */
if (left >= maximum) { @JvmStatic
left = maximum fun updateOffer(offer: GrandExchangeOffer) {
offer.isLimitation = true if (!offer.isActive) {
} return
}
return left
} }
GE_OFFER_LOCK.lock()
/** for (o in OFFERS_BY_ITEMID[offer.itemID]!!) {
* Exchanges between 2 offers. if (o.sell != offer.sell && o.isActive) {
* @param offer The grand exchange offer to update. exchange(offer, o)
* @param o The other offer to exchange with. if (offer.offerState == OfferState.COMPLETED) {
*/
private fun exchange(offer: GrandExchangeOffer, o: GrandExchangeOffer) {
if (o.sell == offer.sell) {
return
}
if (offer.sell && o.offeredValue < offer.offeredValue || !offer.sell && o.offeredValue > offer.offeredValue) {
return
}
var amount = min(getBuylimitAmount(offer), getBuylimitAmount(o))
if (amount < 1) {
return
}
var coinDifference = if (offer.sell) o.offeredValue - offer.offeredValue else offer.offeredValue - o.offeredValue
if (coinDifference < 0) {
return
}
if (EconomyManagement.getEcoState() == EcoStatus.DRAINING) {
coinDifference *= (1.0 - EconomyManagement.getModificationRate()).toInt()
}
offer.completedAmount = offer.completedAmount + amount
o.completedAmount = o.completedAmount + amount
offer.offerState = if (offer.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED
o.offerState = if (o.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED
if (offer.sell) {
if (offer.amountLeft < 1 && offer.player != null) {
offer.player!!.audioManager.send(Audio(4042, 1, 1))
}
addWithdraw(offer,995, amount * offer.offeredValue)
addWithdraw(o, o.itemID, amount)
BuyingLimitation.updateBoughtAmount(o.itemID, o.playerUID, amount)
} else {
if (o.amountLeft < 1 && o.player != null) {
o.player!!.audioManager.send(Audio(4042, 1, 1))
}
addWithdraw(offer, offer.itemID, amount)
addWithdraw(o, 995, amount * o.offeredValue)
BuyingLimitation.updateBoughtAmount(offer.itemID, offer.playerUID, amount)
}
if (coinDifference > 0) {
if (offer.sell) {
addWithdraw(o, 995, coinDifference * amount)
} else {
addWithdraw(offer, 995, coinDifference * amount)
}
}
GrandExchangeDatabase.getDatabase()[o.itemID]?.influenceValue(o.offeredValue)
offer.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION)
o.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION)
o.player?.playerGrandExchange?.update(o)
offer.player?.playerGrandExchange?.update(offer)
dumpDatabase = true
}
/**
* Adds a new item to withdraw.
* @param itemId The item id.
* @param amount The amount to add.
* @param abort If the item is added due to abort.
*/
fun addWithdraw(offer: GrandExchangeOffer, itemId: Int, amount: Int, abort: Boolean = false) {
if (!abort) {
if (offer.sell) {
if (itemId == 995) {
offer.totalCoinExchange += amount
}
} else {
if (itemId == 995) {
offer.totalCoinExchange -= amount
} else {
offer.totalCoinExchange += offer.offeredValue * amount
}
}
}
for (i in offer.withdraw.indices) {
if (offer.withdraw[i] == null) {
offer.withdraw[i] = Item(itemId, amount)
break
}
if (offer.withdraw[i]!!.id == itemId) {
offer.withdraw[i]!!.amount = offer.withdraw[i]!!.amount + amount
break break
} }
} }
if (offer.player != null) { }
PacketRepository.send( buyFromBots(offer)
ContainerPacket::class.java, GE_OFFER_LOCK.unlock()
ContainerContext(offer.player, -1, -1757, 523 + offer.index, offer.withdraw, false) }
)
private fun getBuylimitAmount(offer: GrandExchangeOffer): Int {
var left = offer.amountLeft
if (!offer.sell && left > 0) {
val maximum = BuyingLimitation.getMaximumBuy(offer.itemID, offer.playerUID)
if (left >= maximum) {
left = maximum
offer.isLimitation = true
} }
} }
return left
}
/** /**
* Gets the next UID. * Exchanges between 2 offers.
* @return The UID. * @param offer The grand exchange offer to update.
*/ * @param o The other offer to exchange with.
private fun nextUID(): Long { */
val id = offsetUID++ private fun exchange(offer: GrandExchangeOffer, o: GrandExchangeOffer) {
return if (id == 0L) { if (o.sell == offer.sell) {
nextUID() return
} else id }
if (offer.sell && o.offeredValue < offer.offeredValue || !offer.sell && o.offeredValue > offer.offeredValue) {
return
}
var amount = min(getBuylimitAmount(offer), getBuylimitAmount(o))
if (amount < 1) {
return
}
var coinDifference = if (offer.sell) o.offeredValue - offer.offeredValue else offer.offeredValue - o.offeredValue
if (coinDifference < 0) {
return
}
if (EconomyManagement.getEcoState() == EcoStatus.DRAINING) {
coinDifference *= (1.0 - EconomyManagement.getModificationRate()).toInt()
}
offer.completedAmount = offer.completedAmount + amount
o.completedAmount = o.completedAmount + amount
offer.offerState = if (offer.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED
o.offerState = if (o.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED
if (offer.sell) {
if (offer.amountLeft < 1 && offer.player != null) {
offer.player!!.audioManager.send(Audio(4042, 1, 1))
}
addWithdraw(offer,995, amount * offer.offeredValue)
addWithdraw(o, o.itemID, amount)
BuyingLimitation.updateBoughtAmount(o.itemID, o.playerUID, amount)
} else {
if (o.amountLeft < 1 && o.player != null) {
o.player!!.audioManager.send(Audio(4042, 1, 1))
}
addWithdraw(offer, offer.itemID, amount)
addWithdraw(o, 995, amount * o.offeredValue)
BuyingLimitation.updateBoughtAmount(offer.itemID, offer.playerUID, amount)
}
if (coinDifference > 0) {
if (offer.sell) {
addWithdraw(o, 995, coinDifference * amount)
} else {
addWithdraw(offer, 995, coinDifference * amount)
}
}
GrandExchangeDatabase.getDatabase()[o.itemID]?.influenceValue(o.offeredValue)
offer.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION)
o.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION)
o.player?.playerGrandExchange?.update(o)
offer.player?.playerGrandExchange?.update(offer)
dumpDatabase = true
}
/**
* Adds a new item to withdraw.
* @param itemId The item id.
* @param amount The amount to add.
* @param abort If the item is added due to abort.
*/
fun addWithdraw(offer: GrandExchangeOffer, itemId: Int, amount: Int, abort: Boolean = false) {
if (!abort) {
if (offer.sell) {
if (itemId == 995) {
offer.totalCoinExchange += amount
}
} else {
if (itemId == 995) {
offer.totalCoinExchange -= amount
} else {
offer.totalCoinExchange += offer.offeredValue * amount
}
}
}
for (i in offer.withdraw.indices) {
if (offer.withdraw[i] == null) {
offer.withdraw[i] = Item(itemId, amount)
break
}
if (offer.withdraw[i]!!.id == itemId) {
offer.withdraw[i]!!.amount = offer.withdraw[i]!!.amount + amount
break
}
}
if (offer.player != null) {
PacketRepository.send(
ContainerPacket::class.java,
ContainerContext(offer.player, -1, -1757, 523 + offer.index, offer.withdraw, false)
)
} }
} }
/**
* Gets the next UID.
* @return The UID.
*/
private fun nextUID(): Long {
val id = offsetUID++
return if (id == 0L) {
nextUID()
} else id
}
private fun getItemDefPrice(itemID: Int): Int {
return max(ContentAPI.itemDefinition(itemID).getConfiguration(ItemConfigParser.GE_PRICE) ?: 0, ContentAPI.itemDefinition(itemID).value)
}
@JvmStatic
fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int {
val base = max(GrandExchangeDatabase.getDatabase()[itemID]?.value ?: 0, getItemDefPrice(itemID))
return if(from_bot) (base + (base * 0.1).toInt())
else base
}
} }

View file

@ -1,5 +1,6 @@
package rs09.game.ge package rs09.game.ge
import api.ContentAPI
import core.cache.def.impl.ItemDefinition import core.cache.def.impl.ItemDefinition
import core.game.component.CloseEvent import core.game.component.CloseEvent
import core.game.component.Component import core.game.component.Component
@ -27,8 +28,6 @@ import core.net.packet.out.GrandExchangePacket
import org.json.simple.JSONArray import org.json.simple.JSONArray
import org.json.simple.JSONObject import org.json.simple.JSONObject
import org.rs09.consts.Components import org.rs09.consts.Components
import rs09.game.ge.OfferManager.Companion.dispatch
import rs09.game.ge.OfferManager.Companion.updateOffer
import rs09.game.system.SystemLogger import rs09.game.system.SystemLogger
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.NumberFormat import java.text.NumberFormat
@ -319,13 +318,13 @@ class PlayerGrandExchange(private val player: Player) {
temporaryOffer!!.itemID = itemId temporaryOffer!!.itemID = itemId
temporaryOffer!!.sell = false temporaryOffer!!.sell = false
var itemDb = GrandExchangeDatabase.getDatabase()[itemId] var itemDb = GrandExchangeDatabase.getDatabase()[itemId]
if (itemDb == null) { if (itemDb == null || !ContentAPI.itemDefinition(itemId).isTradeable) {
player.packetDispatch.sendMessage("This item has been blacklisted from the Grand Exchange.") player.packetDispatch.sendMessage("This item has been blacklisted from the Grand Exchange.")
return return
} }
temporaryOffer!!.player = player temporaryOffer!!.player = player
temporaryOffer!!.amount = 1 temporaryOffer!!.amount = 1
temporaryOffer!!.offeredValue = itemDb.value temporaryOffer!!.offeredValue = OfferManager.getRecommendedPrice(itemId)
temporaryOffer!!.index = openedIndex temporaryOffer!!.index = openedIndex
sendConfiguration(temporaryOffer, false) sendConfiguration(temporaryOffer, false)
} }
@ -350,7 +349,7 @@ class PlayerGrandExchange(private val player: Player) {
id = item.noteChange id = item.noteChange
} }
var itemDb = GrandExchangeDatabase.getDatabase()[id] var itemDb = GrandExchangeDatabase.getDatabase()[id]
if (itemDb == null) { if (itemDb == null || !item.definition.isTradeable) {
player.packetDispatch.sendMessage("This item can't be sold on the Grand Exchange.") player.packetDispatch.sendMessage("This item can't be sold on the Grand Exchange.")
return return
} }
@ -358,7 +357,7 @@ class PlayerGrandExchange(private val player: Player) {
temporaryOffer!!.itemID = id temporaryOffer!!.itemID = id
temporaryOffer!!.sell = true temporaryOffer!!.sell = true
temporaryOffer!!.player = player temporaryOffer!!.player = player
temporaryOffer!!.offeredValue = itemDb.value temporaryOffer!!.offeredValue = OfferManager.getRecommendedPrice(id)
temporaryOffer!!.amount = item.amount temporaryOffer!!.amount = item.amount
temporaryOffer!!.index = openedIndex temporaryOffer!!.index = openedIndex
sendConfiguration(temporaryOffer, true) sendConfiguration(temporaryOffer, true)
@ -425,9 +424,9 @@ class PlayerGrandExchange(private val player: Player) {
return return
} }
} }
if (dispatch(player, temporaryOffer!!)) { if (OfferManager.dispatch(player, temporaryOffer!!)) {
offers[openedIndex] = temporaryOffer offers[openedIndex] = temporaryOffer
updateOffer(temporaryOffer!!) OfferManager.updateOffer(temporaryOffer!!)
} }
} else { } else {
val total: Int = temporaryOffer!!.amount * temporaryOffer!!.offeredValue val total: Int = temporaryOffer!!.amount * temporaryOffer!!.offeredValue
@ -436,9 +435,9 @@ class PlayerGrandExchange(private val player: Player) {
player.packetDispatch.sendMessage("You do not have enough coins to cover the offer.") player.packetDispatch.sendMessage("You do not have enough coins to cover the offer.")
return return
} }
if (dispatch(player, temporaryOffer!!) && player.inventory.remove(Item(995, total))) { if (OfferManager.dispatch(player, temporaryOffer!!) && player.inventory.remove(Item(995, total))) {
offers[openedIndex] = temporaryOffer offers[openedIndex] = temporaryOffer
updateOffer(temporaryOffer!!) OfferManager.updateOffer(temporaryOffer!!)
} }
} }
player.monitor.log( player.monitor.log(
@ -546,7 +545,7 @@ class PlayerGrandExchange(private val player: Player) {
val botSales = OfferManager.amtBotsSelling(offer.itemID) val botSales = OfferManager.amtBotsSelling(offer.itemID)
if (botSales > 0) { if (botSales > 0) {
foundAmounts.add(botSales) foundAmounts.add(botSales)
foundOffers.add(BotPrices.getPrice(offer.itemID)) foundOffers.add(OfferManager.getRecommendedPrice(offer.itemID, true))
count++ count++
} }
if (foundOffers.isNotEmpty()) { if (foundOffers.isNotEmpty()) {
@ -564,16 +563,17 @@ class PlayerGrandExchange(private val player: Player) {
player.packetDispatch.sendString(if (offer != null && !offer.sell) text.toString() else examine, 105, 142) player.packetDispatch.sendString(if (offer != null && !offer.sell) text.toString() else examine, 105, 142)
var lowPrice = 0 var lowPrice = 0
var highPrice = 0 var highPrice = 0
val recommendedPrice = OfferManager.getRecommendedPrice(entry?.itemId ?: 0)
if (entry != null) { if (entry != null) {
lowPrice = (entry.value * 0.95).toInt() lowPrice = (recommendedPrice * 0.95).toInt()
highPrice = (entry.value * 1.05).toInt() highPrice = (recommendedPrice * 1.05).toInt()
} }
player.varpManager.get(1109).setVarbit(0, offer?.itemID ?: -1).send(player) player.varpManager.get(1109).setVarbit(0, offer?.itemID ?: -1).send(player)
player.varpManager.get(1110).setVarbit(0, offer?.amount ?: 0).send(player) player.varpManager.get(1110).setVarbit(0, offer?.amount ?: 0).send(player)
player.varpManager.get(1111).setVarbit(0, offer?.offeredValue ?: 0).send(player) player.varpManager.get(1111).setVarbit(0, offer?.offeredValue ?: 0).send(player)
player.varpManager.get(1112).setVarbit(0, openedIndex).send(player) player.varpManager.get(1112).setVarbit(0, openedIndex).send(player)
player.varpManager.get(1113).setVarbit(0, if (sell) 1 else 0).send(player) player.varpManager.get(1113).setVarbit(0, if (sell) 1 else 0).send(player)
player.varpManager.get(1114).setVarbit(0, entry?.value ?: 0).send(player) player.varpManager.get(1114).setVarbit(0, recommendedPrice).send(player)
player.varpManager.get(1115).setVarbit(0, lowPrice).send(player) player.varpManager.get(1115).setVarbit(0, lowPrice).send(player)
player.varpManager.get(1116).setVarbit(0, highPrice).send(player) player.varpManager.get(1116).setVarbit(0, highPrice).send(player)
if (offer != null) { if (offer != null) {

View file

@ -69,6 +69,11 @@ object SystemLogger {
if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[RAND] $message")}") if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[RAND] $message")}")
} }
@JvmStatic
fun logGE(message: String){
if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[ GE] $message")}")
}
@JvmStatic @JvmStatic
fun logTrade(message: String){ fun logTrade(message: String){
if(message.isNotBlank()){ if(message.isNotBlank()){

View file

@ -3,6 +3,7 @@ package rs09.game.world.callback
import core.game.node.entity.skill.hunter.ImpetuousImpulses import core.game.node.entity.skill.hunter.ImpetuousImpulses
import core.game.world.callback.CallBack import core.game.world.callback.CallBack
import core.game.world.map.zone.ZoneBuilder import core.game.world.map.zone.ZoneBuilder
import rs09.game.ge.GrandExchange
import rs09.game.ge.OfferManager import rs09.game.ge.OfferManager
import rs09.game.system.SystemLogger import rs09.game.system.SystemLogger
import java.util.* import java.util.*
@ -16,7 +17,7 @@ object CallbackHub {
fun call(): Boolean { fun call(): Boolean {
calls.add(ZoneBuilder()) calls.add(ZoneBuilder())
calls.add(OfferManager()) calls.add(GrandExchange)
calls.add(ImpetuousImpulses()) calls.add(ImpetuousImpulses())
for (call in calls) { for (call in calls) {
if (!call.call()) { if (!call.call()) {