Skip to content

Script examples

wolfieboy09 edited this page Jul 17, 2024 · 15 revisions

Vanilla

Example of methods you could add to furnaces:

// priority: 0
// The following code was last updated: April 30th 2024

// Visit the wiki for more info - https://kubejs.com/
ComputerCraftEvents.peripheral(event => {
    // First Param: peripheral type
    // Second Param: What block it goes to
    // Note: you can use regex for the second param
    
    event.registerPeripheral("furnace", "minecraft:furnace")
        // This limits the method to 1 call/tick,
        // as the method is scheduled on main thread.
        // The main thread is synced with the block
        .mainThreadMethod("burnTime", (container, direction, arguments) => {
            // This custom method returns the current
            // remaining burntime the furnace has.
            return container.entityData.getInt("BurnTime")
        })
        .mainThreadMethod("cookingProgress", (container) => {
            // This custom method returns the percentage
            // of the current cooking process going on.
            // A progress of 0 returned during two consecutive
            // ticks means that there is no cooking happening.
            let data = container.entityData
            let cookTime = data.getInt('CookTime')
            let cookTimeTotal = data.getInt('CookTimeTotal')
            if (!cookTimeTotal) return 0;
            return (cookTime / cookTimeTotal) * 100
        })


        // This has no limit on calling
        // however, the method can't access most of the in-world data.
        // For example, it couldn't access the NBT of a tile entity
        .method("say_hi", (container, direction, arguments) => {
            container.up.set("diamond_block")
            return "hi, here's your diamond block"
        })
})

Restrict methods to Command Computers

If you're a modpack creator, you may want to rely on Command Computers without allowing your players to call custom methods:

const ServerContext = Java.loadClass("dan200.computercraft.shared.computer.core.ServerContext")

function isCommandComputer (icomputer) {
    const computerId = icomputer.getID()
    const context = ServerContext.get(Utils.getServer())
    const computer = context.registry().getComputers().toArray().find(c => c.getID() === computerId)

    if (!computer) return false

    return computer.getFamily().toString() === "COMMAND"
}

ComputerCraftEvents.peripheral(event => {
    event.registerPeripheral("commandcomputer:furnace", "minecraft:furnace")
        .mainThreadMethod("setBurnTime", (block, dir, args, computer) => {
            if (args.length < 1) throw new Error("setBurnTime(burnTime)")
            if (!isCommandComputer(computer)) throw new Error("This method can only be called by a Command Computer")

            const burnTime = toInt(args[0])
            let data = block.entityData
            data.putInt("BurnTime", burnTime)
            block.setEntityData(data)
        
        })
})

Throttling method calls

If you're a server owner, you may want to limit the number of times a method is called per second on the same peripheral.

const lastThrottleCleanup = Date.now()
const throttleMap = {}
function getPosStr (block) {
    return block.level.dimension.toString() + "@" + block.pos.toString()
}

function cleanupThrottle () {
    const now = Date.now()
    Object.entries(throttleMap).forEach((blockPosStr, expirationTime) => {
        if (now >= expirationTime) {
            delete throttleMap[blockPosStr]
        }
    })
}

function throttleCheck (block, identifier, ms) {
    const now = Date.now()
    const posStr = getPosStr(block) + '@' + identifier
    const throttle = throttleMap[posStr]

    if (throttle && throttle > now) throw new Error("This method is throttled to " + ms + "ms between each call.")
    
    throttleMap[posStr] = now + ms

    if (lastThrottleCleanup + 60000 < now) {
        lastThrottleCleanup = now
        cleanupThrottle()
    }
}

To use it, you just need to put this call at the beggining of your method implementation:

throttleCheck(block, "myperiph.myMethod", 100) // Will make sure the last call of myMethod 
                                               // was made at least 100ms ago, otherwise it
                                               // will throw an error.

We recommend to put it after checking arguments, if there is any.

Pollution of the Realms (aka Pother)

Example of methods you could add to Pother Filter Frames:

Warning

