Create: Avionics
Make your airship fly itself.
CC: Tweaked peripherals for every instrumentable block in Create: Aeronautics. Read attitude, altitude, velocity, bearing. Drive throttle, propellers, balloon gas. Write your autopilot in Lua (that's a bad pitch, but still!)
Build
- Altitude-hold for hot-air balloons
- Heading-and-pitch autopilots for propeller aircraft
- Cruise control bound to a steering wheel
- Tracked turrets for mounted potato cannons
- Whatever else you can write a control loop for
Start
Heading hold on a twin-engine ship:
local nav = peripheral.find("navigation_table")
local port, stbd = peripheral.find("analog_transmission")
local target = nav.getHeadingRad()
while true do
local err = (target - nav.getHeadingRad() + math.pi) % (2*math.pi) - math.pi
local diff = math.max(-5, math.min(5, err * 4))
port.setSignal(7 - diff); stbd.setSignal(7 + diff)
sleep(0.05)
end
Three peripherals, one P-controller, the ship locks heading. Every Aeronautics (and nearly every Create) block is a peripheral — read sensors, set actuators, write the loop you want.
Not just aircraft
The same peripheral.find() surface drives lifts, vehicles, and your whole rotational network — not only things that fly.
Create Elevators — enumerate floors, call the cabin, read where it is:
local lift = peripheral.find("Create_ElevatorPulley")
lift.setTargetFloor(lift.getFloors()[3].y) -- send the cabin to the 3rd floor
repeat sleep(0.1) until lift.isArrived()
print("Arrived: " .. lift.getCurrentFloor().short_name)
Kinetic SCADA — walk the rotation network, alarm on overstress:
local draw, capacity = 0, 0
for _, name in ipairs(peripheral.getNames()) do
local p = peripheral.wrap(name)
if p.getNetworkId then -- any kinetic block
draw = draw + p.getStressImpact()
capacity = capacity + p.getStressContribution()
if p.isOverstressed() then
print("OVERSTRESSED " .. p.getSelfId() .. " (" .. p.getKind() .. ")")
end
end
end
print(("Network load: %.0f / %.0f SU"):format(draw, capacity))
Vehicles — steering and brake override on wheel mounts:
for _, w in ipairs({ peripheral.find("wheel_mount") }) do
w.setSteering(0.5) -- -1..1 -> +/-30 deg
if w.getTouchingFriction() < 0.5 then w.setBrake(0) end -- ease off on slippery stuff
end
getKind, getSubnetworkAnchorId, getColumnContacts, getTouchingFriction — the read side is as rich as the write side.
Full API docs — units, body-frame conventions, every method.
Read the parallel guide. 5 Hz and 20 Hz feel different.
Install
Needs: MC 1.21.1 · NeoForge 21.1.219+ · Create 6.0.10+ · Simulated/Aeronautics/Offroad 1.2.1+ · Sable 1.1.0+ · CC: Tweaked 1.115+
Pairs with
Source · Issues
github.com/SolAstrius/CreateAvionics
MIT. Built on RyanHCode's CC peripheral foundation in Simulated — NOTICE.
Created by yours truly, Sol Astrius Phoenix.
All Versions
+ Add Version### Added
Create_GantryShaftcan now see a carriage while it is moving.** A gantry
contraption at the carriage's own position and removes its blocks from the
world — so every position readback went nil for the entire duration of a
move, which is exactly when a control program needs one. The peripheral now
looks for the contraption entity first and falls back to a block scan, using
Create's own rail-membership test, so positions stay live end to end and are
fractional rather than whole while travelling.
Create_GantryShaft:getCarriage, returning position, state and the
individual getters in a control loop — every field comes from one
observation, so they cannot disagree the way separate polls straddling a
tick boundary can.
Create_GantryShaft:getState, distinguishingempty,parked,
moving, stalled and failed. Previously every one of those answered niland there was no way to tell "no carriage" from "blocked" from "assembly
failed".
Create_GantryShaft:isAssembled,isStalled,getRemainingMovement,
getLastAssemblyError, getRailStart, and disassemble. This brings thegantry to parity with the other contraption controllers — piston, rope
pulley, bearing and elevator pulley all had assembly state and an error
readback already.
getRailStart exposes the origin rail indices aremeasured from, so waypoints can be anchored in world space instead of
silently shifting when a player extends the rail at the start end.
Create_GantryShaftnow queuesgantry_departed,gantry_arrived,
gantry_stalled and gantry_assembly_failed, so a controller can wait onan event instead of polling. Nothing upstream fires on any of these; the
rail is polled for edges every five ticks, and only on shafts that actually
have a computer attached.
### Fixed
Create_GantryShaft:hasCarriageandgetCarriagePositionwere looking
required
GantryCarriageBlock.FACING to point back at the shaft, when inCreate it points *away* from it. The predicate therefore never matched a
carriage on the queried rail at all; what it did match was a carriage one
block away belonging to a *different* rail two blocks away. Since direction
iteration starts at
DOWN, the practical effect was "reports the carriagebelow the rail, never its own" — most visible on a stacked gantry, where
rail, carriage and second rail are exactly that geometry. Reported in #24.
Create_GantryShaft: rail length and index ran past the end of the rail.**
joins shafts into one rail only on exact facing equality. Two abutting
shafts facing opposite ways are two rails, and sneak-placing against an
existing shaft deliberately gives you the opposite facing — so this was one
keystroke away, not a corner case. Affected
getRailLength, getRailIndexand the origin
getCarriagePosition measures from.Create_GantryShaftcould load, and generate, chunks from a Lua call.**
getBlockState up to 256 times per direction with noload check, and
Level.getBlockState resolves its chunk withrequireChunk = true. A rail pointing into ungenerated terrain wouldtherefore generate it, synchronously, on the server thread. The walk now
stops at unloaded chunks, as Create's own does.
Create_GantryShaftdocumented a redstone behaviour that does not
isPowered claimed powering a shaft inverts the carriage'sdirection of travel and
getMovementSpeed claimed to factor that inversionin. Create's
getPinionMovementSpeed reads no redstone state whatsoever. Apowered gantry shaft stops translating its carriage and transmits rotation
into the carriage's output shaft instead — the docs had it backwards from a
real mechanic. Also now documented: the ±0.49 blocks/tick clamp, and that
canAssembleOn answers per-shaft, with single shafts never able toassemble and the two ends requiring opposite signs.
### Docs
altitude_sensor.getVerticalSpeeddocumented a distinction no Lua caller
side", which describes the block entity: the finite difference is
accumulated in a tick handler that returns early on a client level. But
CC:Tweaked runs Lua on the server, so a peripheral call never sees the
client copy. Reworded to what is actually observable — one tick of lag, and
0 until the sensor has ticked twice after being placed or loaded. Same
wording leak removed from four
gas_provider balloon getters ("or 0 if noserver-side balloon" → "or 0 if no balloon is attached"); in a peripheral
call an attached balloon is always the server one.
### Added
- Releases now publish to CurseForge.** The CurseForge listing had been
anyone browsing from a launcher's CurseForge index saw a build three
releases old. Note that Create Aeronautics — which supplies the required
simulated dependency — is published only on Modrinth, and still has tobe installed from there.
### Fixed
- Runs on Create: Simulated 1.3.0.**
mods.tomlhas always declared
simulated [1.2.1,), but 1.3.0 renamed getLaserRange togetRaycastLength and retyped the recovery-compass placer component fromString to UUID, so the jar would not in fact have run against it. Bothare bridged: one jar now covers Simulated 1.2.1 through 1.3.0, and Sable
1.1.x through 2.0.x, with no change to any Lua-visible value.
mounted_potato_cannon:getAimingVectorandgetBarrelPosreturned
promise — the aim came back as a fixed body axis (e.g.
{1, 0, 0}) andthe muzzle position carried raw sub-level local coordinates. Both now
project out of the host sub-level (aim rotated into world frame, muzzle
position projected to world frame), and pass through unchanged when the
cannon isn't on a sub-level ([#18], [#22]).
linked_typewriter: thekeyevent fired before the keypress had been
fires once the key has registered, with
true as the third argument.### Changed
- Startup logs when Create: Simulated's own ComputerCraft registration is
that registration wholesale, and previously nothing in the log said so —
which made reports about missing or misbehaving Simulated peripherals hard
to read.
- Internal: the Create-side peripheral table moved out of
ComputerBehaviourMixin into CreatePeripheralRegistry; the kinetic SCADApack now routes entirely through
KineticReadback so its two declarationscannot drift; the three CC integration classes share one
add() helper.No behavioural change.
- CI compiles and tests against the ends of the declared upstream ranges
build instead of a user's launch. Adds the first tests in the repo.
> **0.5.0 was withdrawn** — it crashed on launch with every mod configuration and has been removed from Modrinth and GitHub releases. Upgrade from 0.4.0 directly to 0.5.1. Since nobody should have 0.5.0, its full changelog is included below.
> 🛑
> **Breaking change for Lua scripts** (introduced in 0.5.0): the per-block
getStressCapacity of the kinetic SCADA pack is renamed to **getStressContribution** (on KineticPeripheral, SimKineticPeripheral, and KineticSource). The old name collided with Create_Stressometer.getStressCapacity, which reports the network-wide total and keeps its name.## 0.5.1
### Fixed
- 0.5.0 crashed on launch** for everyone:
PeripheralComposition
construction, before block-entity registries are bound, throwing
"Trying to access unbound value". Suppliers are now parked and resolved
on first lookup. 0.5.0 has been withdrawn from Modrinth and GitHub
releases — upgrade directly from 0.4.0 to 0.5.1.
- Composed peripherals now report their generic type names: portable
peripheral.hasType(name, "inventory") and are returnedby
peripheral.find("inventory"). 0.5.0 composed only the methods, notthe types, so scripts that locate inventories by type still missed them
(follow-up to [#15]).
## 0.5.0 (yanked) — included because the release was withdrawn
### Added
swivel_bearing: locking-mode control and assembly diagnostics —
isLocked, getLockingMode, setLockingMode (one of locked_always,locked_default, unlocked_default, unlocked_always), andgetLastAssemblyException (the message from the most recent failedassembly attempt, or nil). Public API
ink.astrius.create_avionics.api.simulated.SwivelBearingExt —mixin-supplied interface for downstream addons.
- Kinetic SCADA pack on bare kinetic blocks: a new
KineticSourcegeneric
KineticBlockEntity that has noexplicit peripheral (encased shafts, gearboxes, mixers, …). Thanks
@TechTastic ([#6]).
- Generic-source methods now compose onto capability-supplied peripherals:
(e.g.
portable_engine) also expose matching generic packs instead ofshadowing them.
### Changed
- Breaking**: the per-block
getStressCapacityof the kinetic SCADA pack
getStressContribution (on KineticPeripheral,SimKineticPeripheral, and KineticSource). The old name collided withCreate_Stressometer.getStressCapacity, which reports the network-widetotal and keeps its name.
gas_provider.getGasType(): third-partyLiftingGasTypeimplementations
"unknown". Stock typesstill return
"steam" / "default".- Simulated-side peripherals deduplicated against the originals Simulated
altitude_sensor, gimbal_sensor, and linked_typewriter nowextend their upstream counterparts; standalone duplicates of the
nameplate, modulating-link, directional-link, docking-connector,
nav-table, optical-sensor, and velocity-sensor peripherals were removed
in favor of upstream's, with method parity preserved. Thanks @TechTastic
([#6]).
### Fixed
- Portable engines are inventory peripherals again: the capability-supplied
([#15]).
### Docs
- The kinetic SCADA pack is declared once on the
KineticScadaSurface
(1.10.0-mainthread.4) so
@LuaFunction default methods on interfacesdocument correctly.
StressGauge: use@codeinstead of@linkfor non-peripheral types so
- Full Changelog**: https://github.com/SolAstrius/CreateAvionics/compare/v0.5.0...v0.5.1
- Create: Offroad support.** New
wheel_mountperipheral. - Script overrides for steering and braking.**
setSteering(-1..1)andsetBrake(0..1)bypass the redstone reads. Both independent. - Wheel state reads.**
hasTire,getTireRadius,getExtension,getAngularVelocity,getTouchingFriction,isLiftedUp, plus the usual kinetic SCADA surface.
[Full changelog](https://github.com/SolAstrius/CreateAvionics/blob/main/CHANGELOG.md) · [Docs](https://solastrius.github.io/CreateAvionics/)
gyroscopic_propeller_bearingactually controllable from Lua.** New persistent manual-target override replaces the bearing's automatic gravity-tracking with a script-supplied direction. State is synced to the client and survives save/load. The bearing's 12° cone clamp, redstone power gate, and stabilization-strength gate still apply on top.setManualTarget({x,y,z}),clearManualTarget(),getManualTarget()
## Removed
gyroscopic_propeller_bearing.setTiltandsetStrictTilt. These were never callable in 0.3.0 — CC silently dropped them becauseList<Double>is not a supported@LuaFunctionparameter type. Even with the marshalling fixed, they were single-tick step primitives that the bearing's own physics tick overwrites on the next tick. **UsesetManualTargetinstead.**
[Full changelog](https://github.com/SolAstrius/CreateAvionics/blob/main/CHANGELOG.md) · [Docs](https://solastrius.github.io/CreateAvionics/)
- Proper Create SCADA.** Drop-in replacements for every Create peripheral (
Create_CreativeMotor,Create_RotationSpeedController,Create_Speedometer,Create_Stressometer,Create_MechanicalBearing,Create_MechanicalPiston,Create_RopePulley,Create_ElevatorPulley,Create_ElevatorContact,Create_GantryShaft,Create_SequencedGearshift) - Create's verbatim methods plus a uniform topology surface (getSelfId,getSourceId,getSubnetworkAnchorId,getNetworkId,getKind,getSpeed,hasSource,isOverstressed,getStressImpact,getStressCapacity). nav_tableper-target metadata.** Compass, recovery compass, and map targets now expose lodestone position, recovery death-pos, and map id/bounds.- Public Java API** (
ink.astrius.create_avionics.api.create.*) for downstream addons.
## Breaking - behavior
gimbal_sensor.getLinearAccelerationnow subtracts gravity. A stationary sensor previously reported ~9.8 m/s² on its vertical axis; it now reports zero. Scripts that compensated manually must drop that compensation.
## Breaking - Lua API renames
| Old | New |
|---|---|
|
propeller.getKineticSpeed() | getSpeed() ||
propeller_bearing.getKineticSpeed() | getSpeed() ||
propeller_bearing.getStressApplied() | getStressImpact() ||
mounted_potato_cannon.getKineticSpeed() | getSpeed() ||
analog_transmission.getInputSpeed() | getSpeed() ||
analog_transmission.getStressApplied() | getStressImpact() ||
analog_transmission.getOutputStressApplied() | getOutputStressImpact() |[Full changelog](https://github.com/SolAstrius/CreateAvionics/blob/main/CHANGELOG.md) · [Docs](https://solastrius.github.io/CreateAvionics/)