-
Notifications
You must be signed in to change notification settings - Fork 2
Script examples
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"
})
})
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)
})
})
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.
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
})
})
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))])
}
}))
})