Keep in mind that those functions can take a lot of resources if they are called too frequently. You may want to implement a throttler if you run a public server. And/or decrease the radius formula.

// The following code was last updated: May (the) 4th (be with you) 2024
ComputerCraftEvents.peripheral(event => {
    const WorldData = Java.tryLoadClass("com.endertech.minecraft.mods.adpother.pollution.WorldData")
    const filterMaterials = ['iron', 'gold', 'diamond']

    event.registerPeripheral("pother_filter", /^adpother:(iron|gold|diamond)_filter_frame$/)
        // Returns the amount of pollution that has an impact on the chunk pollution level
        // meaning: it will only count pollution that have reached cloud altitude
        // chunk radius = iron[3x3] / gold[5x5] / diamond[7x7]
        .mainThreadMethod("getCloudPollutionCount", block => {
            const radius = filterMaterials.findIndex(v => block.id.contains(v)) + 1
            let pol, polRes;
            const amounts = {}

            for (let i = -radius; i <= radius; i++) {
                for (let j = -radius; j <= radius; j++) {
                    pol = WorldData.getChunkPollution(block.level, block.pos.offset(i * 16, 0, j * 16))
                    polRes = pol.getInfos()
                    
                    for (let polInfo of polRes.toList()) {
                        let polName = polInfo.getPollutant().getSimpleName()
                        if (!amounts[polName]) amounts[polName] = polInfo.getQuantity()
                        else amounts[polName] += polInfo.getQuantity()
                        //console.info(pol.getPollutant().toString() + ": "+pol.getQuantity())
                    }

                }
            }
            return amounts
        })
        // Returns the amount of pollution inside the cubic radius around the filter
        // it will NOT count GasEntities (gas moving up or down)
        // block radius = iron(8)[17x17x17] / gold(16)[33x33x33] / diamond(32)[65x65x65]
        // args: radius, yRelativeLevel
        .mainThreadMethod("getNearPollutionCount", (block, dir, args) => {
            if (args.length < 2) return "getNearPollutionCount(radius, yRelativeLevel)"
            const amounts = {}
            const argRadius = args[0]
            const y = args[1]
            const radius = filterMaterials.findIndex(v => block.id.contains(v))
            const blRadius = Math.min(argRadius, 8 * Math.pow(2, radius))
            let b, bs, pos
            for (let i = -blRadius; i <= blRadius; i++) {
                for (let j = -blRadius; j <= blRadius; j++) {
                
                    pos = block.pos.offset(i, y, j)
                    bs = block.level.getBlockState(pos)
                    b = bs.getBlock()
                    if (b && b.getCarriedPollutionAmount) {
                            if (!amounts[b.getSimpleName()]) amounts[b.getSimpleName()] = b.getCarriedPollutionAmount(bs)
                            else amounts[b.getSimpleName()] += b.getCarriedPollutionAmount(bs)
                    }   
                }
            }

            return amounts
        })
        // Returns the pollution blocks pos inside the cubic radius around the filter
        // it will NOT include GasEntities (gas moving up or down)
        // args: radius, yRelativeLevel
        .mainThreadMethod("getNearPollutionBlocks", (block, dir, args) => {
            if (args.length < 2) return "getNearPollutionBlocks(radius, yRelativeLevel)"
            const argRadius = args[0]
            const y = args[1]
            const radius = filterMaterials.findIndex(v => block.id.contains(v))
            const blRadius = Math.min(argRadius, 8 * Math.pow(2, radius))
            const list = []
            let b, bs, pos
            for (let i = -blRadius; i <= blRadius; i++) {
                for (let j = -blRadius; j <= blRadius; j++) {
                
                    pos = block.pos.offset(i, y, j)
                    bs = block.level.getBlockState(pos)
                    b = bs.getBlock()
                    if (b && b.getCarriedPollutionAmount) {
                        list.push({
                            type: b.getSimpleName(),
                            amount: b.getCarriedPollutionAmount(bs),
                            pos: {
                                x: pos.x,
                                y: pos.y,
                                z: pos.z
                            }
                        })
                    }
                }
            }

            return list
        })
})

