diff --git a/config.lua b/config.lua index 1409a86..90970ec 100644 --- a/config.lua +++ b/config.lua @@ -125,6 +125,12 @@ -- NOTE: Chest dropping is NYI in plethora 1.19, so do not use unless -- the output chest can be accessed }, + hooks = { + start = nil, -- function(version, config, products) + purchase = nil, -- function(product, amount, refundAmount, transaction, transactionCurrency) + failedPurchase = nil, -- function(transaction, transactionCurrency, product, errorMessage) + programError = nil -- function(err) + }, exchange = { -- Not yet implemented enabled = true, diff --git a/core/ConfigValidator.lua b/core/ConfigValidator.lua index 3619b2c..f7915e4 100644 --- a/core/ConfigValidator.lua +++ b/core/ConfigValidator.lua @@ -108,6 +108,12 @@ exchangeChest = "chest?", outputChest = "chest", }, + hooks = { + start = "function?", + purchase = "function?", + failedPurchase = "function?", + programError = "function?", + }, shopSync = { enabled = "boolean?", name = "string?", @@ -163,6 +169,9 @@ if entryType == "number" and type(value) ~= "number" then error("Config value " .. subpath .. " must be a number") end + if entryType == "function" and type(value) ~= "function" then + error("Config value " .. subpath .. " must be a function") + end if entryType == "color" then if type(value) ~= "number" then error("Config value " .. subpath .. " must be a color") diff --git a/core/ShopState.lua b/core/ShopState.lua index 91b4dd3..2713df0 100644 --- a/core/ShopState.lua +++ b/core/ShopState.lua @@ -2,6 +2,7 @@ local ScanInventory = require("core.inventory.ScanInventory") local Pricing = require("core.Pricing") local sound = require("util.sound") +local eventHook = require("util.eventHook") local shopSyncFrequency = 30 local shopSyncChannel = 9773 @@ -136,17 +137,32 @@ if state.config.settings.playSounds then sound.playSound(state.speaker, state.config.sounds.purchase) end + if state.config.hooks and state.config.hooks.purchase then + eventHook.execute(state.config.hooks.purchase, purchasedProduct, available, refundAmount, transaction, transactionCurrency) + end else refund(transactionCurrency, transaction.from, meta, transaction.value, state.config.lang.refundOutOfStock) + if state.config.hooks and state.config.hooks.failedPurchase then + eventHook.execute(state.config.hooks.failedPurchase, transaction, transactionCurrency, purchasedProduct, state.config.lang.refundOutOfStock) + end end else refund(transactionCurrency, transaction.from, meta, transaction.value, state.config.lang.refundOutOfStock) + if state.config.hooks and state.config.hooks.failedPurchase then + eventHook.execute(state.config.hooks.failedPurchase, transaction, transactionCurrency, purchasedProduct, state.config.lang.refundOutOfStock) + end end else refund(transactionCurrency, transaction.from, meta, transaction.value, state.config.lang.refundAtLeastOne, true) + if state.config.hooks and state.config.hooks.failedPurchase then + eventHook.execute(state.config.hooks.failedPurchase, transaction, transactionCurrency, purchasedProduct, state.config.lang.refundAtLeastOne) + end end else refund(transactionCurrency, transaction.from, meta, transaction.value, state.config.lang.refundInvalidProduct, true) + if state.config.hooks and state.config.hooks.failedPurchase then + eventHook.execute(state.config.hooks.failedPurchase, transaction, transactionCurrency, nil, state.config.lang.refundInvalidProduct) + end end end @@ -194,7 +210,7 @@ if sentName and transactionCurrency.name and transactionCurrency.name:find(".") then sentName = sentName .. "." .. nameSuffix end - if transaction.from ~= transactionCurrency.host and (not transactionCurrency.name and not sentName) or (sentName and sentName:lower() == transactionCurrency.name:lower()) then + if transaction.from ~= transactionCurrency.host and (not transactionCurrency.name and not sentName) or (transactionCurrency.name and sentName and sentName:lower() == transactionCurrency.name:lower()) then local meta = parseMeta(transaction.metadata) if transaction.to == transactionCurrency.host and not transactionCurrency.name and not sentMetaname then sentMetaname = meta[1] @@ -205,10 +221,16 @@ -- Success :D else refund(transactionCurrency, transaction.from, meta, transaction.value, state.config.lang.refundError, true) + if state.config.hooks and state.config.hooks.failedPurchase then + eventHook.execute(state.config.hooks.failedPurchase, transaction, transactionCurrency, nil, state.config.lang.refundError) + end error(err) end else refund(transactionCurrency, transaction.from, meta, transaction.value, state.config.lang.refundNoProduct, true) + if state.config.hooks and state.config.hooks.failedPurchase then + eventHook.execute(state.config.hooks.failedPurchase, transaction, transactionCurrency, nil, state.config.lang.refundNoProduct) + end end end end diff --git a/radon.lua b/radon.lua index a686b6b..8e49bba 100644 --- a/radon.lua +++ b/radon.lua @@ -1,11 +1,12 @@ local oldPullEvent = os.pullEvent os.pullEvent = os.pullEventRaw -local version = "1.1.8" +local version = "1.1.9" --- Imports local _ = require("util.score") local sound = require("util.sound") +local eventHook = require("util.eventHook") local Display = require("modules.display") @@ -636,6 +637,9 @@ local success, err = pcall(function() ShopRunner.launchShop(shopState, function() -- Profiler:activate() print("Radon " .. version .. " has started") + if config.hooks and config.hooks.start then + eventHook.execute(config.hooks.start, version, config, products) + end while true do tree = Solyd.render(tree, Main {t = t, config = config, shopState = shopState}) @@ -688,6 +692,9 @@ os.pullEvent = oldPullEvent if not success then + if config.hooks and config.hooks.programError then + eventHook.execute(config.hooks.programError, err) + end error(err) end print("Radon terminated, goodbye!") diff --git a/util/eventHook.lua b/util/eventHook.lua new file mode 100644 index 0000000..8734536 --- /dev/null +++ b/util/eventHook.lua @@ -0,0 +1,14 @@ +local function executeHook(func, ...) + local args = {...} + local ret = {pcall(func, unpack(args))} + if not ret[1] then + print("Error in hook: " .. ret[2]) + return + end + table.remove(ret, 1) + return unpack(ret) +end + +return { + execute = executeHook +} \ No newline at end of file