Minecraft - Steem/Hive/Blurt - BetasteemCreated with Sketch.

in #minecraft5 hours ago

Been working on making MineCraft and Hive, or mashing them together at least.

It's a new upgraded version, trying to iron out the bugs. Also trying to figure out how to code in a stationary NPC that I don't have to cage in.

Taking all help if its given.

main.lua



-- main.lua

local g_Plugin = nil
local g_Config =
{
    HiveEngineRpc = "https://api.hive-engine.com/rpc/contracts",
    BridgeUrl = "http://127.0.0.1:8787",
    BridgeAuthToken = "",
    PaymentAccount = "",
    BankEnabled = false,
    BankX = 0,
    BankY = 0,
    BankZ = 0,
    Debug = false,
    BankName = "PeakeCoin Bank",
    BankerName = "Banker",
    DefaultSymbol = "PEAK",
    BankSignsEnabled = true,
    BankSignSymbols = "PEAK",
    BankSignUpdateSeconds = 30,
}
local g_Accounts = {}
local g_Bridges = {}
local g_Ledger = {}
local g_Bank =
{
    WorldName = "",
    AtmX = 0,
    AtmY = 0,
    AtmZ = 0,
    AtmBaseY = 0,
    BankerId = nil,
    Placed = false,
    SignsPlaced = false,
    LastPriceUpdate = 0,
    Signs = {},
}
local g_TickCounter = 0

local CONFIG_FILE = nil

local function SetBlockAt(a_World, a_X, a_Y, a_Z, a_BlockType, a_BlockMeta)
    local meta = a_BlockMeta or 0
    local ok = pcall(function()
        if (Vector3i ~= nil) then
            a_World:SetBlock(Vector3i(a_X, a_Y, a_Z), a_BlockType, meta)
        else
            error("Vector3i not available")
        end
    end)
    if not ok then
        a_World:SetBlock(a_X, a_Y, a_Z, a_BlockType, meta)
    end
end

local function ResolveConfigPath()
    if (CONFIG_FILE and CONFIG_FILE ~= "") then
        return CONFIG_FILE
    end
    local plugin = cPluginManager:Get():GetCurrentPlugin()
    if (plugin) then
        CONFIG_FILE = plugin:GetLocalFolder() .. "/HiveEngineTrade.ini"
    else
        CONFIG_FILE = "Plugins/HiveEngineTrade/HiveEngineTrade.ini"
    end
    return CONFIG_FILE
end

local function SendPlayerMessage(a_Player, a_Message)
    if (a_Player and a_Player.SendMessage) then
        a_Player:SendMessage(a_Message)
    else
        LOG(a_Message)
    end
end

local function LoadConfig()
    local configPath = ResolveConfigPath()
    local ini = cIniFile()
    if (not ini:ReadFile(configPath)) then
        LOGINFO("HiveEngineTrade: creating default config at " .. configPath)
    end

    g_Config.HiveEngineRpc = ini:GetValueSet("HiveEngineTrade", "HiveEngineRpc", g_Config.HiveEngineRpc)
    g_Config.BridgeUrl = ini:GetValueSet("HiveEngineTrade", "BridgeUrl", g_Config.BridgeUrl)
    g_Config.BridgeAuthToken = ini:GetValueSet("HiveEngineTrade", "BridgeAuthToken", g_Config.BridgeAuthToken)
    g_Config.PaymentAccount = ini:GetValueSet("HiveEngineTrade", "PaymentAccount", g_Config.PaymentAccount)
    g_Config.BankEnabled = ini:GetValueSetB("HiveEngineTrade", "BankEnabled", g_Config.BankEnabled)
    g_Config.BankX = ini:GetValueSetI("HiveEngineTrade", "BankX", g_Config.BankX)
    g_Config.BankY = ini:GetValueSetI("HiveEngineTrade", "BankY", g_Config.BankY)
    g_Config.BankZ = ini:GetValueSetI("HiveEngineTrade", "BankZ", g_Config.BankZ)
    g_Config.Debug = ini:GetValueSetB("HiveEngineTrade", "Debug", g_Config.Debug)
    g_Config.BankName = ini:GetValueSet("HiveEngineTrade", "BankName", g_Config.BankName)
    g_Config.BankerName = ini:GetValueSet("HiveEngineTrade", "BankerName", g_Config.BankerName)
    g_Config.DefaultSymbol = ini:GetValueSet("HiveEngineTrade", "DefaultSymbol", g_Config.DefaultSymbol)
    g_Config.BankSignsEnabled = ini:GetValueSetB("HiveEngineTrade", "BankSignsEnabled", g_Config.BankSignsEnabled)
    g_Config.BankSignSymbols = ini:GetValueSet("HiveEngineTrade", "BankSignSymbols", g_Config.BankSignSymbols)
    g_Config.BankSignUpdateSeconds = ini:GetValueSetI("HiveEngineTrade", "BankSignUpdateSeconds", g_Config.BankSignUpdateSeconds)

    g_Accounts = {}
    local keyId = ini:FindKey("Accounts")
    if (keyId ~= cIniFile.noID) then
        local numValues = ini:GetNumValues(keyId)
        for idx = 0, numValues - 1 do
            local valueName = ini:GetValueName(keyId, idx)
            if (valueName ~= "__placeholder") then
                local value = ini:GetValue("Accounts", valueName)
                if (value ~= "") then
                    g_Accounts[valueName] = value
                end
            end
        end
    end

    g_Ledger = {}
    local ledgerKeyId = ini:FindKey("Ledger")
    if (ledgerKeyId ~= cIniFile.noID) then
        local numValues = ini:GetNumValues(ledgerKeyId)
        for idx = 0, numValues - 1 do
            local valueName = ini:GetValueName(ledgerKeyId, idx)
            if (valueName ~= "__placeholder") then
                local value = ini:GetValue("Ledger", valueName)
                local playerName, symbol = valueName:match("^([^:]+):(.+)$")
                local amount = tonumber(value or "0") or 0
                if (playerName and symbol and amount ~= 0) then
                    g_Ledger[playerName] = g_Ledger[playerName] or {}
                    g_Ledger[playerName][symbol] = amount
                end
            end
        end
    end

    ini:WriteFile(configPath)

    g_Bridges = {}
    local bridgeKeyId = ini:FindKey("Bridges")
    if (bridgeKeyId ~= cIniFile.noID) then
        local numValues = ini:GetNumValues(bridgeKeyId)
        for idx = 0, numValues - 1 do
            local valueName = ini:GetValueName(bridgeKeyId, idx)
            if (valueName ~= "__placeholder") then
                local value = ini:GetValue("Bridges", valueName)
                if (value ~= "") then
                    g_Bridges[valueName] = value
                end
            end
        end
    end