GregTech Modern (GTCEu)

Example of methods you could add to Gregtech Modern (GTCEu) machines:

Note

You don't need to copy/paste the entire script below; you can just pick which peripherals/methods you want to have. You can also make ones of your own, imagination is the limit, that, and GTCEu sourcecode. If you want a resource pack that looks "gregy," use Birbirl's resource pack: ComputerCraft: Greg Flavored (CurseForge) or ComputerCraft: Greg Flavored (Modrinth)

// priority: 0
// The following code was last updated: May 3rd 2024

const Double = Java.loadClass("java.lang.Double")
const Integer = Java.loadClass("java.lang.Integer")
const Optional = Java.loadClass("java.util.Optional")

const GTCapabilityHelper = Java.loadClass("com.gregtechceu.gtceu.api.capability.GTCapabilityHelper")
const IntCircuitBehaviour = Java.loadClass("com.gregtechceu.gtceu.common.item.IntCircuitBehaviour")
const LargeTurbineMachine = Java.loadClass("com.gregtechceu.gtceu.common.machine.multiblock.generator.LargeTurbineMachine")
const RotorHolderPartMachine = Java.loadClass("com.gregtechceu.gtceu.common.machine.multiblock.part.RotorHolderPartMachine")

// This function is a shortcut allowing to get
// directly the MetaMachine field without having
// to repeat the same code inside our testers/methods
function metaMachineWrapper (cb) {
    return function (block, dir, args, computer, ctx) {
        if (!block || !block.entity || !block.entity.metaMachine) return false
        return cb(block.entity.metaMachine, block, dir, args, computer, ctx)
    }
}

// Also a shortcut to get/find the RotorHolder
// among the large turbine multiblock parts.
function getRotorHolder (turbineMachine) {
    if (!(turbineMachine instanceof LargeTurbineMachine)) return null

    for (let part of turbineMachine.getParts()) {
        if (part.getClass() == RotorHolderPartMachine) {
          return part
        } 
    } 
    return null
}

function toInt (param) {
    if (param == null) return null
    if (param instanceof Double && Math.floor(param) === param)
        return Integer.valueOf(param.intValue())
    else
        throw new Error("The param value '" + param + "' must be a valid integer")
}
function nullifyUndefined (param) {
    return param === undefined ? null : param
}
function opt (param) {
    return Optional.ofNullable(nullifyUndefined(param))
}

