Trains
Create's Train Station, Train Signal and Train Observer: the train at the platform, its schedule, where it can go, assembly, and the signals along the line.
A computer next to a Create Train Station knows which train stands at the platform, which one is coming, and where it could go from there. It renames trains, reads and writes their schedules, and assembles or takes them apart. Train Signals and Train Observers tell it what happens along the line. With them, a program runs a depot, a passenger board or a whole timetable.
local st = peripheral.find("station")
if st.train_present() then
print(st.train_name() .. " stands at " .. st.name())
endstation.name() | The name of the station, as written in its screen. |
station.set_name(name) | Renames the station, as if you typed the name in its screen. |
station.train_present() | True when a train has stopped at this station. |
station.train_imminent() | True when the train coming here is less than 30 blocks away along the track, or already at the platform: the moment to ring a bell or to light a lamp at the edge of the platform. |
station.train_enroute() | True as soon as a train has this station as its next stop, however far away it is. It stays true while the train stands at the platform, and turns false when the train leaves. |
station.train_name() | The name of the train standing at the station. Stops the program with there is no train present when the platform is empty: a program that wants the name of a train that just left must keep it in a variable. |
station.set_train_name(name) | Renames the train standing at the station. Every player sees the new name at once. |
station.has_schedule() | Whether the train standing at the station follows a schedule. |
station.schedule() | The schedule of the train standing at the station. Stops the program with train doesn't have a schedule when it has none. |
station.set_schedule(schedule) | Gives a schedule to the train standing at the station. It replaces the one the train had, and the train starts it at once, from its first entry (or from the entry a progress key names, counted from 0, when the table has one). |
station.can_reach(name) | Whether the train standing at the station can get to a station matching name. |
station.distance_to(name) | How far the train standing at the station would travel to a station matching name. |
station.assembling() | Whether the station is in assembly mode. While it is, no scheduled train comes to it. |
station.set_assembly_mode(on) | Enters or leaves assembly mode. |
station.assemble() | Assembles the train standing in the station's assembly zone, then leaves assembly mode: the new train is ready to go. |
station.disassemble() | Takes the train standing at the platform apart, into blocks on the track. |
sig.state() | The light the signal shows now. |
sig.forced_red() | Whether the signal is held at red, whatever the trains do. |
sig.set_forced_red(red) | Holds the signal at red, as a redstone signal on it would: trains wait before it. |
sig.blocking_trains() | The trains in the section after the signal: the ones a train waiting at a red light waits for. |
sig.signal_type() | The kind of signal: |
sig.cycle_signal_type() | Switches the signal to the other kind, as Create's Wrench does. |
obs.train_passing() | Whether a train is passing now. The observer stays on for a few ticks after the last carriage, so asking every 0.2 second misses no train. |
obs.passing_train() | The name of the train going past, or nil when none is. |
Setting up
Three blocks. The Train Station is a peripheral of type station, the Train Signal of type signal, the Train Observer of type track_observer. Put one against a face of the computer, or against a Data Cable of its network (Minicomputer and newer, named like station@120,64,-35), then wrap it: peripheral.wrap("left"), or peripheral.find("station"). See Peripherals.
On a track. A station or a signal only works once it is placed against a track, the way Create places it (right-click the track with it first). Otherwise the station stops the program with station is not connected to a track, and the signal with no signal: place it against a track.
Trains are shared. A train belongs to the whole server: a name or a schedule a program changes is seen at once by every player and every other station.
The train at the platform. Many methods work on the train standing at the station. When there is none, they stop the program with there is no train present: ask train_present() first.
No events: ask in a loop. Create's own ComputerCraft peripherals send events (train_arrival, train_passing...). Brass has no peripheral events: a program asks again and again, with a short sleep between two questions. Half a second is plenty for trains.
local st = peripheral.find("station")
while not st.train_present() do
sleep(0.5)
end
print(st.train_name() .. " has arrived")To do other work meanwhile, see the main loop. When a block is broken, its methods stop the program with station removed, signal removed or track observer removed, and with peripheral is not loaded when its chunk is not loaded.
The station
station.name()
The name of the station, as written in its screen.
- string
- the name of the station
local st = peripheral.find("station")
print("Welcome to " .. st.name())See also station.set_name()
station.set_name(name)
Renames the station, as if you typed the name in its screen.
namestring- the new name, cut to 64 characters
Schedules name their stations by text: a train whose schedule asks for the old name will not find the station any more. A station that is not on a track cannot be renamed: the program stops with could not set station name.
See also station.name()
station.train_present()
True when a train has stopped at this station.
- boolean
- true while a train stands at the platform
A train that runs through without stopping does not count: watch it with a Train Observer instead.
See also station.train_imminent() station.train_enroute() station.train_name()
station.train_imminent()
True when the train coming here is less than 30 blocks away along the track, or already at the platform: the moment to ring a bell or to light a lamp at the edge of the platform.
- boolean
- true when a train is about to arrive, or already stands at the platform
local st = peripheral.find("station")
while true do
rs.set("top", st.train_imminent()) -- a lamp above the computer
sleep(0.5)
endSee also station.train_present()
station.train_enroute()
True as soon as a train has this station as its next stop, however far away it is. It stays true while the train stands at the platform, and turns false when the train leaves.
- boolean
- true when a train is on its way to this station, or stands at it
The three questions answer true one after the other: train_enroute, then train_imminent, then train_present.
See also station.train_present()
station.train_name()
The name of the train standing at the station. Stops the program with there is no train present when the platform is empty: a program that wants the name of a train that just left must keep it in a variable.
- string
- the name of the train at the platform
See also station.set_train_name()
station.set_train_name(name)
Renames the train standing at the station. Every player sees the new name at once.
namestring- the new name, cut to 64 characters
local st = peripheral.find("station")
if st.train_present() then
st.set_train_name("Iron Express")
endSee also station.train_name()
Schedules
A train follows its schedule, the one written in a Train Schedule: a list of stops, each with the conditions to wait for before leaving. A program reads it as a table, and gives a new one to the train at the platform. The table has the shape of Create's own schedule data, with its keys in snake_case (the same shape as Create's ComputerCraft peripheral):
local schedule = {
cyclic = true,
entries = {
{
instruction = {id = "create:destination", data = {text = "Iron Mine"}},
conditions = {
{ {id = "create:delay", data = {value = 20, time_unit = 1}} },
},
},
{
instruction = {id = "create:destination", data = {text = "Smeltery"}},
conditions = {
{ {id = "create:idle", data = {value = 5, time_unit = 1}} },
},
},
},
}cyclic:truestarts again from the first entry after the last one;falsestops the train after it.entries: the steps, in order. Each one has aninstruction,{id =, data =}.conditions, for a destination: a list of groups of conditions. The train leaves as soon as one group is complete, and a group is complete when its conditions are met, one after the other. Create's screen shows each group as a column. Even a single condition sits in its own group:{ {condition} }. A stop without any condition keeps the train there for good.
The usual instructions and conditions:
| id | what it does | data |
|---|---|---|
create:destination | go to a station (* allowed in the name) | text: the name of the station |
create:rename | change the title of the schedule, the text displays show | text: the new title |
create:throttle | limit the top speed of the train | value: percent |
create:delay | condition: wait some time | value, and time_unit: 0 ticks, 1 seconds, 2 minutes |
create:idle | condition: the cargo has not moved for some time | value, time_unit |
create:powered | condition: the station receives redstone | none |
For any other, set it up in a Train Schedule in game, give it to a train, and look at what schedule() returns.
station.has_schedule()
Whether the train standing at the station follows a schedule.
- boolean
- true when the train at the platform has a schedule
local st = peripheral.find("station")
if st.train_present() and not st.has_schedule() then
print(st.train_name() .. " has no schedule")
endA train keeps its schedule once it is done with it (a schedule that is not cyclic stops the train, it does not go away).
See also station.schedule()
The schedule of the train standing at the station. Stops the program with train doesn't have a schedule when it has none.
- table
- the schedule of the train at the platform, shaped as above
Listing the stops:
local st = peripheral.find("station")
local s = st.schedule()
for i, entry in ipairs(s.entries) do
if entry.instruction.id == "create:destination" then
print(i .. ". " .. entry.instruction.data.text)
end
endGives a schedule to the train standing at the station. It replaces the one the train had, and the train starts it at once, from its first entry (or from the entry a progress key names, counted from 0, when the table has one).
scheduletable- a schedule shaped like the one
schedule()returns
The table may hold 4096 values at most, 16 levels deep. Without any entry the program stops with a schedule needs at least one entry. When the train had no schedule, the new one behaves like a schedule handed out by a station: a player who takes it off the train gets no Train Schedule back.
local st = peripheral.find("station")
local s = st.schedule()
s.cyclic = false -- one more trip, then stop
st.set_schedule(s)See also station.schedule()
Where can the train go?
Before sending a train, a program can ask whether it can get there at all: a station renamed, a track broken or a signal missing is enough to cut a line. These two methods look at every station of the train's track network whose name matches a filter:
*stands for any text, none included:"Mine*"matchesMine,Mine 2andMine North;?stands for exactly one character:"Platform ?"matchesPlatform 1toPlatform 9, notPlatform 10;- everything else must be exactly the same, capitals included. A filter has 64 characters at most.
Both answer for the train standing at the platform (there is no train present otherwise), the way Create's navigation would drive it: a train that can only go forwards may not reach a station that another train could. The search through the network is what makes them costly.
Whether the train standing at the station can get to a station matching name.
namestring- the name of a station,
*and?allowed
- boolean
- true when the train at the platform can reach a station of that name
local st = peripheral.find("station")
if st.can_reach("Iron Mine") then
print("the line to the mine is open")
else
print("no way to the mine from here")
endSee also station.distance_to()
How far the train standing at the station would travel to a station matching name.
namestring- the name of a station,
*and?allowed
- number|nil
- the distance in blocks along the track, rounded to 0.01, or nil when no such station can be reached
When several stations match, it is the distance to the one the navigation would choose, usually the nearest.
local st = peripheral.find("station")
local d = st.distance_to("Quarry*")
if d then
print("nearest quarry: " .. math.floor(d) .. " blocks")
endSee also station.can_reach()
Assembling trains
In assembly mode, a station turns the bogeys and glued blocks standing on its track into a train, and takes a train apart into blocks again. These methods do what the buttons of the station's screen do.
Like a player clicking those buttons, a station changes once per tick at most: a second set_assembly_mode, assemble or disassemble on the same station in the same tick stops the program with the station already changed this tick: sleep(0) waits for the next. Put a sleep(0) between two of them.
station.assembling()
Whether the station is in assembly mode. While it is, no scheduled train comes to it.
- boolean
- true while the station is in assembly mode
See also station.set_assembly_mode()
Enters or leaves assembly mode.
onboolean- true to enter assembly mode, false to leave it
Entering it with a train at the platform takes that train apart, like disassemble(). Asking for the mode the station is already in stops the program, with failed to enter assembly mode or failed to exit assembly mode: ask assembling() first.
See also station.assembling() station.assemble()
Assembles the train standing in the station's assembly zone, then leaves assembly mode: the new train is ready to go.
local st = peripheral.find("station")
if not st.assembling() then
st.set_assembly_mode(true)
sleep(0) -- one change per tick
end
st.assemble()
print(st.train_name() .. " is ready")Outside assembly mode it stops the program with station must be in assembly mode. When Create refuses to assemble (no Train Controls, a bogey missing...), it stops with failed to assemble train: the station's screen tells why.
Takes the train standing at the platform apart, into blocks on the track.
The station is then in assembly mode: call assemble() to build the train again, or set_assembly_mode(false) to open the station to trains. A Train Schedule a player gave to the train drops at the station. Errors: station must not be in assembly mode, there is no train present, and could not disassemble train when Create cannot do it.
See also station.assemble()
Train Signal
A Train Signal splits the track into sections, and lets a train into the section after it only when that section is free. A program reads its light, sees which trains hold the section, and can keep it at red.
sig.state()
The light the signal shows now.
- string
"red","yellow","green"or"invalid"
"yellow" only shows on a cross signal: some ways out of its section are free, others are not. "invalid" (the light blinks in game) means the signal is not on a track, or does not separate two sections, as on a loop with no other signal.
local sig = peripheral.find("signal")
if sig.state() == "red" then
print("the section ahead is taken")
endSee also signal.blocking_trains()
sig.forced_red()
Whether the signal is held at red, whatever the trains do.
- boolean
- true when the signal is forced to red, by a program or by redstone
See also signal.set_forced_red()
sig.set_forced_red(red)
Holds the signal at red, as a redstone signal on it would: trains wait before it.
redboolean- true to hold the signal at red, false to let it go
The change is made at the end of the program's tick. Redstone keeps the last word: when a block next to the signal changes, the signal follows its redstone input again. Leave a signal that a program drives without redstone around it.
local sig = peripheral.find("signal")
sig.set_forced_red(true) -- works on the line
sleep(120)
sig.set_forced_red(false)See also signal.forced_red()
sig.blocking_trains()
The trains in the section after the signal: the ones a train waiting at a red light waits for.
- table
- the names of the trains in the section the signal guards, an empty list when it is free
local sig = peripheral.find("signal")
for _, name in ipairs(sig.blocking_trains()) do
print(name .. " is in the way")
endSee also signal.state()
sig.signal_type()
The kind of signal:
- string
"entry_signal"or"cross_signal"
"entry_signal"lets a train in when the section is free;"cross_signal"lets a train in only when it can go all the way through the section, so that no train stops in the middle of a junction or a crossing.
See also signal.cycle_signal_type()
sig.cycle_signal_type()
Switches the signal to the other kind, as Create's Wrench does.
See also signal.signal_type()
Train Observer
A Train Observer watches one point of the track and gives a redstone signal while a train goes past. When an item is set in its filter, only trains carrying that cargo count.
obs.train_passing()
Whether a train is passing now. The observer stays on for a few ticks after the last carriage, so asking every 0.2 second misses no train.
- boolean
- true while a train goes past the observed point
See also track_observer.passing_train()
obs.passing_train()
The name of the train going past, or nil when none is.
- string|nil
- the name of the train passing now, or nil
Counting the trains of a line:
local obs = peripheral.find("track_observer")
local count = 0
local was = false
while true do
local now = obs.train_passing()
if now and not was then
count = count + 1
print(count .. ": " .. (obs.passing_train() or "a train"))
end
was = now
sleep(0.2)
endSee also track_observer.train_passing()
Examples
An arrivals board
A computer on the platform, with no monitor: its own screen announces each train. The time comes from the world's clock (os.day_time()).
local st = peripheral.find("station")
local last_train = "The train"
local function clock()
local t = (os.day_time() + 6000) % 24000
return string.format("%02d:%02d", t // 1000, t % 1000 * 60 // 1000)
end
local function situation()
if st.train_present() then return "present" end
if st.train_imminent() then return "imminent" end
if st.train_enroute() then return "enroute" end
return "empty"
end
term.clear()
term.set_cursor(1, 1)
print(st.name())
local was = "empty"
while true do
local now = situation()
if now ~= was then
if was == "present" then
print(clock() .. " " .. last_train .. " has left")
end
if now == "present" then
last_train = st.train_name()
print(clock() .. " " .. last_train .. " at the platform")
elseif now == "imminent" then
print(clock() .. " arriving: stand back")
elseif now == "enroute" then
print(clock() .. " a train is on its way")
end
was = now
end
sleep(0.5)
endThe name of the train is kept in last_train while it stands at the platform: once it has left, train_name() would stop the program.
A depot that only sends trains where they can go
Each train that stops at the depot gets a new schedule: one trip to the next mine in turn, then back to the depot. A mine whose line is cut is skipped, and when no mine can be reached, the train waits at the depot until one can.
local st = peripheral.find("station")
local DEPOT = st.name()
local MINES = {"Iron Mine", "Coal Mine", "Quarry*"}
local turn = 1
-- one stop: go to a station, wait there some seconds
local function stop(station, seconds)
return {
instruction = {id = "create:destination", data = {text = station}},
conditions = {
{ {id = "create:delay", data = {value = seconds, time_unit = 1}} },
},
}
end
-- the next mine the train at the platform can reach, or nil
local function next_mine()
for i = 1, #MINES do
local mine = MINES[turn]
turn = turn % #MINES + 1
if st.can_reach(mine) then
return mine
end
end
return nil
end
local served = false
while true do
if not st.train_present() then
served = false
elseif not served then
local mine = next_mine()
if mine then
local blocks = math.floor(st.distance_to(mine))
st.set_schedule({cyclic = false, entries = {stop(mine, 30), stop(DEPOT, 5)}})
print(st.train_name() .. " leaves for " .. mine .. ", " .. blocks .. " blocks")
served = true
end
end
sleep(2)
endserved remembers that the train at the platform has its orders, so it gets them once. When it comes back from the mine, it stops at the depot again and the program sends it to the next mine. A train with no mine in reach is asked again every two seconds: a can_reach costs 64 instructions, a third of a tick of a Tube Computer, so do not ask at every tick.