TaleOS logo TaleOSProgrammable computers for Hytale
Download v0.1.0 · Early Access
Official documentation · v0.1.0

TaleOS Wiki

Build, program and automate your Hytale world with Lua computers, mobile terminals, robots, displays and shared storage.

TaleOS is a server-side Hytale mod that adds programmable Computers, Turtles, Pocket Computers, multi-block Monitors, PenDrives, USB drives, NAS units, wireless Modems and Speakers. Players only need the vanilla client.

This wiki documents the features that are actually present in TaleOS 0.1.0. It is inspired by the usefulness of computer-mod documentation, not a promise of drop-in compatibility with other mods.

TaleOS programmable computer
Server-side only. Install the JAR in Server/mods/ and restart. Assets are delivered by the server; players do not install a separate client mod.

How to use this wiki

Use the sidebar to browse by topic, or press / and search for a function such as turtle.dig, fs.write or term.redirect. Code blocks include a copy button. APIs list Lua arguments and return values from left to right.

Guide

Getting started

From a copper bar to your first running program in a few minutes.

1. Install the mod

  1. Download taleos-0.1.0.jar from CurseForge.
  2. Copy it into the Hytale server's Server/mods/ directory.
  3. Restart the server. TaleOS requires Java 25 and is built against Hytale server 0.6.2.
  4. Join with the vanilla client. No client installation is required.

2. Craft and place a Computer

At a Workbench → Survival, combine 4 copper bars + 4 Deadwood planks. Place the Computer and press F to open its terminal.

After booting, you should see TaleOS v0.1 (TaleOS) and the tale> prompt.

3. Try the shell

id
help
echo Hello, Hytale!
lua

In the Lua REPL, enter:

print("Hello from Lua!")
tale.getComputerID()
exit

The REPL first tries to evaluate your input as an expression; if that fails, it runs it as a statement. It prints returned non-nil values. Type exit to return to the shell.

4. Create a program

Run edit hello.lua. Enter this program, save it, and use Save and run (or save and type hello):

local id = os.getComputerID()
print("Hello from computer #" .. id)

for i = 1, 3 do
  print("Count: " .. i)
  os.sleep(1)
end

The editor offers save, save-and-run, exit and Tab completion. The terminal also supports completion; the running-program Stop button terminates a program without killing the shell.

5. Run automatically at boot

Create startup.lua in the computer's root directory. TaleOS executes it before the shell, both after a server restart and after reboot. Errors are printed and the shell still starts.

-- startup.lua
print("Computer #" .. os.getComputerID() .. " is ready")
Next step: place a Monitor next to the Computer and run reloj, or craft a Turtle and try go forward 3.
Reference

Crafting recipes

All TaleOS recipes use Workbench → Survival and produce one item.

ItemIngredientsCrafting timePurpose
TaleOS_Computer4 copper bars + 4 Deadwood planks5 sFixed programmable Lua computer.
TaleOS_Turtle6 copper bars + 2 Deadwood planks5 sMobile robot with inventory and two upgrade slots.
TaleOS_Monitor2 copper bars + 2 Deadwood planks3 sColored text and pixel display; connects into rectangles.
TaleOS_PocketComputer1 TaleOS Computer + 2 copper bars5 sPortable terminal with persistent identity and files.
TaleOS_PenDrive2 copper bars + 1 Deadwood plank3 sPortable 1 MiB storage medium.
TaleOS_USB_Drive4 copper bars + 1 Deadwood plank4 sSingle-PenDrive reader for adjacent machines.
TaleOS_NAS6 copper bars + 2 Deadwood planks6 sEight-PenDrive network storage unit.
TaleOS_Modem4 copper bars + 1 Deadwood plank3 sWireless rednet/GPS peripheral or Turtle upgrade.
TaleOS_Speaker2 copper bars + 2 Deadwood planks3 sPositional SoundEvent playback or Turtle upgrade.
TaleOS_Crafting_Upgrade2 copper bars + 2 Deadwood planks3 sEnables Turtle crafting.

Tools and weapons

TaleOS does not add special tools. Use ordinary Hytale pickaxes, shovels, weapons and other registered tools in Turtle upgrade slots. A tool appropriate for the target block is required for digging. Tools and weapons do not lose durability when used by a Turtle.

Stacking and identity

Computers, Turtles, Pocket Computers and PenDrives have persistent identity and do not stack. Monitors, USB drives, NAS units, Modems and Speakers can stack up to 25 before identity is attached. Breaking a Computer or Turtle creates an item labeled with its ID; placing it again preserves its files and state.

Guide

Computers & files

Terminals, persistent storage and the Lua sandbox.

Using the terminal

Press F on a Computer or Turtle. Type at the prompt and press Enter. The output area keeps a scrollable transcript. Completion is available with Tab in the terminal and editor. Use Stop to terminate the currently running program; the shell itself remains available.

The native Lua terminal is a 51×19 character grid. It supports cursor positioning, scrolling, independent foreground/background colors per cell, blit, clipped windows and redirection to Monitors.

Persistent files

Each machine has a jailed filesystem with a 1 MiB quota. Computer files live in mods/TaleOS/computers/<id>/. The read-only rom/ mount contains TaleOS and built-in programs. PenDrives have independent 1 MiB quotas.

Shell paths are relative to the current directory. Absolute paths start with /. Both . and .. are normalized, and attempts to escape the computer's filesystem are rejected.

Identity when broken

Breaking a Computer or Turtle produces an item labeled Computer #ID or Turtle #ID. Its ID, files, inventory and equipped upgrades are retained when placed again. These items do not stack, preventing two placed machines from sharing one live disk.

Lua sandbox and budgets

TaleOS uses LuaJ with a per-tick instruction budget (200,000 bytecodes by default). Infinite loops, including loops hidden in pcall, xpcall or child coroutines, are killed without stopping the server.