ComputerCraftEvents.peripheral(event => {
    // Example use of the registerPeripheral method by
    // providing a regex pattern to match GTCEu wires & cables
    event.registerPeripheral("gt_cable", /^gtceu:.*_(wire|cable)$/)
        .mainThreadMethod("getAverageVoltage", (block) => {
            if (!block.entity) return 0;
            return block.entity.averageVoltage
        })
        .mainThreadMethod("getAverageAmperage", (block) => {
            if (!block.entity) return 0;
            return block.entity.averageAmperage
        })
        .mainThreadMethod("getAverageFlowingCurrent", (block) => {
            if (!block.entity) return 0;
            return block.entity.averageVoltage * block.entity.averageAmperage
        })

    // registerComplexPeripheral is another way of registering
    // a peripheral toward many blocks sharing a capability/feature
    // checked inside a custom test function returning only true or false.
    event.registerComplexPeripheral("gt_energy_container", metaMachineWrapper((machine) => {
        return !!(machine.energyContainer)
    }))
        .mainThreadMethod("getEnergyStored", metaMachineWrapper((machine) => {
            return machine.energyContainer.energyStored
        }))
        .mainThreadMethod("getEnergyCapacity", metaMachineWrapper((machine) => {
            return machine.energyContainer.energyCapacity
        }))
        .mainThreadMethod("getOutputPerSec", metaMachineWrapper((machine) => {
            return machine.energyContainer.getOutputPerSec()
        }))
        .mainThreadMethod("getInputPerSec", metaMachineWrapper((machine) => {
            return machine.energyContainer.getInputPerSec()
        }))

    // GTCapabilityHelper.getXXX() is another handy way to get
    // capability handlers directly, but we suspect it might
    // take more resources than our custom metaMachineWrapper.
    event.registerComplexPeripheral("gt_workable", (block) => !!GTCapabilityHelper.getWorkable(block.level, block.pos, null))
        .mainThreadMethod("getProgress", (block, dir) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).progress)
        .mainThreadMethod("getMaxProgress", (block, dir) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).maxProgress)
        .mainThreadMethod("isActive", (block, dir) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).isActive())

    event.registerComplexPeripheral("gt_controllable", (block) => !!GTCapabilityHelper.getControllable(block.level, block.pos, null))
        .mainThreadMethod("isWorkingEnabled", (block, dir) => GTCapabilityHelper.getControllable(block.level, block.pos, dir).isWorkingEnabled())
        .mainThreadMethod("setWorkingEnabled", (block, dir, args) => GTCapabilityHelper.getControllable(block.level, block.pos, dir).setWorkingEnabled(!!args[0]) || "OK")

    event.registerComplexPeripheral("gt_overclockable", (block) => !!GTCapabilityHelper.getWorkable(block.level, block.pos, null))
        .mainThreadMethod("getOverclockTier", (block, dir) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).getOverclockTier())
        .mainThreadMethod("getOverclockVoltage", (block, dir) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).getOverclockVoltage())
        .mainThreadMethod("getMaxOverclockTier", (block, dir) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).getMaxOverclockTier())
        .mainThreadMethod("getMinOverclockTier", (block, dir) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).getMinOverclockTier())
        .mainThreadMethod("setOverclockTier", (block, dir, args) => GTCapabilityHelper.getWorkable(block.level, block.pos, dir).setOverclockTier(toInt(args[0])) || "OK")

    // This one feels like magic: it allows to get/set the circuit number
    // used by the machine / bus. Keep in mind that `-1` means no circuit.
    event.registerComplexPeripheral("gt_circuit_machine", metaMachineWrapper((machine) => {
        return !!machine && !!machine.getCircuitInventory
    }))
        .mainThreadMethod("getProgrammedCircuit", metaMachineWrapper((machine) => {
            const stack = machine.getCircuitInventory().storage.getStackInSlot(0)
            if (stack == Item.empty) return -1;
            return IntCircuitBehaviour.getCircuitConfiguration(stack)
        }))
        .mainThreadMethod("setProgrammedCircuit", metaMachineWrapper((machine, block, _, args) => {
            const storage = machine.getCircuitInventory().storage
            if (args[0] == -1)
                storage.setStackInSlot(0, Item.empty)
            else
                storage.setStackInSlot(0, IntCircuitBehaviour.stack(toInt(args[0])))

            storage.onContentsChanged(0);
            return "OK"
        }))

    event.registerComplexPeripheral("gt_distinct_part", metaMachineWrapper((machine) => {
        return !!machine && !!machine.setDistinct
    }))
        .mainThreadMethod("isDistinct", metaMachineWrapper(machine => machine.isDistinct()))
        .mainThreadMethod("setDistinct", metaMachineWrapper((machine, block, _, args) => machine.setDistinct(!!args[0]) || "OK"))

    // This one is the most complex one, it allows to manage
    // a large turbine (steam/gas/plasma) and get information
    // about its rotor power/durability/efficiency/speed.
    event.registerComplexPeripheral("gt_turbine_rotor", metaMachineWrapper((machine) => {
        return !!machine && (machine instanceof LargeTurbineMachine)
    }))
        .mainThreadMethod("getOverclockVoltage", metaMachineWrapper((machine) => {
            return machine.getOverclockVoltage()
        }))
        .mainThreadMethod("getCurrentProduction", metaMachineWrapper((machine) => {
            const rotor = getRotorHolder(machine)
            if (!rotor) return 0;
            let voltage = machine.getOverclockVoltage()
            let speed = rotor.getRotorSpeed()
            let maxSpeed = rotor.getMaxRotorHolderSpeed()
            if (speed >= maxSpeed) return voltage

            return Math.floor(voltage * JavaMath.pow(speed / maxSpeed, 2))
        }))
        .mainThreadMethod("getRotorDurability", metaMachineWrapper((machine) => {
            const rotor = getRotorHolder(machine)
            return rotor && rotor.getRotorDurabilityPercent()
        }))
        .mainThreadMethod("hasRotor", metaMachineWrapper((machine) => {
            const rotor = getRotorHolder(machine)
            return rotor && rotor.hasRotor()
        }))
        .mainThreadMethod("getRotorEfficiency", metaMachineWrapper((machine) => {
            const rotor = getRotorHolder(machine)
            return rotor && rotor.getRotorEfficiency()
        }))
        .mainThreadMethod("getRotorPower", metaMachineWrapper((machine) => {
            const rotor = getRotorHolder(machine)
            return rotor && rotor.getRotorPower()
        }))
        .mainThreadMethod("getRotorSpeed", metaMachineWrapper((machine) => {
            const rotor = getRotorHolder(machine)
            return rotor && rotor.getRotorSpeed()
        }))
        .mainThreadMethod("getMaxRotorSpeed", metaMachineWrapper((machine) => {
            const rotor = getRotorHolder(machine)
            return rotor && rotor.getMaxRotorHolderSpeed()
        }))
         /**
         * In order to use the three following methods, you'll need to declare those
         * variables in the top-level of your script (that's the very first lines):
         * const InventoryMethodsClass = Java.loadClass("dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods")
         * const InventoryMethods = new InventoryMethodsClass()
         */
        .mainThreadMethod("insertRotor", metaMachineWrapper((machine, block, dir, args, computer) => {
            if (args.length < 2)
                throw new Error("insertRotor(fromInvPeriphName, fromSlot)\n" +
                    "If you use a modem network, the inventory peripheral must be connected on that network.")
            
            const rotor = getRotorHolder(machine)
            if (rotor) {
                const invHandler = rotor.holder.getCapability(ForgeCapabilities.ITEM_HANDLER).resolve().get()
                InventoryMethods.pullItems.apply(InventoryMethods, [invHandler, computer, args[0], toInt(args[1]), opt(toInt(1)), opt(toInt(1))])

                return 1
            }
        }))
        .mainThreadMethod("extractRotor", metaMachineWrapper((machine, block, dir, args, computer) => {
            if (args.length < 1)
                throw new Error("extractRotor(toInvPeriphName[, toSlot])\n" +
                    "If you use a modem network, the inventory peripheral must be connected on that network.\n" +
                    "The inventory peripheral must have one free slot to put the current rotor.")
            
            const rotor = getRotorHolder(machine)
            if (rotor) {
                const invHandler = rotor.holder.getCapability(ForgeCapabilities.ITEM_HANDLER).resolve().get()
                return InventoryMethods.pushItems.apply(InventoryMethods, [invHandler, computer, args[0], toInt(1), opt(toInt(1)), opt(toInt(args[1]))])
            }
        }))
        .mainThreadMethod("hotswapRotor", metaMachineWrapper((machine, block, dir, args, computer) => {
            if (args.length < 2)
                throw new Error("hotswapRotor(fromInvPeriphName, fromSlot)\n" +
                    "If you use a modem network, the inventory peripheral must be connected on that network.\n" +
                    "The inventory peripheral must have one free slot to put the current rotor during the swap.")

            const rotor = getRotorHolder(machine)
            if (rotor) {
                const invHandler = rotor.holder.getCapability(ForgeCapabilities.ITEM_HANDLER).resolve().get()
                InventoryMethods.pushItems.apply(InventoryMethods, [invHandler, computer, args[0], toInt(1), opt(toInt(1)), opt(null)])
                return InventoryMethods.pullItems.apply(InventoryMethods, [invHandler, computer, args[0], toInt(args[1]), opt(toInt(1)), opt(toInt(1))])
            }
        }))
})