inventory.lua
1 --[[ 2 ToasterGen Spin 3 4 Copyright (C) 2025 Clifton Toaster Reid <cliftontreid@duck.com> 5 6 This library is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 ]] 11 12 ---@class IvMnager 13 ---@field addItemToPlayer fun(direction: "top"|"bottom"|"left"|"right"|"front"|"back", item: table): number 14 ---@field removeItemFromPlayer fun(direction: "top"|"bottom"|"left"|"right"|"front"|"back", item: table): number 15 ---@field getArmor fun(): table 16 ---@field getOwner fun(): string 17 ---@field isPlayerEquipped fun(): boolean 18 ---@field isWearing fun(slot: number): boolean 19 ---@field getItemInHand fun(): table 20 ---@field getItemInOffHand fun(): table 21 ---@field getFreeSlot fun(): number 22 ---@field isSpaceAvailable fun(): boolean 23 ---@field getEmptySpace fun(): number 24 25 local money = "numismatics:sprocket" 26 local invside = "bottom" 27 28 local Logger = require("src.log") 29 local Tracer = require("src.trace") 30 31 ---@type table<number, IvMnager> 32 local invManagers = {} 33 34 local inv = {} 35 36 ---Gets the amount of money (emeralds) a player has in their off-hand 37 ---@param idx number The player index in invManagers 38 ---@param parentId string|nil The parent trace ID for logging 39 ---@return number|nil The count of money items, or nil if player not found, no item in off-hand, or item is not money 40 local function getMoneyInPlayer(idx, parentId) 41 local tr = Tracer.new() 42 tr:setName("inventory.getMoneyInPlayer") 43 tr:addTag("idx", string.format("%d", idx)) 44 if parentId then 45 tr:setParentId(parentId) 46 end 47 48 local player = invManagers[idx + 1] 49 if not player then 50 Logger.debug("Player %d not found", idx) 51 return nil 52 end 53 54 local success, item = pcall(function() 55 local ptr = Tracer.new() 56 ptr:setName("getItemInOffHand") 57 ptr:addTag("idx", string.format("%d", idx)) 58 ptr:setParentId(tr.traceId) 59 60 local hand = player:getItemInOffHand() 61 62 ptr:addAnnotation(hand.name or "nil") 63 Tracer.addSpan(ptr:endSpan()) 64 return hand 65 end) 66 67 ---@type number | nil 68 local amount = item.count or 0 69 70 if not success or not item then 71 Logger.debug("No item in off-hand for player %d", idx) 72 tr:addAnnotation("No item in off-hand") 73 amount = nil 74 end 75 if item.name ~= money then 76 Logger.debug("Item in off-hand is not money: %s", item.name) 77 tr:addAnnotation("Item in off-hand is not money") 78 amount = nil 79 end 80 Tracer.addSpan(tr:endSpan()) 81 return amount 82 end 83 84 ---Removes a specified amount of money from a player 85 ---@param idx number The player index in invManagers 86 ---@param amount number The amount of money to take from the player 87 ---@param parentId string|nil The parent trace ID for logging 88 ---@return number|nil The amount taken if successful, nil if player not found or has insufficient funds 89 local function takeMoneyFromPlayer(idx, amount, parentId) 90 local tr = Tracer.new() 91 tr:setName("inventory.takeMoneyFromPlayer") 92 tr:addTag("idx", string.format("%d", idx)) 93 tr:addTag("amount", string.format("%d", amount)) 94 if parentId then 95 tr:setParentId(parentId) 96 end 97 if amount <= 0 then 98 Logger.debug("Amount must be greater than 0") 99 tr:addAnnotation("Amount must be greater than 0") 100 Tracer.addSpan(tr:endSpan()) 101 return nil 102 end 103 104 local player = invManagers[idx + 1] 105 if not player then 106 tr:addAnnotation("Player not found") 107 Logger.debug("Player %d not found", idx) 108 Tracer.addSpan(tr:endSpan()) 109 return nil 110 end 111 112 local count = getMoneyInPlayer(idx, tr.traceId) -- Pass the traceId here 113 if not count or count < amount then 114 tr:addAnnotation("Not enough money") 115 Logger.debug("Not enough money in player %d: %d < %d", idx, count or 0, amount) 116 Tracer.addSpan(tr:endSpan()) 117 return nil 118 end 119 120 local success, result = pcall(function() 121 local ptr = Tracer.new() 122 ptr:setName("removeItemFromPlayer") 123 ptr:addTag("idx", string.format("%d", idx)) 124 ptr:addTag("amount", string.format("%d", amount)) 125 ptr:setParentId(tr.traceId) 126 127 local removedCount = player.removeItemFromPlayer(invside, { 128 name = money, 129 count = amount, 130 }) 131 132 ptr:addAnnotation(string.format("Removed %d", removedCount or -1)) 133 Tracer.addSpan(ptr:endSpan()) 134 return removedCount 135 end) 136 137 ---@type number | nil 138 local retres = amount 139 140 if not success then 141 Logger.debug("Failed to take money from player %d", idx) 142 tr:addAnnotation("Failed to take money") 143 retres = nil 144 end 145 if result ~= amount then 146 Logger.debug("Failed to take %d money from player %s, took %d", amount, inv.getPlayer(idx), result) 147 tr:addAnnotation(string.format("Failed to take %d money", amount)) 148 retres = nil 149 end 150 Logger.debug("Successfully took %d money from player %s", amount, inv.getPlayer(idx)) 151 return retres 152 end 153 154 ---Adds a specified amount of money to a player 155 ---@param idx number The player index in invManagers 156 ---@param amount number The amount of money to add to the player 157 ---@param parentId string|nil The parent trace ID for logging 158 ---@return number|nil The amount added if successful, nil if player not found or adding fails 159 local function addMoneyToPlayer(idx, amount, parentId) 160 local tr = Tracer.new() 161 tr:setName("inventory.addMoneyToPlayer") 162 tr:addTag("idx", string.format("%d", idx)) 163 tr:addTag("amount", string.format("%d", amount)) 164 if parentId then 165 tr:setParentId(parentId) 166 end 167 if amount <= 0 then 168 Logger.debug("Amount must be greater than 0") 169 tr:addAnnotation("Amount must be greater than 0") 170 Tracer.addSpan(tr:endSpan()) 171 return nil 172 end 173 174 local player = invManagers[idx + 1] 175 if not player then 176 Logger.debug("Player %d not found", idx) 177 tr:addAnnotation("Player not found") 178 Tracer.addSpan(tr:endSpan()) 179 return nil 180 end 181 182 local success, res = pcall(function() 183 local ptr = Tracer.new() 184 ptr:setName("addItemToPlayer") 185 ptr:addTag("idx", string.format("%d", idx)) 186 ptr:addTag("amount", string.format("%d", amount)) 187 ptr:setParentId(tr.traceId) 188 189 local gave = player.addItemToPlayer(invside, { 190 name = money, 191 count = amount, 192 }) 193 194 ptr:addAnnotation(string.format("Added %d", gave or -1)) 195 Tracer.addSpan(ptr:endSpan()) 196 197 return gave 198 end) 199 200 ---@type number | nil 201 local retres = amount -- Initialize with the intended return value 202 203 if not success then 204 Logger.debug("Failed to add money to player %d", idx) 205 tr:addAnnotation("Failed to add money") 206 retres = nil 207 elseif res ~= amount then 208 Logger.debug("Added %d money to player %d, but expected %d", res or -1, idx, amount) 209 tr:addAnnotation(string.format("Failed to add %d money, added %d", amount, res or -1)) 210 -- take back the money given 211 local takeBackSuccess = pcall(function() 212 local ptr = Tracer.new() 213 ptr:setName("removeItemFromPlayer (rollback)") 214 ptr:addTag("idx", string.format("%d", idx)) 215 ptr:addTag("amount", string.format("%d", res or -1)) 216 ptr:setParentId(tr.traceId) 217 218 player.removeItemFromPlayer(invside, { 219 name = money, 220 count = res, 221 }) 222 223 ptr:addAnnotation("Rollback attempted") 224 Tracer.addSpan(ptr:endSpan()) 225 end) 226 if not takeBackSuccess then 227 Logger.debug("Failed to take back money from player %d", idx) 228 tr:addAnnotation("Rollback failed") 229 end 230 -- Even if takeBackSuccess fails, we still return nil 231 retres = nil 232 else 233 Logger.debug("Added %d money to player %d", amount, idx) 234 tr:addAnnotation(string.format("Successfully added %d money", amount)) 235 end 236 237 Tracer.addSpan(tr:endSpan()) 238 return retres 239 end 240 241 ---Gets a player's ownership information 242 ---@param idx number The player index in invManagers 243 ---@param parentId string|nil The parent trace ID for logging 244 ---@return string|nil The player owner identifier, or nil if player not found 245 local function getPlayer(idx, parentId) 246 local tr = Tracer.new() 247 tr:setName("inventory.getPlayer") 248 tr:addTag("idx", string.format("%d", idx)) 249 if parentId then 250 tr:setParentId(parentId) 251 end 252 253 local player = invManagers[idx + 1] 254 if not player then 255 Logger.debug("Player %d not found", idx) 256 tr:addAnnotation("Player not found") 257 Tracer.addSpan(tr:endSpan()) 258 return nil 259 end 260 261 local success, own = pcall(function() 262 local ptr = Tracer.new() 263 ptr:setName("getOwner") 264 ptr:addTag("idx", string.format("%d", idx)) 265 ptr:setParentId(tr.traceId) 266 267 local owner = player.getOwner() 268 269 ptr:addAnnotation(owner or "nil") 270 Tracer.addSpan(ptr:endSpan()) 271 return owner 272 end) 273 274 if not success or not own then 275 Logger.debug("Failed to get owner for player %d", idx) 276 tr:addAnnotation("Failed to get owner") 277 Tracer.addSpan(tr:endSpan()) 278 return nil 279 end 280 281 tr:addAnnotation(string.format("Owner: %s", own)) 282 Tracer.addSpan(tr:endSpan()) 283 return own 284 end 285 286 ---Finds the first iv manager that is owned by player 287 ---@param player string The player identifier 288 ---@param parentId string|nil The parent trace ID for logging 289 ---@return number|nil The index of the iv manager, or nil if not found 290 local function findPlayer(player, parentId) 291 local tr = Tracer.new() 292 tr:setName("inventory.findPlayer") 293 tr:addTag("player", player) 294 if parentId then 295 tr:setParentId(parentId) 296 end 297 298 for idx, iv in pairs(invManagers) do 299 local success, owner = pcall(function() 300 local ptr = Tracer.new() 301 ptr:setName("getOwner") 302 ptr:addTag("idx", string.format("%d", idx)) 303 ptr:setParentId(tr.traceId) 304 305 local own = iv.getOwner() 306 307 ptr:addAnnotation(own or "nil") 308 Tracer.addSpan(ptr:endSpan()) 309 return own 310 end) 311 if success and owner == player then 312 Logger.debug("Found player %s at index %d", player, idx) 313 tr:addAnnotation(string.format("Player found at index %d", idx)) 314 Tracer.addSpan(tr:endSpan()) 315 return idx 316 elseif not success then 317 Logger.debug("Failed to get owner for index %d", idx) 318 tr:addAnnotation(string.format("Failed getOwner for index %d", idx)) 319 end 320 end 321 322 Logger.debug("Player %s not found", player) 323 tr:addAnnotation("Player not found") 324 Tracer.addSpan(tr:endSpan()) 325 return nil 326 end 327 328 local function init(config) 329 if config == nil then 330 error("Config is nil") 331 return 332 end 333 334 -- assign the table<number, IvMnager> to invManagers using the config's 335 -- table<number, string> as the key and the peripheral.wrap function to turn the 336 -- string into a IvMnager 337 338 for idx, name in pairs(config) do 339 local player = peripheral.wrap(name) 340 if player then 341 invManagers[idx] = player 342 else 343 error("Peripheral " .. name .. " not found") 344 end 345 end 346 end 347 348 inv.getMoneyInPlayer = getMoneyInPlayer 349 inv.takeMoneyFromPlayer = takeMoneyFromPlayer 350 inv.addMoneyToPlayer = addMoneyToPlayer 351 inv.getPlayer = getPlayer 352 inv.findPlayer = findPlayer 353 inv.init = init 354 355 return inv