Scripts cannot access Java, host files, host I/O or native process execution. luajava, io, debug, package, require, dofile and loadfile are unavailable. The standard os table is replaced by a safe TaleOS version.

Loaded chunks

World reads and Turtle actions require loaded chunks. With no players nearby, a chunk may unload and block reads return no block. Server owners can use vanilla keep-loaded settings for automation areas; see Administration.

Reference

Shell commands

Built-in commands available at the tale> prompt.

Arguments separated by spaces are passed to programs as strings. Your own name.lua in the current directory can be run with name. Built-in ROM programs take precedence when names collide.

CommandDescription
helpShow command and API help.
idPrint the persistent computer ID.
ls [directory]List a directory; defaults to the current directory. Directory names end in /.
cd [directory]Change directory. With no argument, go to /. Supports .. and absolute paths. A numeric ID is a shortcut for nas/<id> when applicable.
cat <file>Print a UTF-8 file.
mkdir <directory>Create a directory, including required parents.
rm <path>Delete a file or directory. ROM mounts cannot be changed.
echo [text…]Print its arguments.
clearClear the terminal.
luaOpen the Lua mini-REPL; use exit to leave.
edit <file>Open the visual editor. Save, save-and-run or exit.
wget <url> [file]Download a text resource over HTTP. If omitted, the filename comes from the URL. Never executes the download automatically.
relojDisplay a centered HH:MM:SS clock on an adjacent Monitor. Stop it with the Stop button.
gps [locate]Locate this machine using at least four GPS references.
gps host <x> <y> <z>Serve a GPS reference at fixed coordinates until stopped.
rebootRestart TaleOS and run startup.lua again.
shutdownShut down this machine.

Turtle-only commands

CommandDescription
go <direction> [count]Move or turn. Directions: forward, back, up, down, left, right. Aliases: fd, bk, dn, lt, rt, forwards. Count defaults to 1.
turn <left|right> [count]Turn in place without moving.
tunnel [length]Dig a 3×3 cross-section for the given number of sections; defaults to 1. Uses the appropriate equipped tool.
excavate <length>Dig and move through a 1×3 face for a positive integer length.
equip <left|right>Swap the selected inventory item into an upgrade slot.
unequip <left|right>Move an upgrade into the selected empty inventory slot.

Examples

mkdir programs
cd programs
edit counter.lua
counter
cd ..
ls /
cat /startup.lua
Guide

Your first programs

Learn the TaleOS style: terminal output, events, files and cooperative tasks.

Output and arguments

print(...) converts values to text, separates them with tabs and writes a newline. A program invoked as demo one 2 receives "one" and "2" through ....

local name, countText = ...
countText = tonumber(countText) or 1
print("Hello, " .. (name or "world"))
for i = 1, countText do
  print("Iteration " .. i)
end

Errors and action results

Filesystem errors raise Lua errors and can be caught with pcall. Turtle actions usually return true or false, reason. Peripheral method errors may also raise Lua errors; handle them when hardware may be absent.

local ok, reason = turtle and turtle.forward()
if turtle and not ok then
  print("Blocked: " .. tostring(reason))
end

local wrote, err = pcall(fs.write, "status.txt", "ready")
if not wrote then print(err) end

Waiting for events

Use os.pullEvent() to wait for any event or os.pullEvent("timer") to accept only one kind. os.pullEventRaw does not filter. See the event reference for exact fields.

local timer = os.startTimer(2)
while true do
  local event, id = os.pullEvent()
  if event == "timer" and id == timer then
    print("Two seconds elapsed")
    break
  end
end

Two tasks at once

parallel.waitForAny returns when one function finishes; waitForAll waits for every function. Tasks cooperate through events and yields rather than preemptive threads.

local function ticker()
  while true do
    os.sleep(1)
    print("tick")
  end
end

local function listener()
  local event, side = os.pullEvent("peripheral")
  print("Attached on " .. side)
end

parallel.waitForAny(ticker, listener)

Startup and persistence

Save durable state with fs.write or fs.append; do not rely only on Lua variables. Put unattended behavior in startup.lua. A startup program that loops forever still receives events and can be stopped from the terminal.

Lua API

fs · Filesystem

Read and manage files inside a machine or mounted PenDrive.

Paths are strings. The shell-wrapped API resolves relative paths against the current directory; / selects the root. Missing files generally return nil on reads, while invalid writes, missing directories, read-only mounts and quota errors raise Lua errors prefixed with fs:.

FunctionReturnsBehavior
fs.exists(path)booleanWhether a file or directory exists.
fs.isDir(path)booleanWhether the path exists and is a directory.
fs.list(path)tableA numerically indexed array of child names. Names of directories include a trailing /.
fs.read(path)string | nilRead the entire UTF-8 file, or nil if it does not exist.
fs.write(path, contents)trueCreate or replace a file. Both arguments are strings. Parent directories must exist.
fs.append(path, contents)trueAppend text to a file, creating it if necessary.
fs.mkdir(path)trueCreate a directory and required parents.
fs.delete(path)trueDelete a file or directory. ROM and active mount protections apply.
fs.getSize(path)numberFile size in bytes; directories report 0.
fs.getCapacity([path])numberTotal quota in bytes for the current filesystem or the filesystem containing path.
fs.getFreeSpace([path])numberRemaining bytes in that filesystem's quota.

Example: append a log

fs.mkdir("logs")
fs.append("logs/activity.txt", os.date() .. " started\n")

if fs.exists("logs/activity.txt") then
  print(fs.read("logs/activity.txt"))
  print(fs.getSize("logs/activity.txt") .. " bytes")
end

print(fs.getFreeSpace("logs") .. " bytes free")

Mounted media

USB media appear as usb, usb2, etc. NAS media appear as nas/<PenDriveID>. Use fs.getCapacity("nas/22") or fs.getFreeSpace("usb") to query a specific medium. Removing a PenDrive removes its mount and subsequent accesses fail rather than reading stale data.

Lua API

os · System & events