end

local function SaveAccount(a_PlayerName, a_HiveAccount)
    local ini = cIniFile()
    ini:ReadFile(ResolveConfigPath())
    ini:SetValue("Accounts", a_PlayerName, a_HiveAccount)
    ini:WriteFile(ResolveConfigPath())
    g_Accounts[a_PlayerName] = a_HiveAccount
end

local function SaveBridge(a_PlayerName, a_BridgeUrl)
    local ini = cIniFile()
    ini:ReadFile(ResolveConfigPath())
    if (a_BridgeUrl == nil or a_BridgeUrl == "") then
        ini:DeleteValue("Bridges", a_PlayerName)
        g_Bridges[a_PlayerName] = nil
    else
        ini:SetValue("Bridges", a_PlayerName, a_BridgeUrl)
        g_Bridges[a_PlayerName] = a_BridgeUrl
    end
    ini:WriteFile(ResolveConfigPath())
end

local function GetLinkedAccount(a_PlayerName)
    return g_Accounts[a_PlayerName]
end

local function GetBridgeUrlForPlayer(a_Player)
    if (not a_Player) then
        return g_Config.BridgeUrl
    end
    local name = a_Player:GetName()
    return g_Bridges[name] or g_Config.BridgeUrl
end

local function HasAdminPermission(a_Player)
    if (not a_Player) then
        return true
    end
    if (a_Player.HasPermission) then
        return a_Player:HasPermission("hive.admin")
    end
    return false
end

local function GetLedgerBalance(a_PlayerName, a_Symbol)
    local perPlayer = g_Ledger[a_PlayerName]
    if (not perPlayer) then
        return 0
    end
    return perPlayer[a_Symbol] or 0
end

local function SetLedgerBalance(a_PlayerName, a_Symbol, a_Amount)
    local amount = tonumber(a_Amount) or 0
    if (amount == 0) then
        if (g_Ledger[a_PlayerName]) then
            g_Ledger[a_PlayerName][a_Symbol] = nil
        end
    else
        g_Ledger[a_PlayerName] = g_Ledger[a_PlayerName] or {}
        g_Ledger[a_PlayerName][a_Symbol] = amount
    end

    local ini = cIniFile()
    ini:ReadFile(ResolveConfigPath())
    local key = a_PlayerName .. ":" .. a_Symbol
    if (amount == 0) then
        ini:DeleteValue("Ledger", key)
    else
        ini:SetValue("Ledger", key, tostring(amount))
    end
    ini:WriteFile(ResolveConfigPath())

    return amount
end

local function AddLedgerBalance(a_PlayerName, a_Symbol, a_Delta)
    local delta = tonumber(a_Delta)
    if (not delta) then
        return nil, "Invalid amount"
    end
    local current = GetLedgerBalance(a_PlayerName, a_Symbol)
    local nextAmount = current + delta
    if (nextAmount < 0) then
        return nil, "Insufficient balance"
    end
    return SetLedgerBalance(a_PlayerName, a_Symbol, nextAmount)