Identity, timers, sleep, events and computer lifecycle.

FunctionReturnsBehavior
os.getComputerID()numberThe persistent numeric ID of this machine.
os.startTimer(seconds)numberStart a timer and return its ID. A timer event fires when it expires.
os.cancelTimer(id)nilCancel a pending timer. Unknown or completed IDs are ignored.
os.sleep([seconds])nilWait using a timer; omitted duration means 0. Other events are consumed while waiting for this timer.
os.queueEvent(name, ...)booleanQueue a custom event. Returns false when the bounded 256-event queue is full.
os.pullEvent([filter])event fields…Wait for the next event, skipping events that do not match the optional name.
os.pullEventRaw()event fields…Wait for the next event without filtering. Used internally by parallel, rednet and GPS.
os.reboot()does not returnRestart the machine and run startup again.
os.shutdown()does not returnShut down the machine.
os.clock()numberSafe LuaJ clock value, intended for measuring elapsed execution time.
os.time([table])numberLua time function based on real/server time, not Hytale's day cycle.
os.date([format[, time]])string | tableFormat real/server time. os.date("*t") returns fields such as hour, min and sec.
os.openEditor(path)trueRequest the visual editor for the player using the terminal. Normally invoked with the edit program.
Two kinds of sleep: os.sleep(seconds) uses real timers. tale.sleep(ticks) pauses for server ticks (20 ticks ≈ one second at normal tick rate).

One-shot and repeating timers

local id = os.startTimer(5)
print("Timer #" .. id)

local event, fired = os.pullEvent("timer")
if fired == id then print("Five seconds elapsed") end

os.queueEvent("my_event", 42, { ready = true })
local name, value, data = os.pullEvent("my_event")
print(name, value, data.ready)
Lua reference

Event reference

Values returned by os.pullEvent and os.pullEventRaw.

The first return value is always the event name. Subsequent values depend on the event. Events are delivered in FIFO order and the queue is bounded to 256 entries.

EventAdditional fieldsWhen it fires
stdinlineA player or the admin command submits a line to the shell. Primarily used internally by TaleOS.
timertimerIDA timer from os.startTimer expires.
peripheralnameA peripheral becomes available on a side or alias.
peripheral_detachnameA peripheral is removed or becomes unavailable.
monitor_resizenameA connected Monitor structure changes size or is split.
modem_messageside, channel, replyChannel, payload, distanceAn open channel receives a wireless message on a loaded receiver in the same world.
usbsideA PenDrive is mounted. Side identifies the USB drive, or nas for NAS-backed media.
usb_ejectsideA mounted PenDrive is ejected or its mount disappears.
custom namecustom values…Your program calls os.queueEvent(name, ...). Strings, numbers, booleans, tables and nil are supported.

Reading a modem message

local event, side, channel, replyTo, payload, distance =
  os.pullEvent("modem_message")

print("Received on " .. side .. ", channel " .. channel)
print("Reply channel: " .. replyTo)
print("Distance: " .. distance)
Not currently generated: mouse, keyboard, monitor-touch, redstone, disk/floppy, websocket, alarm and multishell events are not part of TaleOS 0.1.0.
Lua API

term · Terminal

Write text, position the cursor, scroll and draw colored character cells.

Coordinates are one-based: 1,1 is the upper-left cell. Terminal color arguments accept either a color name or a single power-of-two value from colors. print writes to the current redirect target.

FunctionReturnsBehavior
term.write(value)nilWrite text at the cursor and advance it, wrapping and scrolling as needed.
term.blit(text, fg, bg)nilWrite equal-length strings of text and hexadecimal foreground/background color digits.
term.clear()nilFill the terminal with spaces using current colors and move the cursor to 1,1.
term.clearLine()nilClear the current row and move the cursor to its first column.
term.scroll(lines)nilScroll by an integer amount; positive values move content upward.
term.getSize()width, heightDimensions in character cells. The native terminal is 51×19.
term.setCursorPos(x, y)nilSet the one-based cursor position.
term.getCursorPos()x, yRead the current position.
term.setCursorBlink(boolean)nilEnable or disable cursor blinking.
term.getCursorBlink()booleanRead the cursor blink state.
term.setTextColor(color)nilSet foreground for subsequent writes.
term.getTextColor()numberCurrent foreground as a power-of-two color value.
term.setBackgroundColor(color)nilSet background for subsequent writes.
term.getBackgroundColor()numberCurrent background as a power-of-two color value.
term.isColor()booleanWhether this terminal supports color.
term.native()tableThe original native terminal object.
term.current()tableThe current redirect target.
term.redirect(target)previous targetSend terminal methods and print output to a terminal, window or Monitor. Restore with the returned object.

Using blit

Each character in the color strings is a hexadecimal digit representing a palette index: 0 is black, f is white, 5 is lime. All three strings must have the same length.

term.clear()
term.setCursorPos(1, 1)
term.blit("TaleOS", "555555", "000000")
term.setCursorPos(1, 3)
term.setTextColor(colors.lime)
term.setBackgroundColor(colors.black)
term.write("Colored terminal text")

Always restore redirects

local previous = term.redirect(term.native())
-- Work with the selected target here.
term.redirect(previous)
Lua API

colors & colours

The shared 16-color palette used by terminals and Monitors.

colors and colours are the same table. Each named value is a power of two. Terminal functions accept these values; Monitor pixel functions use the equivalent names.

ColorValueHexBlit digit
colors.black1#14161c0
colors.navy2#2a3a6a1
colors.blue4#3a5fce2
colors.cyan8#3cb5b53
colors.green16#3f9e4d4
colors.lime32#7cc0505
colors.yellow64#e0c0406
colors.orange128#d8832c7
colors.red256#cc40408
colors.pink512#e08fb09
colors.magenta1024#b84fb8a
colors.purple2048#7a4fc0b
colors.brown4096#7a5a38c
colors.gray8192#707880d
colors.silver16384#b8c0c8e
colors.white32768#e8e8e8f
Lua API

window · Clipped surfaces

Create independent terminal regions inside a parent terminal or Monitor.

A window has its own cursor, colors, visibility and dimensions. Drawing is clipped to the window and does not move or scroll its parent's cursor.

FunctionReturnsBehavior
window.create(parent, x, y, width, height[, visible])windowCreate a one-based child region. Width and height must be positive; visibility defaults to true.
win.setVisible(boolean)nilShow or hide the window without destroying its cursor or settings.
win.isVisible()booleanRead visibility.
win.reposition(x, y[, width[, height]])nilMove and optionally resize the window, resetting its cursor to 1,1.

Windows also implement the terminal methods: write, blit, clear, clearLine, scroll, getSize, setCursorPos, getCursorPos, setCursorBlink, getCursorBlink, setTextColor, getTextColor, setBackgroundColor, getBackgroundColor and isColor.

Two independent panels

term.clear()
local left = window.create(term.current(), 1, 1, 24, 8, true)
local right = window.create(term.current(), 27, 1, 24, 8, true)

left.setTextColor(colors.lime)
left.write("Status: online")
right.setTextColor(colors.yellow)
right.write("Storage: 80%")

local previous = term.redirect(left)
print("This appears in the left panel")
term.redirect(previous)
Lua API

parallel · Cooperative tasks

Run multiple event-driven functions without blocking the computer.

FunctionReturnsBehavior
parallel.waitForAny(...)numberRun all supplied functions and return the one-based index of the first that finishes. Other tasks stop being resumed.
parallel.waitForAll(...)nilRun all supplied functions until every one has finished.

Functions execute cooperatively as coroutines. Long-running tasks should wait for events, timers or world calls. An error in one task is propagated to the caller.

local function listen()
  while true do
    local event, side = os.pullEvent("peripheral")
    print("Peripheral attached: " .. side)
  end
end

local function heartbeat()
  while true do
    print("alive")
    os.sleep(5)
  end
end

parallel.waitForAll(listen, heartbeat)
Lua API

shell · Current directory & paths

Resolve paths the same way as the TaleOS shell.

FunctionReturnsBehavior
shell.dir()stringCurrent directory relative to root; the root is an empty string.
shell.resolve(path)stringResolve relative or absolute paths and normalize . and ...
shell.setDir(path)nilSet the current directory after resolving it. This does not itself verify that the directory exists.
print("Current directory: /" .. shell.dir())
print(shell.resolve("../shared/data.txt"))
print(shell.resolve("/nas/22/program.lua"))
Lua API

tale · Hytale bridge

TaleOS-specific world access, localization and tick-based control.

Relative coordinates are offsets from the machine: 0,-1,0 is the block immediately below. World calls run safely on the server tick thread and are limited to 32 world calls per machine per tick.

FunctionReturnsBehavior
tale.getBlock([dx[, dy[, dz]]])string | nilHytale block ID at the relative position. Defaults to 0,-1,0. Nil means air or an unloaded/inaccessible chunk.
tale.detect([dx[, dy[, dz]]])booleanTrue when a block ID is present at the relative position.
tale.chat(message)trueBroadcast a message to the machine's world chat.
tale.time()numberReal epoch seconds. This is not the Hytale world-day time.
tale.sleep([ticks])nilPark the machine for at least one server tick; defaults to 1. Use 20 for roughly one second.
tale.monitor([dx, dy, dz])Monitor | nilGet a Monitor object. With no offset, search the six neighboring faces in right, left, top, bottom, front, back order.
tale.getComputerID()numberLow-level equivalent of os.getComputerID.
tale.setPrompt(text)nilChange the prompt displayed next to the terminal input.
tale.language()stringConfigured UI language: en or es.
tale.tr(text)stringTranslate a known built-in string; unknown text is returned unchanged.
tale.pullEvent([filter])event fields…Low-level event wait behind the safer os.pullEvent functions.
tale.reboot() / tale.shutdown()does not returnLow-level lifecycle functions behind the os equivalents.

Inspect the ground

local id = tale.getBlock(0, -1, 0)
if id then
  print("Block below: " .. id)
else
  print("Air or unloaded chunk")
end

if tale.detect(1, 0, 0) then
  tale.chat("There is a block beside computer #" .. os.getComputerID())
end
Lua API

http · Safe downloads

Asynchronous HTTP GET requests without blocking server ticks.

The API exists only when HTTP is enabled in mods/TaleOS/config.json. TaleOS 0.1.0 supports text GET requests, not POST, WebSockets, proxies or Git cloning.

FunctionReturnsBehavior
http.get(url)response | nil, errorPerform a GET request, following validated redirects. On success returns a response object; on failure returns nil and a reason.
response.readAll()stringRead the complete response body.
response.getResponseCode()number, stringHTTP status code and reason phrase.
response.close()nilClose the response. Call after reading it.

Download and save a Lua file

local response, err = http.get(
  "https://raw.githubusercontent.com/user/repo/main/program.lua")
if not response then error(err) end

local code, reason = response.getResponseCode()
if code < 200 or code >= 300 then
  response.close()
  error("HTTP " .. code .. " " .. reason)
end