end

local function HttpPostJson(a_Url, a_Table, a_Callback, a_Headers)
    local body, err = cJson:Serialize(a_Table)
    if (not body) then
        a_Callback(false, "JSON serialize failed: " .. (err or "<unknown>"))
        return
    end

    local headers = a_Headers or {}
    headers["Content-Type"] = "application/json"

    local ok, msg = cUrlClient:Post(a_Url,
        function(a_Body, a_Data)
            if (not a_Body) then
                a_Callback(false, a_Data or "<no error>")
                return
            end
            a_Callback(true, a_Body, a_Data)
        end,
        headers,
        body
    )

    if (not ok) then
        a_Callback(false, msg or "<no error>")
    end
end

local function HeRpc(a_Method, a_Params, a_Callback)
    HttpPostJson(
        g_Config.HiveEngineRpc,
        { jsonrpc = "2.0", id = 1, method = a_Method, params = a_Params },
        a_Callback
    )
end

local function RequireLinkedAccount(a_Player)
    local name = a_Player:GetName()
    local account = GetLinkedAccount(name)
    if (not account or account == "") then
        SendPlayerMessage(a_Player, "No Hive account linked. Use /hive link <account>")
        return nil
    end
    return account
end

function Initialize(a_Plugin)
    g_Plugin = a_Plugin
    g_Plugin:SetName("HiveEngineTrade")
    g_Plugin:SetVersion(1)

    LoadConfig()
    LOG("HiveEngineTrade: ConfigPath=" .. ResolveConfigPath())
    LOG("HiveEngineTrade: Config BankEnabled=" .. tostring(g_Config.BankEnabled) ..
        " BankXYZ=" .. g_Config.BankX .. "," .. g_Config.BankY .. "," .. g_Config.BankZ ..
        " Debug=" .. tostring(g_Config.Debug))

    cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity)
    cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_BREAKING_BLOCK, OnPlayerBreakingBlock)
    cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick)

    if (g_Config.BankEnabled) then
        SetupBank()
    end

    LOG("Initialized " .. g_Plugin:GetName() .. " v." .. g_Plugin:GetVersion())
    return true
end

local function IsBankerEntity(a_Entity)
    if (not a_Entity) then
        return false
    end
    if (g_Bank.BankerId and a_Entity:GetUniqueID() == g_Bank.BankerId) then
        return true
    end
    if (a_Entity:GetClass() ~= "cVillager") then
        return false
    end
    if (g_Bank.AtmX == 0 and g_Bank.AtmZ == 0) then
        return false
    end
    local pos = a_Entity:GetPosition()
    local dx = math.abs(pos.x - g_Bank.AtmX)
    local dy = math.abs(pos.y - (g_Bank.AtmY or pos.y))
    local dz = math.abs(pos.z - g_Bank.AtmZ)
    return (dx <= 3) and (dy <= 3) and (dz <= 3)
end

local function EnsureBankerAt(a_World, a_X, a_Y, a_Z)
    local found = nil
    local searchBox = cBoundingBox(Vector3d(a_X, a_Y, a_Z), 4, 2)
    a_World:ForEachEntityInBox(searchBox, function (a_Entity)
        if (IsBankerEntity(a_Entity)) then
            found = a_Entity
            return true
        end
        return false
    end)

    if (found) then
        g_Bank.BankerId = found:GetUniqueID()
        return
    end

    local entityId = a_World:SpawnMob(a_X + 0.5, a_Y, a_Z + 0.5, mtVillager)
    if (entityId == cEntity.INVALID_ID) then
        LOGWARNING("HiveEngineTrade: failed to spawn banker villager")
        return
    end

    a_World:DoWithEntityByID(entityId, function (a_Entity)
        g_Bank.BankerId = a_Entity:GetUniqueID()
    end)
end

local function ParseSymbolList(a_List)
    local symbols = {}
    for token in string.gmatch(a_List or "", "[^,%s]+") do
        table.insert(symbols, string.upper(token))
    end
    if (#symbols == 0 and g_Config.DefaultSymbol ~= "") then
        table.insert(symbols, string.upper(g_Config.DefaultSymbol))
    end
    return symbols
end

local function EnsureBankSigns(a_World)
    if (g_Bank.SignsPlaced or (not g_Config.BankSignsEnabled)) then
        return
    end
    local baseX = g_Bank.AtmX
    local baseY = g_Bank.AtmY
    local baseZ = g_Bank.AtmZ
    local signBlock = 68 -- wall sign
    local signMeta = 2 -- facing north (toward interior)
    local symbols = ParseSymbolList(g_Config.BankSignSymbols)
    local signY = baseY + 2
    local signZ = baseZ + 2
    local positions = {
        { baseX - 2, signY, signZ },
        { baseX - 1, signY, signZ },
        { baseX, signY, signZ },
        { baseX + 1, signY, signZ },
        { baseX + 2, signY, signZ },
    }

    g_Bank.Signs = {}
    for idx, symbol in ipairs(symbols) do
        local pos = positions[idx]
        if (pos) then
            local sx, sy, sz = pos[1], pos[2], pos[3]
            SetBlockAt(a_World, sx, sy, sz, 0, 0)
            SetBlockAt(a_World, sx, sy, sz, signBlock, signMeta)
            a_World:SetSignLines(sx, sy, sz, "PRICE", symbol, "loading", "")
            table.insert(g_Bank.Signs, { Symbol = symbol, X = sx, Y = sy, Z = sz })
        end
    end

    g_Bank.SignsPlaced = true
    if (g_Config.Debug) then
        LOG("HiveEngineTrade: Bank signs placed count=" .. tostring(#g_Bank.Signs) .. " z=" .. tostring(signZ))
    end
end

local function UpdateBankSigns()
    if (not g_Config.BankSignsEnabled) then
        return
    end
    if (g_Bank.WorldName == "" or (not g_Bank.SignsPlaced)) then
        return
    end
    local now = os.time()
    if ((now - g_Bank.LastPriceUpdate) < g_Config.BankSignUpdateSeconds) then
        return
    end
    g_Bank.LastPriceUpdate = now

    for _, sign in ipairs(g_Bank.Signs) do
        HeRpc("findOne", {
            contract = "market",
            table = "metrics",
            query = { symbol = sign.Symbol },
        }, function(success, bodyOrErr)
            local world = cRoot:Get():GetWorld(g_Bank.WorldName)
            if (not world) then
                return
            end
            if (not success) then
                world:SetSignLines(sign.X, sign.Y, sign.Z, "PRICE", sign.Symbol, "n/a", "")
                return
            end
            local result = cJson:Parse(bodyOrErr)
            local lastPrice = "n/a"
            local volume = ""
            if (result and result.result) then
                lastPrice = result.result.lastPrice or lastPrice
                volume = result.result.volume or volume
            end
            local line4 = (volume ~= "") and ("vol " .. tostring(volume)) or ""
            world:SetSignLines(sign.X, sign.Y, sign.Z, "PRICE", sign.Symbol, tostring(lastPrice), line4)
        end)
    end
end

local function EnsureBankPlatform(a_World, a_X, a_Y, a_Z)
    -- Build a simple 7x7 stone-brick building with a plank roof.
    -- a_Y is the desired ground level; we place the floor one block lower.
    local baseY = a_Y - 1
    -- Use numeric block IDs for compatibility:
    -- 0 = air, 5 = planks, 98 = stone bricks
    local floorBlock = 98
    local wallBlock = 98
    local roofBlock = 5
    local roofMeta = 0  -- oak
    local airBlock = 0
    local size = 3
    local wallHeight = 5
    local barsBlock = 101 -- iron bars

    -- Clear a 7x7x8 volume to remove old blocks
    for dy = 0, 7 do
        for dx = -size, size do
            for dz = -size, size do
                SetBlockAt(a_World, a_X + dx, baseY + dy, a_Z + dz, airBlock, 0)
            end
        end
    end

    -- Floor (7x7)
    for dx = -size, size do
        for dz = -size, size do
            SetBlockAt(a_World, a_X + dx, baseY, a_Z + dz, floorBlock, 0)
        end
    end

    -- Walls (height 5)
    for dy = 1, wallHeight do
        for dx = -size, size do
            for dz = -size, size do
                local isEdge = (dx == -size) or (dx == size) or (dz == -size) or (dz == size)
                if (isEdge) then
                    -- Doorway at front center (z -size)
                    if not((dz == -size) and (dx == 0) and (dy <= 2)) then
                        SetBlockAt(a_World, a_X + dx, baseY + dy, a_Z + dz, wallBlock, 0)
                    end
                end
            end
        end
    end

    -- Roof (7x7)
    for dx = -size, size do
        for dz = -size, size do
            SetBlockAt(a_World, a_X + dx, baseY + wallHeight + 1, a_Z + dz, roofBlock, roofMeta)
        end
    end

    -- Banker cage (3x3 iron bars) centered on the banker
    local cageMinX = a_X - 1
    local cageMaxX = a_X + 1
    local cageMinZ = a_Z - 1
    local cageMaxZ = a_Z + 1
    for dy = 1, 3 do
        for dx = cageMinX, cageMaxX do
            for dz = cageMinZ, cageMaxZ do
                local isEdge = (dx == cageMinX) or (dx == cageMaxX) or (dz == cageMinZ) or (dz == cageMaxZ)
                if (isEdge) then
                    SetBlockAt(a_World, dx, baseY + dy, dz, barsBlock, 0)
                else
                    SetBlockAt(a_World, dx, baseY + dy, dz, airBlock, 0)
                end
            end
        end
    end
    -- Cage roof
    for dx = cageMinX, cageMaxX do
        for dz = cageMinZ, cageMaxZ do
            SetBlockAt(a_World, dx, baseY + 4, dz, barsBlock, 0)
        end
    end
end

function SetupBank()
    if (g_Bank.Placed) then
        return
    end
    local world = cRoot:Get():GetDefaultWorld()
    if (not world) then
        LOGWARNING("HiveEngineTrade: default world not ready")
        return
    end
    local spawnX = math.floor(world:GetSpawnX())
    local spawnY = math.floor(world:GetSpawnY())
    local spawnZ = math.floor(world:GetSpawnZ())

    local useFixed = ((g_Config.BankX ~= 0) or (g_Config.BankY ~= 0) or (g_Config.BankZ ~= 0))
    local atmX = useFixed and g_Config.BankX or (spawnX + 1)
    local atmZ = useFixed and g_Config.BankZ or spawnZ

    g_Bank.WorldName = world:GetName()
    if (g_Config.Debug) then
        LOG("HiveEngineTrade: SetupBank world=" .. g_Bank.WorldName .. " useFixed=" .. tostring(useFixed) .. " target=" .. atmX .. "," .. (useFixed and g_Config.BankY or spawnY) .. "," .. atmZ)
    end

    local chunkX = math.floor(atmX / 16)
    local chunkZ = math.floor(atmZ / 16)

    world:ChunkStay({{chunkX, chunkZ}}, nil, function ()
        local groundY = spawnY
        if (useFixed) then
            groundY = g_Config.BankY
        else
            local isValid, height = world:TryGetHeight(atmX, atmZ)
            if (isValid and height and (height > 0)) then
                groundY = height
            end
        end
        if (g_Config.Debug) then
            LOG("HiveEngineTrade: Chunk ready; placing bank at " .. atmX .. "," .. groundY .. "," .. atmZ)
        end
        g_Bank.AtmX = atmX
        g_Bank.AtmZ = atmZ
        g_Bank.AtmY = groundY
        g_Bank.AtmBaseY = groundY - 1
        EnsureBankPlatform(world, atmX, groundY, atmZ)
        world:SetSpawn(atmX, groundY, atmZ)
        EnsureBankerAt(world, atmX, groundY, atmZ)
        EnsureBankSigns(world)
        g_Bank.Placed = true
        if (g_Config.Debug) then
            LOG("HiveEngineTrade: Bank placed and spawn set")
        end
    end)
end

function OnTick(a_TimeDelta)
    if (g_Config.BankEnabled and (not g_Bank.Placed)) then
        SetupBank()
    end
    if (g_Config.BankEnabled and g_Bank.Placed and (not g_Bank.SignsPlaced)) then
        local world = cRoot:Get():GetWorld(g_Bank.WorldName)
        if (world) then
            EnsureBankSigns(world)
        end
    end
    UpdateBankSigns()
    g_TickCounter = g_TickCounter + 1
    if (g_Config.Debug and (g_TickCounter == 20)) then
        LOG("HiveEngineTrade: Tick running, bankPlaced=" .. tostring(g_Bank.Placed))
    end
end

function HandleHiveBankSigns(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local world = a_Player:GetWorld()
    g_Bank.SignsPlaced = false
    EnsureBankSigns(world)
    SendPlayerMessage(a_Player, "Bank signs refreshed")
    return true
end

function HandleHiveLink(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local hiveAccount = a_Split[2]
    if (not hiveAccount or hiveAccount == "") then
        return true, "Usage: /hive link <hiveAccount>"
    end
    SaveAccount(a_Player:GetName(), hiveAccount)
    SendPlayerMessage(a_Player, "Linked Hive account: " .. hiveAccount)
    return true
end

function HandleHiveBridge(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local url = a_Split[2]
    if (not url or url == "") then
        return true, "Usage: /hive bridge <url|clear>"
    end
    if (string.lower(url) == "clear") then
        SaveBridge(a_Player:GetName(), "")
        SendPlayerMessage(a_Player, "Cleared personal bridge URL; using server default")
        return true
    end
    SaveBridge(a_Player:GetName(), url)
    SendPlayerMessage(a_Player, "Set personal bridge URL: " .. url)
    return true
end

function HandleHiveVault(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local name = a_Player:GetName()
    local perPlayer = g_Ledger[name]
    if (not perPlayer) then
        SendPlayerMessage(a_Player, "Vault balance: empty")
        return true
    end
    SendPlayerMessage(a_Player, "Vault balances for " .. name .. ":")
    local count = 0
    for symbol, amount in pairs(perPlayer) do
        SendPlayerMessage(a_Player, "- " .. symbol .. " = " .. tostring(amount))
        count = count + 1
    end
    if (count == 0) then
        SendPlayerMessage(a_Player, "Vault balance: empty")
    end
    return true
end

function HandleHiveVaultAdd(a_Split, a_Player)
    if (not HasAdminPermission(a_Player)) then
        return true, "No permission"
    end
    local playerName = a_Split[2]
    local symbol = a_Split[3]
    local amount = a_Split[4]
    if (not playerName or not symbol or not amount) then
        return true, "Usage: /hive vaultadd <player> <symbol> <amount>"
    end
    local newBalance, err = AddLedgerBalance(playerName, symbol, amount)
    if (not newBalance) then
        SendPlayerMessage(a_Player, "Vault add failed: " .. (err or "<error>"))
        return true
    end
    SendPlayerMessage(a_Player, "Vault updated: " .. playerName .. " " .. symbol .. " = " .. tostring(newBalance))
    return true
end

function HandleHiveVaultTake(a_Split, a_Player)
    if (not HasAdminPermission(a_Player)) then
        return true, "No permission"
    end
    local playerName = a_Split[2]
    local symbol = a_Split[3]
    local amount = a_Split[4]
    if (not playerName or not symbol or not amount) then
        return true, "Usage: /hive vaulttake <player> <symbol> <amount>"
    end
    local delta = -math.abs(tonumber(amount) or 0)
    if (delta == 0) then
        return true, "Invalid amount"
    end
    local newBalance, err = AddLedgerBalance(playerName, symbol, delta)
    if (not newBalance) then
        SendPlayerMessage(a_Player, "Vault take failed: " .. (err or "<error>"))
        return true
    end
    SendPlayerMessage(a_Player, "Vault updated: " .. playerName .. " " .. symbol .. " = " .. tostring(newBalance))
    return true
end

function HandleHiveDeposit(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local symbol = a_Split[2]
    local amount = a_Split[3]
    if (not symbol or not amount) then
        return true, "Usage: /hive deposit <symbol> <amount>"
    end
    local amountNum = tonumber(amount)
    if (not amountNum or amountNum <= 0) then
        return true, "Invalid amount"
    end
    local hiveAccount = RequireLinkedAccount(a_Player)
    if (not hiveAccount) then
        return true
    end

    local headers = {}
    if (g_Config.BridgeAuthToken ~= "") then
        headers["Authorization"] = "Bearer " .. g_Config.BridgeAuthToken
    end

    local payload = {
        action = "deposit",
        account = hiveAccount,
        player = a_Player:GetName(),
        symbol = symbol,
        amount = tostring(amountNum),
    }

    local bridgeUrl = GetBridgeUrlForPlayer(a_Player)
    HttpPostJson(bridgeUrl .. "/deposit", payload, function(success, bodyOrErr)
        if (not success) then
            SendPlayerMessage(a_Player, "Deposit request failed: " .. (bodyOrErr or "<error>"))
            return
        end
        local result, err = cJson:Parse(bodyOrErr)
        if (not result) then
            SendPlayerMessage(a_Player, "Bridge response invalid: " .. (err or "<parse error>"))
            return
        end
        if (result.ok) then
            local instructions = result.instructions or result.memo or result.address or "Deposit request created"
            SendPlayerMessage(a_Player, "Deposit instructions: " .. instructions)
            SendPlayerMessage(a_Player, "After deposit confirms, an admin will credit your vault balance.")
        else
            SendPlayerMessage(a_Player, "Deposit rejected: " .. (result.error or "<unknown>"))
        end
    end, headers)

    return true
end

function HandleHiveWithdraw(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local symbol = a_Split[2]
    local amount = a_Split[3]
    if (not symbol or not amount) then
        return true, "Usage: /hive withdraw <symbol> <amount>"
    end
    local amountNum = tonumber(amount)
    if (not amountNum or amountNum <= 0) then
        return true, "Invalid amount"
    end
    local name = a_Player:GetName()
    local balance = GetLedgerBalance(name, symbol)
    if (balance < amountNum) then
        SendPlayerMessage(a_Player, "Insufficient vault balance: " .. tostring(balance))
        return true
    end
    local hiveAccount = RequireLinkedAccount(a_Player)
    if (not hiveAccount) then
        return true
    end

    local headers = {}
    if (g_Config.BridgeAuthToken ~= "") then
        headers["Authorization"] = "Bearer " .. g_Config.BridgeAuthToken
    end

    local payload = {
        action = "withdraw",
        account = hiveAccount,
        player = a_Player:GetName(),
        symbol = symbol,
        amount = tostring(amountNum),
    }

    local bridgeUrl = GetBridgeUrlForPlayer(a_Player)
    HttpPostJson(bridgeUrl .. "/withdraw", payload, function(success, bodyOrErr)
        if (not success) then
            SendPlayerMessage(a_Player, "Withdraw failed: " .. (bodyOrErr or "<error>"))
            return
        end
        local result, err = cJson:Parse(bodyOrErr)
        if (not result) then
            SendPlayerMessage(a_Player, "Bridge response invalid: " .. (err or "<parse error>"))
            return
        end
        if (result.ok) then
            if (result.txid or result.processed) then
                local newBalance = AddLedgerBalance(name, symbol, -amountNum)
                SendPlayerMessage(a_Player, "Withdraw submitted: " .. (result.txid or "<processed>") .. ". New vault balance: " .. tostring(newBalance))
            elseif (result.pending) then
                SendPlayerMessage(a_Player, "Withdraw queued: " .. (result.requestId or "<pending>"))
                SendPlayerMessage(a_Player, "An admin will finalize this withdrawal.")
            else
                SendPlayerMessage(a_Player, "Withdraw accepted.")
            end
        else
            SendPlayerMessage(a_Player, "Withdraw rejected: " .. (result.error or "<unknown>"))
        end
    end, headers)

    return true
end

function HandleHiveBalance(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local symbol = a_Split[2]
    if (not symbol or symbol == "") then
        return true, "Usage: /hive balance <symbol> [hiveAccount]"
    end

    local hiveAccount = a_Split[3]
    if (not hiveAccount or hiveAccount == "") then
        hiveAccount = RequireLinkedAccount(a_Player)
        if (not hiveAccount) then
            return true
        end
    end

    HeRpc("findOne", {
        contract = "tokens",
        table = "balances",
        query = { account = hiveAccount, symbol = symbol },
    }, function(success, bodyOrErr)
        if (not success) then
            SendPlayerMessage(a_Player, "Balance lookup failed: " .. (bodyOrErr or "<error>"))
            return
        end
        local result, err = cJson:Parse(bodyOrErr)
        if (not result) then
            SendPlayerMessage(a_Player, "Invalid response: " .. (err or "<parse error>"))
            return
        end
        local balance = "0"
        if (result.result and result.result.balance) then
            balance = result.result.balance
        end
        SendPlayerMessage(a_Player, "Balance " .. hiveAccount .. " " .. symbol .. " = " .. balance)
    end)

    return true
end

function HandleHivePrice(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local symbol = a_Split[2]
    if (not symbol or symbol == "") then
        return true, "Usage: /hive price <symbol>"
    end

    HeRpc("findOne", {
        contract = "market",
        table = "metrics",
        query = { symbol = symbol },
    }, function(success, bodyOrErr)
        if (not success) then
            SendPlayerMessage(a_Player, "Price lookup failed: " .. (bodyOrErr or "<error>"))
            return
        end
        local result, err = cJson:Parse(bodyOrErr)
        if (not result) then
            SendPlayerMessage(a_Player, "Invalid response: " .. (err or "<parse error>"))
            return
        end
        local lastPrice = "n/a"
        local volume = "n/a"
        if (result.result) then
            lastPrice = result.result.lastPrice or lastPrice
            volume = result.result.volume or volume
        end
        SendPlayerMessage(a_Player, "Market " .. symbol .. " lastPrice=" .. lastPrice .. " volume=" .. volume)
    end)

    return true
end

function HandleHiveSwap(a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end
    local hiveAmount = a_Split[2]
    local symbol = a_Split[3] or g_Config.DefaultSymbol
    if (not hiveAmount) then
        return true, "Usage: /hive swap <hiveAmount> [symbol]"
    end

    local amountNum = tonumber(hiveAmount)
    if (not amountNum or amountNum <= 0) then
        return true, "Invalid hiveAmount"
    end

    local hiveAccount = RequireLinkedAccount(a_Player)
    if (not hiveAccount) then
        return true
    end

    HeRpc("findOne", {
        contract = "market",
        table = "metrics",
        query = { symbol = symbol },
    }, function(success, bodyOrErr)
        if (not success) then
            SendPlayerMessage(a_Player, "Swap lookup failed: " .. (bodyOrErr or "<error>"))
            return
        end
        local result, err = cJson:Parse(bodyOrErr)
        if (not result) then
            SendPlayerMessage(a_Player, "Invalid response: " .. (err or "<parse error>"))
            return
        end
        if (not result.result or not result.result.lastPrice) then
            SendPlayerMessage(a_Player, "No market data for " .. symbol)
            return
        end
        local price = tonumber(result.result.lastPrice)
        if (not price or price <= 0) then
            SendPlayerMessage(a_Player, "Invalid market price for " .. symbol)
            return
        end
        local quantity = amountNum / price
        local qtyStr = string.format("%.3f", quantity)
        local priceStr = string.format("%.3f", price)

        SendPlayerMessage(a_Player, "Submitting market swap at price " .. priceStr .. " for quantity " .. qtyStr)
        local split = { a_Split[1], symbol, priceStr, qtyStr }
        SubmitTrade("buy", split, a_Player)
    end)

    return true
end

local function SubmitTrade(a_Action, a_Split, a_Player)
    if (not a_Player) then
        return true, "This command must be run by a player"
    end

    local symbol = a_Split[2]
    local price = a_Split[3]
    local quantity = a_Split[4]
    if (not symbol or not price or not quantity) then
        return true, "Usage: /hive " .. a_Action .. " <symbol> <price> <quantity>"
    end

    local hiveAccount = RequireLinkedAccount(a_Player)
    if (not hiveAccount) then
        return true
    end

    local headers = {}
    if (g_Config.BridgeAuthToken ~= "") then
        headers["Authorization"] = "Bearer " .. g_Config.BridgeAuthToken
    end

    local payload = {
        action = a_Action,
        account = hiveAccount,
        symbol = symbol,
        price = price,
        quantity = quantity,
    }

    local bridgeUrl = GetBridgeUrlForPlayer(a_Player)
    HttpPostJson(bridgeUrl .. "/trade", payload, function(success, bodyOrErr)
        if (not success) then
            SendPlayerMessage(a_Player, "Trade failed: " .. (bodyOrErr or "<error>"))
            return
        end
        local result, err = cJson:Parse(bodyOrErr)
        if (not result) then
            SendPlayerMessage(a_Player, "Bridge response invalid: " .. (err or "<parse error>"))
            return
        end
        if (result.ok) then
            SendPlayerMessage(a_Player, "Trade submitted: " .. (result.txid or "<no txid>"))
        else
            SendPlayerMessage(a_Player, "Trade rejected: " .. (result.error or "<unknown>"))
        end
    end, headers)

    return true
end

function HandleHiveBuy(a_Split, a_Player)
    return SubmitTrade("buy", a_Split, a_Player)
end

function HandleHiveSell(a_Split, a_Player)
    return SubmitTrade("sell", a_Split, a_Player)
end

function OnPlayerRightClickingEntity(a_Player, a_Entity)
    if (not IsBankerEntity(a_Entity)) then
        return false
    end

    SendPlayerMessage(a_Player, g_Config.BankName .. " - Banker")
    SendPlayerMessage(a_Player, "Use /hive vault, /hive deposit <symbol> <amount>, /hive withdraw <symbol> <amount>")
    SendPlayerMessage(a_Player, "Market: /hive price " .. g_Config.DefaultSymbol .. " and /hive swap <hiveAmount> " .. g_Config.DefaultSymbol)
    return true
end

function OnPlayerBreakingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta)
    if (g_Bank.WorldName == "") then
        return false
    end
    local world = a_Player:GetWorld()
    if (world:GetName() ~= g_Bank.WorldName) then
        return false
    end
    local inFootprint =
        (a_BlockX >= g_Bank.AtmX - 2) and (a_BlockX <= g_Bank.AtmX + 2) and
        (a_BlockZ >= g_Bank.AtmZ - 2) and (a_BlockZ <= g_Bank.AtmZ + 2) and
        (a_BlockY >= g_Bank.AtmBaseY) and (a_BlockY <= g_Bank.AtmBaseY + 4)
    if (inFootprint) then
        SendPlayerMessage(a_Player, "This bank is unbreakable")
        return true
    end
    return false
end

🪙 PeakeCoin Ecosystem

💱 PeakeCoin USDT Bridge (Hive ↔ Polygon/MATIC)
Bridge SWAP.USDT from Hive Engine to USDT on Polygon (MATIC).
Whitelist access, documentation, and bridge status updates:
👉 https://geocities.ws/peakecoin


⚙️ HiveP.I.M.P. — PeakeCoin Intelligent Market Protector
Operated by @hivepimp, P.I.M.P. focuses on stabilizing PEK markets and strengthening liquidity on Hive Engine.
Community participation supports long-term ecosystem health.


🤖 PeakeBot — Autonomous Trading System
Independent multi-token trading bot with RC-awareness, adaptive delay logic, and smart cycle control.
📊 Trading bot documentation:
👉 https://geocities.ws/p/e/peakecoin/trading-bot/peakebot_v0_01.html
💻 Open-source repositories:
👉 https://github.com/paulmoon410


🎰 PeakeSino — The PeakeCoin Casino (Beta)
Blockchain-powered games using PEK as the native in-game currency.
Built on Hive with a focus on provable fairness and community-driven growth.
🃏 Play the beta games here:
👉 https://geocities.ws/peakecoin/pek_casino/beta_games/index.html


🙏 Acknowledgements

Thanks to and please follow:
@enginewitty @ecoinstant @neoxian @txracer @thecrazygm @holdonia @aggroed

For their continued support, guidance, and help expanding the PeakeCoin ecosystem.


Sort:  

Upvoted! Thank you for supporting witness @jswit.

Coin Marketplace

STEEM 0.06
TRX 0.30
JST 0.053
BTC 71636.90
ETH 2111.86
USDT 1.00
SBD 0.49