local body = response.readAll()
response.close()
fs.write("program.lua", body)
print("Saved " .. #body .. " bytes")

Or use the shell: wget https://raw.githubusercontent.com/user/repo/main/program.lua program.lua, then run program.

Security: private, loopback, LAN, link-local and reserved addresses are denied by default. Timeouts, response sizes, redirects, per-computer concurrency and global bandwidth are limited. See Configuration.
Hardware guide + API

Monitors

Draw colored text and pixels directly on blocks in the world.

Place a Monitor beside a Computer or Turtle. Monitors are output devices: they do not open an interface with F. Use tale.monitor(), peripheral.wrap("side") or peripheral.find("monitor") from Lua.

Dimensions and multi-block displays

Each Monitor provides 40×24 pixels and a 10×4 character terminal. Compatible Monitors with the same facing join into rectangles up to 8×8 blocks: 320×192 pixels and 80×32 terminal cells. Every block renders its own crop of the shared canvas.

Resizing preserves compatible content. Splitting a structure creates independent canvases and queues monitor_resize for affected attachments.

Getting a Monitor

local mon = tale.monitor() -- search all six neighbors
if not mon then error("No adjacent Monitor") end

-- Or by peripheral side:
local bySide = peripheral.wrap("right")
-- Or all attached Monitors:
local first, second = peripheral.find("monitor")

Pixel methods

Pixel coordinates are one-based. Pixel colors are palette names such as "black", "lime" or "white". The built-in microfont uses 3×5 glyphs.

FunctionReturnsBehavior
mon.getSize()width, heightSize in pixels, including all connected blocks.
mon.getTerminalSize()width, heightSize in 4×6-pixel character cells.
mon.setPixel(x, y, colorName)nilSet one pixel.
mon.getPixel(x, y)stringRead a pixel's palette name.
mon.fill(x, y, width, height, colorName)nilFill a rectangle whose upper-left corner is x,y.
mon.clear([colorName])nilWith a color, clear the pixel canvas. Without arguments, clear the character terminal.
mon.write(x, y, text[, colorName])nilDraw microfont text at pixel coordinates; default color is white.

Terminal methods

Monitors also support write(text), blit, clearLine, scroll, setCursorPos, getCursorPos, setCursorBlink, getCursorBlink, setTextColor, getTextColor, setBackgroundColor, getBackgroundColor and isColor. Their cursor and color operations use character cells, not pixels.

local mon = tale.monitor()
mon.clear("navy")
mon.fill(5, 5, 20, 3, "blue")
mon.write(5, 10, "TALEOS", "lime")

-- Use it as a normal terminal:
local previous = term.redirect(mon)
term.setTextColor(colors.white)
print("Hello on the Monitor")
term.redirect(previous)

Built-in clock

Place a Monitor adjacent to a Computer and run reloj. The program clears the canvas once per second, redraws all eight HH:MM:SS characters and sleeps for 20 ticks. Stop it from the terminal.

Hardware guide

Turtles

Mobile Lua computers that move, mine, fight, build and manage items.

Inventory and upgrades

Press F on a Turtle to combine its terminal, 4×4 inventory, your inventory and two upgrade slots. Lua slots are numbered 1–16, left-to-right and top-to-bottom. The selected slot is highlighted and defaults to slot 1.

The left and right upgrade slots accept ordinary tools and weapons, TaleOS_Modem, TaleOS_Speaker and TaleOS_Crafting_Upgrade. Equipped hardware appears on the 3D chassis and tools swing when used. Tools and weapons do not consume durability.

Equipping from Lua or the shell

-- Select a slot containing a pickaxe, then:
equip left

-- Select an empty slot to retrieve it:
unequip left
turtle.select(1)
local ok, reason = turtle.equipLeft()
if not ok then print(reason) end

turtle.select(2)
turtle.equipRight()

Action results

Most physical actions return true on success or false, reason on failure. Always check the result in unattended programs. Movement and turning are animated, and a Turtle retains its running program, ID, files, inventory and upgrades while moving.

for i = 1, 10 do
  while turtle.detect() do
    local ok, reason = turtle.dig("right")
    if not ok then error(reason) end
    tale.sleep(2)
  end
  local moved, reason = turtle.forward()
  if not moved then error(reason) end
end

Mining and combat

turtle.dig requires a suitable tool with sufficient gathering quality. Without a side argument it tries equipped tool slots; pass "left" or "right" to choose explicitly. Drops enter the Turtle inventory when possible.

turtle.attack uses an equipped weapon against a damageable target in the selected direction. It returns false when there is no valid target or a solid block obstructs the attack.

Crafting

Equip a Crafting Upgrade and arrange ingredients in the 16 inventory slots. turtle.craft([limit]) searches supported ordinary tier-0 Crafting recipes and creates up to limit crafts (default 64). Input and output stacks, including relevant metadata, are planned before changes are committed.

Useful built-ins

No fuel management needed. turtle.getFuelLevel() and turtle.getFuelLimit() return "unlimited"; refuel is unnecessary and reports that this Turtle does not use fuel.
Lua API

turtle API

The complete movement, world, inventory and upgrade interface.

This table exists only on Turtles. Unless noted otherwise, action functions return true or false, reason. Direction suffixes Up and Down select those faces; unsuffixed world actions use the front.

Movement and turning

FunctionReturnsBehavior
turtle.forward() / back()true | false, reasonMove one block horizontally in the corresponding direction.
turtle.up() / down()true | false, reasonMove one block vertically.
turtle.turnLeft() / turnRight()true | false, reasonRotate 90 degrees without changing position.

Detection, inspection and comparison

FunctionReturnsBehavior
turtle.detect[Up/Down]()booleanWhether a block is present.
turtle.inspect[Up/Down]()true, detail | false, reasonInspect a block. Detail currently contains name, the Hytale block ID.
turtle.compare[Up/Down]()booleanCompare the target block's ID with the block key of the selected item.
turtle.compareTo(slot)boolean | false, reasonCompare item IDs in the selected slot and another slot. Two empty slots compare equal.
local ok, info = turtle.inspectDown()
if ok then print("Standing on " .. info.name) end
if turtle.compareDown() then print("Selected item matches the ground") end

Inventory

FunctionReturnsBehavior
turtle.select(slot)true | false, reasonSelect a slot from 1 to 16.
turtle.getSelectedSlot()numberCurrent one-based selected slot.
turtle.getItemCount([slot])numberItems in the given or selected slot; empty/invalid slots report 0.
turtle.getItemSpace([slot])numberRemaining stack capacity; an empty valid slot reports 64.
turtle.getItemDetail([slot])table | nilFields: name, count, maxCount, and when applicable durability, maxDurability.
turtle.transferTo(slot[, count])true | false, reasonMove items from the selected slot to another slot. Count defaults to the whole stack.

Upgrades and fuel

FunctionReturnsBehavior
turtle.equipLeft() / equipRight()true | false, reasonSwap the selected item with the corresponding upgrade slot.
turtle.equip()true | false, reasonAlias for equipRight.
turtle.unequip()true | false, reasonMove the right upgrade into the selected empty slot.
turtle.getEquippedLeft() / getEquippedRight()table | nilEquipped item name/count and durability fields when applicable.
turtle.getFuelLevel() / getFuelLimit()"unlimited"Turtles do not require fuel.
turtle.refuel()false, reasonReports that refueling is unnecessary.

Digging, attacking, crafting and placing

FunctionReturnsBehavior
turtle.dig[Up/Down]([side])true | false, reasonBreak a block with an appropriate tool. Side is optional "left" or "right".
turtle.attack[Up/Down]([side])true | false, reasonAttack a damageable entity with an equipped weapon.
turtle.craft([limit])true | false, reasonCraft using inventory ingredients and a Crafting Upgrade. Limit is 1–64; default 64.
turtle.place[Up/Down]()true | false, reasonPlace the selected item if it is a block and the destination is empty.

World item transfer

FunctionReturnsBehavior
turtle.suck[Up/Down]([count])true | false, reasonPull items from an adjacent container or collect nearby dropped items into inventory.
turtle.drop[Up/Down]([count])true | false, reasonPush selected items into an adjacent container or drop them into the world. Count defaults to the whole stack.

Small builder example

for i = 1, 8 do
  if turtle.getItemCount() == 0 then
    error("Selected slot is empty")
  end
  local ok, reason = turtle.placeDown()
  if not ok then error(reason) end
  ok, reason = turtle.forward()
  if not ok then error(reason) end
end
Hardware guide

PenDrives, USB & NAS

Portable files, shared programs and removable persistent storage.

PenDrives

Each PenDrive has a persistent numeric ID, an optional label and an independent 1 MiB filesystem. Its ID appears as PenDrive #ID in storage interfaces. PenDrives do not stack and cannot be inserted in two places at once.

USB drives

Insert one PenDrive into a USB Drive adjacent to a Computer or Turtle. It mounts at usb, usb2, etc., depending on how many drives are attached. The usb_drive peripheral can inspect, rename, locate and eject media.

local drive = peripheral.find("usb_drive")
if drive and drive.isPenDrivePresent() then
  print("Drive #" .. drive.getPenDriveID())
  print("Mounted at " .. drive.getMountPath())
  print(fs.read(drive.getMountPath() .. "/readme.txt"))
end

usb_drive methods

MethodReturnsBehavior
isPenDrivePresent()booleanWhether media is inserted.
getPenDriveID()number | nilPersistent medium ID.
getPenDriveLabel()stringCurrent label; requires media.
setPenDriveLabel([label])trueSet or clear the label; requires media.
getMountPath()string | nilPath visible to this computer; requires media.
eject()booleanSafely eject the PenDrive and remove its mount.

Stackable NAS units

Each NAS holds 8 PenDrives. Face-connected NAS units form one storage component. Every running Computer, Turtle or Pocket Computer within a spherical 16-block range of that component receives mounts at nas/<PenDriveID>.

All connected machines see the same live files: a program saved to nas/22/startup.lua can be read or executed by another machine with access to PenDrive 22. Removing media or moving out of range unmounts it; stale mounts are removed automatically.

ls nas
cd 22          -- shortcut for cd nas/22
ls
cat LEEME_NAS.txt
cd /
cat nas/22/shared-program.lua

Run a mounted program from the shell by using a path that resolves to its Lua file, or load its source explicitly in your own program. For portable startup logic, copy the desired program to the machine's own startup.lua.

Media events

usb and usb_eject fire with a side name when media mounts or unmounts. NAS media use nas as the event side. See Events.

Hardware guide

Pocket Computers

A mobile TaleOS terminal with persistent files and nearby NAS access.

Craft with one Computer and two copper bars. Use the Pocket Computer item to open the mobile terminal. It has the same shell, Lua APIs, editor, filesystem and 1 MiB local quota as other machines.

Persistent identity

A Pocket Computer keeps its ID and files while carried. Duplicate stacks cannot start multiple sessions on the same disk, protecting the filesystem from concurrent writers.

When execution suspends

Execution and NAS access suspend when the item is dropped, its holder disconnects or changes worlds. Returning to a valid held state resumes the mobile session with the same persistent identity.

NAS on the move

Nearby connected NAS units are discovered around the player using the same 16-block range. Mounts follow the player and are removed when media leaves range or the Pocket Computer suspends.

Mobile context: wireless and NAS positioning follows the holder. World-dependent calls still require a valid loaded world context.
Lua API

peripheral API

Discover and call Monitors, Modems, inventories, USB drives and Speakers.

Peripheral names are usually side strings: left, right, top, bottom, front, back. Turtle upgrades use their corresponding side. Coordinate aliases use @dx,dy,dz for directly addressed neighbors.

FunctionReturnsBehavior
peripheral.getNames()tableArray of currently available peripheral names.
peripheral.getType(name)string | nilmonitor, modem, inventory, usb_drive, speaker, or nil.
peripheral.getMethods(name)table | nilArray of callable method names.
peripheral.wrap(name)table | nilCreate an object whose methods call this peripheral.
peripheral.find(type)zero or more tablesReturn every wrapped peripheral of this type as separate return values.
peripheral.call(name, method, ...)values…Call a method directly without wrapping first.

Discovery example

for _, name in ipairs(peripheral.getNames()) do
  print(name .. " is " .. tostring(peripheral.getType(name)))
  for _, method in ipairs(peripheral.getMethods(name)) do
    print("  " .. method)
  end
end

local speaker = peripheral.find("speaker")
if speaker then speaker.playSound("TaleOS_Boot_Beep") end

Listen for peripheral and peripheral_detach to react to hardware changes.

Peripheral API

Inventory peripherals

Inspect chests and other item containers adjacent to a machine.

Wrap a supported container as an inventory peripheral. Slots are one-based. Turtles can additionally move items with suck and drop.

MethodReturnsBehavior
inv.size()numberTotal number of slots.
inv.list()tableMap of occupied slot numbers to basic stacks: name and count.
inv.getItemDetail(slot)table | nilDetailed stack: name, count, maxCount, optional durability/maxDurability and safe metadata.
inv.getItemLimit(slot)numberMaximum stack size for the item in this slot.
local chest = peripheral.find("inventory")
if chest then
  print("Slots: " .. chest.size())
  for slot, stack in pairs(chest.list()) do
    print(slot, stack.name, stack.count)
  end
end
Hardware + Lua API

Modems & rednet

Channels, protocols, broadcasts and DNS over wireless networking.

Place a Modem adjacent to a Computer or equip it on a Turtle. Messages reach loaded receivers in the same world within the configured range (64 blocks by default). Payloads are deep-copied and bounded; each receiver is limited to 32 messages per tick by default.

modem methods

MethodReturnsBehavior
modem.open(channel)nilListen on a channel from 0 to 65535.
modem.close(channel)nilStop listening on that channel.
modem.closeAll()nilClose every channel on this modem.
modem.isOpen(channel)booleanWhether the channel is open.
modem.transmit(channel, replyChannel, payload)nilSend a payload with a reply channel. Listeners queue modem_message.
modem.isWireless()trueTaleOS Modems are wireless.

rednet functions

Rednet manages channels and protocols for you. It opens your computer ID and rednet.CHANNEL_BROADCAST (65535) when rednet.open succeeds.

FunctionReturnsBehavior
rednet.open(side)booleanOpen rednet on a wireless Modem.
rednet.isOpen(side)booleanWhether rednet is open on that side.
rednet.close(side) / closeAll()nilClose rednet channels on one/all managed Modems.
rednet.send(receiver, message[, protocol])booleanSend to a computer ID/channel (0–65535), optionally tagged with a protocol.
rednet.broadcast(message[, protocol])booleanSend to all open rednet receivers in range.
rednet.receive([protocol[, timeout]])sender, message, protocol | nilWait for a matching message; timeout is optional seconds. Nil on timeout.
rednet.host(protocol, hostname)nilRegister a hostname under a protocol; requires an open Modem.
rednet.unhost(protocol)nilRemove all names hosted by this computer under that protocol.
rednet.lookup(protocol[, hostname[, timeout]])ID | tableFind a specific host ID, or all host IDs when hostname is nil.
rednet.run()does not returnAnswer DNS lookups. The normal TaleOS shell already runs this in parallel.

Two-computer chat

-- Receiver:
rednet.open("right")
local sender, message = rednet.receive("demo", 10)
if sender then print(sender .. ": " .. tostring(message)) end

-- Sender (replace 8 with the receiver's ID):
rednet.open("left")
rednet.send(8, "Hello!", "demo")
Lua API

gps · Positioning

Locate machines using distances to at least four wireless reference hosts.

GPS uses channel gps.CHANNEL = 65534. A locating machine needs a wireless Modem and responses from at least four non-coplanar references with consistent distances.

FunctionReturnsBehavior
gps.locate([timeout])x, y, z | nil, errorRequest references and solve a 3D position. Default timeout is 2 seconds.
gps.host(x, y, z)does not returnServe fixed reference coordinates until the program is stopped.
gps.trilaterate(references)x, y, z | nil, errorSolve from an array of at least four tables containing finite x, y, z and non-negative distance.

Set up a GPS network

  1. Place four Computers with wireless Modems at known, non-coplanar positions.
  2. Run gps host <x> <y> <z> on each, using its actual coordinates.
  3. Attach a Modem to the locating machine and run gps locate.
local x, y, z = gps.locate(3)
if x then
  print(string.format("Position: %.2f %.2f %.2f", x, y, z))
else
  print("GPS failed: " .. tostring(y))
end
Geometry matters. Four hosts in one plane do not provide a unique 3D solution. Add a host above or below that plane and keep all reference chunks loaded.
Peripheral API

Speakers

Play registered Hytale SoundEvents at a position in the world.

Place a Speaker beside a Computer or equip it on a Turtle. TaleOS plays existing registered SoundEvents; arbitrary microphone input, note synthesis and streaming audio are not included.

MethodReturnsBehavior
speaker.playSound(name[, volume[, pitch]])true | false, reasonPlay a SoundEvent. Volume defaults to 1 and accepts 0–3. Pitch defaults to 1 and accepts 0.5–2.

Sound names may contain letters, digits, underscore, dot, slash and hyphen, up to 256 characters. Unknown SoundEvents return false. Playback is limited to one sound per equipped Speaker per tick.

local speaker = peripheral.find("speaker")
if not speaker then error("No Speaker attached") end

local ok, reason = speaker.playSound("TaleOS_Boot_Beep", 1, 1)
if not ok then print(reason) end
Server owners

Configuration & security

Language, HTTP access rules and wireless limits.

TaleOS creates mods/TaleOS/config.json relative to the server working directory. Edit it while the server is stopped, then restart. Keep a backup before making changes.

Complete default configuration

{
  "language": "en",
  "http": {
    "enabled": true,
    "max_requests": 16,
    "timeout_seconds": 30,
    "max_redirects": 16,
    "bandwidth": {
      "global_download": 33554432
    },
    "rules": [
      { "host": "$private", "action": "deny" },
      { "host": "*", "action": "allow", "max_download": 16777216 }
    ]
  },
  "modem": {
    "range": 64,
    "max_messages_per_tick": 32
  }
}

English or Spanish

Set "language": "en" (default) or "language": "es". This translates TaleOS interfaces, commands, Turtle messages, peripherals, errors and admin output. Lua API names and text produced by user programs do not change.

HTTP settings

SettingDefaultMeaning
http.enabledtrueExpose http.get and wget. False removes the API.
http.max_requests16Concurrent requests per computer; 0 removes this per-computer limit.
http.timeout_seconds30Request timeout, allowed range 0–60.
http.max_redirects16Maximum redirects per request.
http.bandwidth.global_download33554432Global download throughput budget: 32 MiB/s.
http.rulesdeny private, allow publicOrdered host rules; first match wins.

Host rules

Rules support domains, IPs, wildcards such as *.example.com, CIDR ranges and the special $private group. Every DNS result and redirect destination is checked again. max_download limits bytes for a matching rule; 16777216 is 16 MiB.

"rules": [
  { "host": "$private", "action": "deny" },
  { "host": "raw.githubusercontent.com", "action": "allow", "max_download": 16777216 },
  { "host": "*.example.com", "action": "allow", "max_download": 1048576 },
  { "host": "*", "action": "deny" }
]
Fail closed. Invalid HTTP configuration disables HTTP; an invalid rule denies all addresses. Do not broaden access to private infrastructure unless you understand the risk of server-side requests.

Wireless settings

modem.range is the same-world wireless radius in blocks, and modem.max_messages_per_tick limits delivery work. Both accept values from 1 to 256. Invalid modem settings use safe defaults (64 and 32).

Persistent server data

Server owners

Administration & logs

Operate computers from the console and diagnose running programs.

The /taleos command

/taleos <world> <x> <y> <z> <shell line…>

Boot the computer at that block if needed and queue one shell line. This is useful for automated tests and server administration. From the server console, omit the slash as required by Hytale's command parser.

taleos creative_test 191 123 -64 id
taleos creative_test 191 123 -64 ls nas
taleos creative_test 199 123 -65 __turtle id

Administrative diagnostics available at the same coordinates include __probe for chunk/block diagnostics, __blockid for block IDs, __stop to request program termination and __nasdebug to log NAS media locations. Turtle commands can be prefixed with __turtle .

Computer logs

Every machine writes a timestamped transcript to mods/TaleOS/logs/computer-<id>.log. It includes boots, submitted commands, shell output and program errors. Use these logs instead of relying on screenshots when diagnosing automation.

Keep automation loaded

Machines need loaded chunks for world reads, wireless delivery and physical actions. Hytale's vanilla keep-loaded command can maintain an automation area:

/world settings keeploaded set <minX> <minZ> <maxX> <maxZ> --world=<world>
Use keep-loaded sparingly. Large permanently loaded areas increase memory and tick cost. Bound the region to the machines that need it.

Backups and updates

Back up mods/TaleOS/ together with the world before updating or migrating a server. Do not delete identity files while keeping machine disks: IDs bind placed blocks, portable items, PenDrives and their data.

Built-in smoke test

Starting the JVM with -Dtaleos.selftest=true runs a startup smoke test: boot to the TaleOS prompt, submit an ID command and verify that an infinite loop is killed by its instruction budget while the server remains alive.

Reference

Compatibility & limits

What TaleOS 0.1.0 does—and what it does not do yet.

Platform

Relationship to other computer mods

TaleOS is a clean-room implementation inspired by the programmability of computer mods. Familiar names such as fs, term, peripheral, rednet and turtle do not imply complete API or program compatibility. Check signatures and return values in this wiki when porting a program.

Not implemented in 0.1.0

Operational limits

Lua instruction budgets kill runaway code, world calls are limited per tick, HTTP is asynchronous and validated, event queues are bounded, and network messages are size/rate limited. These protections reduce server risk but do not replace sensible permissions, backups and resource monitoring.

Examples

Example programs

Small, complete programs you can adapt for your own machines.

1. Monitor status board

Save as status.lua with a Monitor adjacent to the Computer:

local mon = tale.monitor()
if not mon then error("Place a Monitor next to this Computer") end

while true do
  local width = mon.getSize()
  local text = "ONLINE #" .. os.getComputerID()
  mon.clear("navy")
  mon.fill(1, 1, width, 1, "blue")
  mon.write(2, 2, text, "lime")
  mon.write(2, 10, os.date("%H:%M:%S"), "white")
  tale.sleep(20)
end

2. Peripheral event logger

fs.mkdir("logs")
print("Watching peripherals…")
while true do
  local event, name = os.pullEvent()
  if event == "peripheral" or event == "peripheral_detach" then
    local line = os.date() .. " " .. event .. " " .. name .. "\n"
    fs.append("logs/peripherals.txt", line)
    print(line)
  end
end

3. Safe dig-and-place routine

For a Turtle with a suitable tool in the right upgrade slot and blocks in slot 1:

local function check(ok, reason)
  if not ok then error(reason or "Action failed") end
end

turtle.select(1)
for step = 1, 10 do
  turtle.turnRight()
  if turtle.detectDown() then
    check(turtle.digDown("right"))
    tale.sleep(2)
  end
  check(turtle.placeUp())
  turtle.turnLeft()
  check(turtle.forward())
  print("Step " .. step .. "/10")
end

4. Networked doorbell

-- Computer beside a Speaker:
rednet.open("right")
local speaker = peripheral.find("speaker")
while true do
  local sender, message = rednet.receive("doorbell")
  if message == "ring" then
    print("Bell from #" .. sender)
    if speaker then speaker.playSound("TaleOS_Boot_Beep") end
  end
end

-- Another Computer:
rednet.open("left")
rednet.broadcast("ring", "doorbell")

5. Shared NAS configuration

local path = "nas/22/settings.txt"
if not fs.exists(path) then
  fs.write(path, "motd=Hello from shared storage")
end

local settings = fs.read(path)
print(settings)

Adapt the PenDrive ID to one shown by ls nas. Any machine in range can read the same file.