<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ocr.michiyo.me/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Michiyo</id>
	<title>OpenComputers: Rebooted - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://ocr.michiyo.me/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Michiyo"/>
	<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/wiki/Special:Contributions/Michiyo"/>
	<updated>2026-10-09T02:22:14Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.45.4</generator>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/tablet&amp;diff=1758</id>
		<title>Item/tablet</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/tablet&amp;diff=1758"/>
		<updated>2026-09-10T06:00:00Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Tablet =&lt;br /&gt;
&lt;br /&gt;
[[File:tablet.png|128px|tablet.png]]&lt;br /&gt;
&lt;br /&gt;
Tablets are built by placing a [[Item/tablet case|Tablet Case]] into an [[Block/assembler|Assembler]], configuring as desired and assembling it. Tablets act as portable computers that cannot directly interact with the world - for example [[Item/redstone card|Redstone Cards]] do &#039;&#039;not&#039;&#039; work in them. A number of upgrades, however, do, such as the [[Item/sign upgrade|Sign Upgrade]] or the [[Item/piston upgrade|Piston Upgrade]].&lt;br /&gt;
&lt;br /&gt;
You can sneak-click with a tablet in your hand to access the disk drive slot, and the power button to toggle the power off/on.&lt;br /&gt;
&lt;br /&gt;
Tablets can be put into a [[Block/charger|Charger]] to refill their energy and to access the first hard disk built into the tablet from a computer connected to the charger - in this setup, the charger will act similar to a disk drive, with the tablet being the floppy disk. This can be very useful in case you forgot to install an OS on the hard drive built into the tablet, or after bricking a tablet&#039;s OS.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Since version 1.7.3 for minecraft 1.12&#039;&#039; Tablets can &#039;&#039;&#039;also&#039;&#039;&#039; be charged by Forge Energy compatible machines, such as Thermal Expansion&#039;s Energetic Infuser.&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Item/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Template:WIP&amp;diff=1757</id>
		<title>Template:WIP</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Template:WIP&amp;diff=1757"/>
		<updated>2026-09-01T21:05:57Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span style=&amp;quot;color:#ac7d00; font-size:1.6em; vertical-align:middle;&amp;quot;&amp;gt;⚠&amp;lt;/span&amp;gt; {{{1}}} &amp;lt;span style=&amp;quot;color:#ac7d00; font-size:1.6em; vertical-align:middle;&amp;quot;&amp;gt;⚠&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Usage ==&lt;br /&gt;
&lt;br /&gt;
Use this template to mark content that is still a work in progress.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{WIP|This section is still being worked on.}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Displays as:&lt;br /&gt;
&lt;br /&gt;
{{WIP|This section is still being worked on.}}&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;&lt;br /&gt;
: The message to display between the warning icons.&lt;br /&gt;
&lt;br /&gt;
== Example ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{WIP|Documentation is incomplete and may change.}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Article message templates]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Contents&amp;diff=1756</id>
		<title>Contents</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Contents&amp;diff=1756"/>
		<updated>2026-09-01T20:50:45Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{WIP|We are currently in the process of unifying the separate block/item pages and component API pages together for ease of navigation. If you find the dedicated component API page is missing, check the item page. Thank you for your patience during this transition.}}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;oc-contents-table&amp;quot;&lt;br /&gt;
! colspan=&amp;quot;2&amp;quot; | Links&lt;br /&gt;
|-&lt;br /&gt;
! [[Main Page|OpenComputers]]&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
! General&lt;br /&gt;
| [[Openos|Install and Use OpenOS]] - [[Crossmod interoperation|Crossmod Interoperation]] - [[Lua conventions|Lua Conventions]] - [[Computer users|Computer Users]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{:Block/contents}}&lt;br /&gt;
{{:Item/contents}}&lt;br /&gt;
{{:API/contents}}&lt;br /&gt;
{{:Component/contents}}&lt;br /&gt;
{{:Addon/contents}}&lt;br /&gt;
{{:Tutorial/contents}}&lt;br /&gt;
&lt;br /&gt;
{|&lt;br /&gt;
! Other Languages&lt;br /&gt;
! Page&lt;br /&gt;
|-&lt;br /&gt;
| Simplified Chinese&lt;br /&gt;
| [[Start/zh|简体中文（Simplified Chinese）]]&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Template:WIP&amp;diff=1755</id>
		<title>Template:WIP</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Template:WIP&amp;diff=1755"/>
		<updated>2026-09-01T20:50:36Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Created page with &amp;quot;&amp;lt;span style=&amp;quot;color:#ac7d00;&amp;quot;&amp;gt;⚠&amp;lt;/span&amp;gt; {{{1}}} &amp;lt;span style=&amp;quot;color:#ac7d00;&amp;quot;&amp;gt;⚠&amp;lt;/span&amp;gt;  &amp;lt;noinclude&amp;gt; == Usage ==  Use this template to mark content that is still a work in progress.  &amp;lt;pre&amp;gt; {{WIP|This section is still being worked on.}} &amp;lt;/pre&amp;gt;  Displays as:  {{WIP|This section is still being worked on.}}  == Parameters ==  ; &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; : The message to display between the warning icons.  == Example ==  &amp;lt;pre&amp;gt; {{WIP|Documentation is incomplete and may change.}} &amp;lt;/pre&amp;gt;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span style=&amp;quot;color:#ac7d00;&amp;quot;&amp;gt;⚠&amp;lt;/span&amp;gt; {{{1}}} &amp;lt;span style=&amp;quot;color:#ac7d00;&amp;quot;&amp;gt;⚠&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;noinclude&amp;gt;&lt;br /&gt;
== Usage ==&lt;br /&gt;
&lt;br /&gt;
Use this template to mark content that is still a work in progress.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{WIP|This section is still being worked on.}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Displays as:&lt;br /&gt;
&lt;br /&gt;
{{WIP|This section is still being worked on.}}&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;&lt;br /&gt;
: The message to display between the warning icons.&lt;br /&gt;
&lt;br /&gt;
== Example ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{{WIP|Documentation is incomplete and may change.}}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Article message templates]]&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1734</id>
		<title>Projector</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1734"/>
		<updated>2026-09-01T07:28:26Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projector =&lt;br /&gt;
&lt;br /&gt;
[[File:projector.png|Projector]]&lt;br /&gt;
&lt;br /&gt;
The projector is a long-range, flat display controlled by a computer. It can either render a 320x200 pixel framebuffer directly, or emulate a normal OpenComputers &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component with a 160x50 character display.&lt;br /&gt;
&lt;br /&gt;
The projected image is not a physical block and does not need a wall or other screen block behind it. The projector raycasts in its facing direction and places the display on the first collidable block it reaches, up to 12.5 blocks away. If no block is hit, it uses the maximum distance. The projection is scaled with distance, so a nearby surface produces a smaller image. A tiny offset from the target surface prevents z-fighting.&lt;br /&gt;
&lt;br /&gt;
== Placement and modes ==&lt;br /&gt;
&lt;br /&gt;
The projector faces away from the player when placed. Its [[Item/scrench|scrench]] has a special function: right-clicking the projector toggles between pixel mode and screen mode instead of rotating the block. The selected mode is saved with the projector.&lt;br /&gt;
&lt;br /&gt;
Only the component for the active mode is visible on the OpenComputers network:&lt;br /&gt;
&lt;br /&gt;
* Pixel mode exposes a &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component.&lt;br /&gt;
* Screen mode exposes a native &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
The two component addresses are separate. Use &amp;lt;code&amp;gt;component.list(&amp;quot;projector&amp;quot;)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;component.list(&amp;quot;screen&amp;quot;)&amp;lt;/code&amp;gt; after changing modes; do not expect the projector&#039;s address to be the screen&#039;s address.&lt;br /&gt;
&lt;br /&gt;
== Pixel mode ==&lt;br /&gt;
&lt;br /&gt;
Pixel mode provides a 320x200 RGBA framebuffer. Coordinates are one-based, with &amp;lt;code&amp;gt;(1, 1)&amp;lt;/code&amp;gt; at the upper-left corner. Colors may be supplied as &amp;lt;code&amp;gt;0xRRGGBB&amp;lt;/code&amp;gt; for an opaque color or &amp;lt;code&amp;gt;0xAARRGGBB&amp;lt;/code&amp;gt; for an explicit alpha value. Color &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; is transparent black.&lt;br /&gt;
&lt;br /&gt;
The projector component provides these callbacks:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Callback&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getResolution()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns &amp;lt;code&amp;gt;320, 200&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;isProjecting()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns whether the projector is on and powered.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;turnOn()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;turnOff()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Enable or disable projection.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;get(x, y)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns the RGB color at a pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;set(x, y, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets one pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;fill(x, y, width, height, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Fills a rectangle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;clear()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Clears the framebuffer to transparent black.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setRaw(data)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets row-major RGBA bytes. There are four bytes per pixel; incomplete data clears the remaining pixels.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Pixel mode consumes energy while the projector is on. Large updates are sent to clients as compressed framebuffer updates, so a saved framebuffer can be restored when its chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local projector = component.proxy(component.list(&amp;quot;projector&amp;quot;)())&lt;br /&gt;
&lt;br /&gt;
projector.clear()&lt;br /&gt;
projector.fill(1, 1, 320, 200, 0xFF101820)&lt;br /&gt;
projector.fill(20, 20, 280, 160, 0xFF204060)&lt;br /&gt;
projector.set(25, 25, 0xFFFFCC00)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Screen mode ==&lt;br /&gt;
&lt;br /&gt;
Screen mode emulates a native OpenComputers T3 screen:&lt;br /&gt;
&lt;br /&gt;
* Resolution: 160x50 characters&lt;br /&gt;
* Maximum color depth: 8-bit&lt;br /&gt;
* Standard &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;gpu&amp;lt;/code&amp;gt; APIs&lt;br /&gt;
* Normal screen text, color, palette, mouse, and keyboard behavior&lt;br /&gt;
&lt;br /&gt;
Use a graphics card to draw to the projected screen just as with any other OpenComputers screen. If it is the only screen available, the GPU may already be bound to it; binding explicitly makes a program unambiguous.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local gpu = component.gpu&lt;br /&gt;
local screenAddress = component.list(&amp;quot;screen&amp;quot;)()&lt;br /&gt;
&lt;br /&gt;
gpu.bind(screenAddress)&lt;br /&gt;
gpu.setResolution(160, 50)&lt;br /&gt;
gpu.setDepth(8)&lt;br /&gt;
gpu.setBackground(0x102030)&lt;br /&gt;
gpu.setForeground(0xFFFFFF)&lt;br /&gt;
gpu.fill(1, 1, 160, 50, &amp;quot; &amp;quot;)&lt;br /&gt;
gpu.set(2, 2, &amp;quot;Hello from the projector&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Right-clicking the visible projected screen opens the normal OpenComputers screen interface. The interface accepts mouse and keyboard input. An OpenComputers [[Block/keyboard|keyboard]] placed next to the projector can also be used for normal in-world screen input.&lt;br /&gt;
&lt;br /&gt;
The screen&#039;s text buffer is saved with the projector, and the emulated screen component is restored when the projector&#039;s chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
== Power and troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
The projector must be connected to a powered OpenComputers network. In pixel mode, check for the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component. In screen mode, check for the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component instead. The inactive mode is intentionally absent from component listings.&lt;br /&gt;
&lt;br /&gt;
If a program cached a component address before switching modes, enumerate the active component again. Component addresses can also change when a component is recreated, so programs should prefer component type/name discovery over hard-coding an address.&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1733</id>
		<title>Projector</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1733"/>
		<updated>2026-09-01T07:27:43Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projector =&lt;br /&gt;
&lt;br /&gt;
[[File:projector.png|Projector]]&lt;br /&gt;
&lt;br /&gt;
The projector is a long-range, flat display controlled by a computer. It can either render a 320x200 pixel framebuffer directly, or emulate a normal OpenComputers &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component with a 160x50 character display.&lt;br /&gt;
&lt;br /&gt;
The projected image is not a physical block and does not need a wall or other screen block behind it. The projector raycasts in its facing direction and places the display on the first collidable block it reaches, up to 12.5 blocks away. If no block is hit, it uses the maximum distance. The projection is scaled with distance, so a nearby surface produces a smaller image. A tiny offset from the target surface prevents z-fighting.&lt;br /&gt;
&lt;br /&gt;
== Placement and modes ==&lt;br /&gt;
&lt;br /&gt;
The projector faces away from the player when placed. Its [[Item/scrench|scrench]] has a special function: right-clicking the projector toggles between pixel mode and screen mode instead of rotating the block. The selected mode is saved with the projector.&lt;br /&gt;
&lt;br /&gt;
Only the component for the active mode is visible on the OpenComputers network:&lt;br /&gt;
&lt;br /&gt;
* Pixel mode exposes a &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component.&lt;br /&gt;
* Screen mode exposes a native &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
The two component addresses are separate. Use &amp;lt;code&amp;gt;component.list(&amp;quot;projector&amp;quot;)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;component.list(&amp;quot;screen&amp;quot;)&amp;lt;/code&amp;gt; after changing modes; do not expect the projector&#039;s address to be the screen&#039;s address.&lt;br /&gt;
&lt;br /&gt;
== Pixel mode ==&lt;br /&gt;
&lt;br /&gt;
Pixel mode provides a 320x200 RGBA framebuffer. Coordinates are one-based, with &amp;lt;code&amp;gt;(1, 1)&amp;lt;/code&amp;gt; at the upper-left corner. Colors may be supplied as &amp;lt;code&amp;gt;0xRRGGBB&amp;lt;/code&amp;gt; for an opaque color or &amp;lt;code&amp;gt;0xAARRGGBB&amp;lt;/code&amp;gt; for an explicit alpha value. Color &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; is transparent black.&lt;br /&gt;
&lt;br /&gt;
The projector component provides these callbacks:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Callback&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getResolution()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns &amp;lt;code&amp;gt;320, 200&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;isProjecting()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns whether the projector is on and powered.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;turnOn()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;turnOff()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Enable or disable projection.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;get(x, y)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns the RGB color at a pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;set(x, y, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets one pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;fill(x, y, width, height, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Fills a rectangle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;clear()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Clears the framebuffer to transparent black.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setRaw(data)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets row-major RGBA bytes. There are four bytes per pixel; incomplete data clears the remaining pixels.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Pixel mode consumes energy while the projector is on. Large updates are sent to clients as compressed framebuffer updates, so a saved framebuffer can be restored when its chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local projector = component.proxy(component.list(&amp;quot;projector&amp;quot;)())&lt;br /&gt;
&lt;br /&gt;
projector.clear()&lt;br /&gt;
projector.fill(1, 1, 320, 200, 0xFF101820)&lt;br /&gt;
projector.fill(20, 20, 280, 160, 0xFF204060)&lt;br /&gt;
projector.set(25, 25, 0xFFFFCC00)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Screen mode ==&lt;br /&gt;
&lt;br /&gt;
Screen mode emulates a native OpenComputers T3 screen:&lt;br /&gt;
&lt;br /&gt;
* Resolution: 160x50 characters&lt;br /&gt;
* Maximum color depth: 8-bit&lt;br /&gt;
* Standard &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;gpu&amp;lt;/code&amp;gt; APIs&lt;br /&gt;
* Normal screen text, color, palette, mouse, and keyboard behavior&lt;br /&gt;
&lt;br /&gt;
Use a graphics card to draw to the projected screen just as with any other OpenComputers screen. If it is the only screen available, the GPU may already be bound to it; binding explicitly makes a program unambiguous.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local gpu = component.gpu&lt;br /&gt;
local screenAddress = component.list(&amp;quot;screen&amp;quot;)()&lt;br /&gt;
&lt;br /&gt;
gpu.bind(screenAddress)&lt;br /&gt;
gpu.setResolution(160, 50)&lt;br /&gt;
gpu.setDepth(8)&lt;br /&gt;
gpu.setBackground(0x102030)&lt;br /&gt;
gpu.setForeground(0xFFFFFF)&lt;br /&gt;
gpu.fill(1, 1, 160, 50, &amp;quot; &amp;quot;)&lt;br /&gt;
gpu.set(2, 2, &amp;quot;Hello from the projector&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Right-clicking the visible projected screen opens the normal OpenComputers screen interface. The interface accepts mouse and keyboard input. An OpenComputers [[Keyboard|keyboard]] placed next to the projector can also be used for normal in-world screen input.&lt;br /&gt;
&lt;br /&gt;
The screen&#039;s text buffer is saved with the projector, and the emulated screen component is restored when the projector&#039;s chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
== Power and troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
The projector must be connected to a powered OpenComputers network. In pixel mode, check for the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component. In screen mode, check for the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component instead. The inactive mode is intentionally absent from component listings.&lt;br /&gt;
&lt;br /&gt;
If a program cached a component address before switching modes, enumerate the active component again. Component addresses can also change when a component is recreated, so programs should prefer component type/name discovery over hard-coding an address.&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1732</id>
		<title>Projector</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1732"/>
		<updated>2026-09-01T07:26:26Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: /* Placement and modes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projector =&lt;br /&gt;
&lt;br /&gt;
[[File:projector.png|Projector]]&lt;br /&gt;
&lt;br /&gt;
The projector is a long-range, flat display controlled by a [[Computer|computer]]. It can either render a 320x200 pixel framebuffer directly, or emulate a normal OpenComputers &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component with a 160x50 character display.&lt;br /&gt;
&lt;br /&gt;
The projected image is not a physical block and does not need a wall or other screen block behind it. The projector raycasts in its facing direction and places the display on the first collidable block it reaches, up to 12.5 blocks away. If no block is hit, it uses the maximum distance. The projection is scaled with distance, so a nearby surface produces a smaller image. A tiny offset from the target surface prevents z-fighting.&lt;br /&gt;
&lt;br /&gt;
== Placement and modes ==&lt;br /&gt;
&lt;br /&gt;
The projector faces away from the player when placed. Its [[Item/scrench|scrench]] has a special function: right-clicking the projector toggles between pixel mode and screen mode instead of rotating the block. The selected mode is saved with the projector.&lt;br /&gt;
&lt;br /&gt;
Only the component for the active mode is visible on the OpenComputers network:&lt;br /&gt;
&lt;br /&gt;
* Pixel mode exposes a &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component.&lt;br /&gt;
* Screen mode exposes a native &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
The two component addresses are separate. Use &amp;lt;code&amp;gt;component.list(&amp;quot;projector&amp;quot;)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;component.list(&amp;quot;screen&amp;quot;)&amp;lt;/code&amp;gt; after changing modes; do not expect the projector&#039;s address to be the screen&#039;s address.&lt;br /&gt;
&lt;br /&gt;
== Pixel mode ==&lt;br /&gt;
&lt;br /&gt;
Pixel mode provides a 320x200 RGBA framebuffer. Coordinates are one-based, with &amp;lt;code&amp;gt;(1, 1)&amp;lt;/code&amp;gt; at the upper-left corner. Colors may be supplied as &amp;lt;code&amp;gt;0xRRGGBB&amp;lt;/code&amp;gt; for an opaque color or &amp;lt;code&amp;gt;0xAARRGGBB&amp;lt;/code&amp;gt; for an explicit alpha value. Color &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; is transparent black.&lt;br /&gt;
&lt;br /&gt;
The projector component provides these callbacks:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Callback&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getResolution()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns &amp;lt;code&amp;gt;320, 200&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;isProjecting()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns whether the projector is on and powered.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;turnOn()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;turnOff()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Enable or disable projection.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;get(x, y)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns the RGB color at a pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;set(x, y, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets one pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;fill(x, y, width, height, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Fills a rectangle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;clear()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Clears the framebuffer to transparent black.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setRaw(data)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets row-major RGBA bytes. There are four bytes per pixel; incomplete data clears the remaining pixels.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Pixel mode consumes energy while the projector is on. Large updates are sent to clients as compressed framebuffer updates, so a saved framebuffer can be restored when its chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local projector = component.proxy(component.list(&amp;quot;projector&amp;quot;)())&lt;br /&gt;
&lt;br /&gt;
projector.clear()&lt;br /&gt;
projector.fill(1, 1, 320, 200, 0xFF101820)&lt;br /&gt;
projector.fill(20, 20, 280, 160, 0xFF204060)&lt;br /&gt;
projector.set(25, 25, 0xFFFFCC00)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Screen mode ==&lt;br /&gt;
&lt;br /&gt;
Screen mode emulates a native OpenComputers T3 screen:&lt;br /&gt;
&lt;br /&gt;
* Resolution: 160x50 characters&lt;br /&gt;
* Maximum color depth: 8-bit&lt;br /&gt;
* Standard &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;gpu&amp;lt;/code&amp;gt; APIs&lt;br /&gt;
* Normal screen text, color, palette, mouse, and keyboard behavior&lt;br /&gt;
&lt;br /&gt;
Use a graphics card to draw to the projected screen just as with any other OpenComputers screen. If it is the only screen available, the GPU may already be bound to it; binding explicitly makes a program unambiguous.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local gpu = component.gpu&lt;br /&gt;
local screenAddress = component.list(&amp;quot;screen&amp;quot;)()&lt;br /&gt;
&lt;br /&gt;
gpu.bind(screenAddress)&lt;br /&gt;
gpu.setResolution(160, 50)&lt;br /&gt;
gpu.setDepth(8)&lt;br /&gt;
gpu.setBackground(0x102030)&lt;br /&gt;
gpu.setForeground(0xFFFFFF)&lt;br /&gt;
gpu.fill(1, 1, 160, 50, &amp;quot; &amp;quot;)&lt;br /&gt;
gpu.set(2, 2, &amp;quot;Hello from the projector&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Right-clicking the visible projected screen opens the normal OpenComputers screen interface. The interface accepts mouse and keyboard input. An OpenComputers [[Keyboard|keyboard]] placed next to the projector can also be used for normal in-world screen input.&lt;br /&gt;
&lt;br /&gt;
The screen&#039;s text buffer is saved with the projector, and the emulated screen component is restored when the projector&#039;s chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
== Power and troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
The projector must be connected to a powered OpenComputers network. In pixel mode, check for the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component. In screen mode, check for the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component instead. The inactive mode is intentionally absent from component listings.&lt;br /&gt;
&lt;br /&gt;
If a program cached a component address before switching modes, enumerate the active component again. Component addresses can also change when a component is recreated, so programs should prefer component type/name discovery over hard-coding an address.&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1731</id>
		<title>Projector</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Projector&amp;diff=1731"/>
		<updated>2026-09-01T07:23:41Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Created page with &amp;quot;= Projector =  Projector  The projector is a long-range, flat display controlled by a computer. It can either render a 320x200 pixel framebuffer directly, or emulate a normal OpenComputers &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component with a 160x50 character display.  The projected image is not a physical block and does not need a wall or other screen block behind it. The projector raycasts in its facing direction and places the display on the first c...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Projector =&lt;br /&gt;
&lt;br /&gt;
[[File:projector.png|Projector]]&lt;br /&gt;
&lt;br /&gt;
The projector is a long-range, flat display controlled by a [[Computer|computer]]. It can either render a 320x200 pixel framebuffer directly, or emulate a normal OpenComputers &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component with a 160x50 character display.&lt;br /&gt;
&lt;br /&gt;
The projected image is not a physical block and does not need a wall or other screen block behind it. The projector raycasts in its facing direction and places the display on the first collidable block it reaches, up to 12.5 blocks away. If no block is hit, it uses the maximum distance. The projection is scaled with distance, so a nearby surface produces a smaller image. A tiny offset from the target surface prevents z-fighting.&lt;br /&gt;
&lt;br /&gt;
== Placement and modes ==&lt;br /&gt;
&lt;br /&gt;
The projector faces away from the player when placed. Its [[Item/wrench|scrench]] has a special function: right-clicking the projector toggles between pixel mode and screen mode instead of rotating the block. The selected mode is saved with the projector.&lt;br /&gt;
&lt;br /&gt;
Only the component for the active mode is visible on the OpenComputers network:&lt;br /&gt;
&lt;br /&gt;
* Pixel mode exposes a &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component.&lt;br /&gt;
* Screen mode exposes a native &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component and hides the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
The two component addresses are separate. Use &amp;lt;code&amp;gt;component.list(&amp;quot;projector&amp;quot;)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;component.list(&amp;quot;screen&amp;quot;)&amp;lt;/code&amp;gt; after changing modes; do not expect the projector&#039;s address to be the screen&#039;s address.&lt;br /&gt;
&lt;br /&gt;
== Pixel mode ==&lt;br /&gt;
&lt;br /&gt;
Pixel mode provides a 320x200 RGBA framebuffer. Coordinates are one-based, with &amp;lt;code&amp;gt;(1, 1)&amp;lt;/code&amp;gt; at the upper-left corner. Colors may be supplied as &amp;lt;code&amp;gt;0xRRGGBB&amp;lt;/code&amp;gt; for an opaque color or &amp;lt;code&amp;gt;0xAARRGGBB&amp;lt;/code&amp;gt; for an explicit alpha value. Color &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; is transparent black.&lt;br /&gt;
&lt;br /&gt;
The projector component provides these callbacks:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Callback&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;getResolution()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns &amp;lt;code&amp;gt;320, 200&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;isProjecting()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns whether the projector is on and powered.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;turnOn()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;turnOff()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Enable or disable projection.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;get(x, y)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns the RGB color at a pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;set(x, y, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets one pixel.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;fill(x, y, width, height, color)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Fills a rectangle.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;clear()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Clears the framebuffer to transparent black.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;setRaw(data)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Sets row-major RGBA bytes. There are four bytes per pixel; incomplete data clears the remaining pixels.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Pixel mode consumes energy while the projector is on. Large updates are sent to clients as compressed framebuffer updates, so a saved framebuffer can be restored when its chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local projector = component.proxy(component.list(&amp;quot;projector&amp;quot;)())&lt;br /&gt;
&lt;br /&gt;
projector.clear()&lt;br /&gt;
projector.fill(1, 1, 320, 200, 0xFF101820)&lt;br /&gt;
projector.fill(20, 20, 280, 160, 0xFF204060)&lt;br /&gt;
projector.set(25, 25, 0xFFFFCC00)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Screen mode ==&lt;br /&gt;
&lt;br /&gt;
Screen mode emulates a native OpenComputers T3 screen:&lt;br /&gt;
&lt;br /&gt;
* Resolution: 160x50 characters&lt;br /&gt;
* Maximum color depth: 8-bit&lt;br /&gt;
* Standard &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;gpu&amp;lt;/code&amp;gt; APIs&lt;br /&gt;
* Normal screen text, color, palette, mouse, and keyboard behavior&lt;br /&gt;
&lt;br /&gt;
Use a graphics card to draw to the projected screen just as with any other OpenComputers screen. If it is the only screen available, the GPU may already be bound to it; binding explicitly makes a program unambiguous.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local component = require(&amp;quot;component&amp;quot;)&lt;br /&gt;
local gpu = component.gpu&lt;br /&gt;
local screenAddress = component.list(&amp;quot;screen&amp;quot;)()&lt;br /&gt;
&lt;br /&gt;
gpu.bind(screenAddress)&lt;br /&gt;
gpu.setResolution(160, 50)&lt;br /&gt;
gpu.setDepth(8)&lt;br /&gt;
gpu.setBackground(0x102030)&lt;br /&gt;
gpu.setForeground(0xFFFFFF)&lt;br /&gt;
gpu.fill(1, 1, 160, 50, &amp;quot; &amp;quot;)&lt;br /&gt;
gpu.set(2, 2, &amp;quot;Hello from the projector&amp;quot;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Right-clicking the visible projected screen opens the normal OpenComputers screen interface. The interface accepts mouse and keyboard input. An OpenComputers [[Keyboard|keyboard]] placed next to the projector can also be used for normal in-world screen input.&lt;br /&gt;
&lt;br /&gt;
The screen&#039;s text buffer is saved with the projector, and the emulated screen component is restored when the projector&#039;s chunk is loaded.&lt;br /&gt;
&lt;br /&gt;
== Power and troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
The projector must be connected to a powered OpenComputers network. In pixel mode, check for the &amp;lt;code&amp;gt;projector&amp;lt;/code&amp;gt; component. In screen mode, check for the &amp;lt;code&amp;gt;screen&amp;lt;/code&amp;gt; component instead. The inactive mode is intentionally absent from component listings.&lt;br /&gt;
&lt;br /&gt;
If a program cached a component address before switching modes, enumerate the active component again. Component addresses can also change when a component is recreated, so programs should prefer component type/name discovery over hard-coding an address.&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1729</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1729"/>
		<updated>2026-08-30T16:31:26Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS placed here will be applied to all skins */&lt;br /&gt;
.client-darkmode .mw-parser-output img,&lt;br /&gt;
.client-darkmode .mw-parser-output .image {&lt;br /&gt;
    filter: invert(1) hue-rotate(180deg);&lt;br /&gt;
}&lt;br /&gt;
/* Undo DarkMode&#039;s inversion for images */&lt;br /&gt;
.client-darkmode .mw-parser-output img,&lt;br /&gt;
.client-darkmode .mw-parser-output .image,&lt;br /&gt;
.client-darkmode .mw-logo-icon,&lt;br /&gt;
.client-darkmode #p-logo .mw-wiki-logo {&lt;br /&gt;
    filter: invert(1) hue-rotate(180deg) !important;&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    table-layout: fixed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(1),&lt;br /&gt;
.oc-contents-table td:nth-child(1) {&lt;br /&gt;
    width: 12%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(2),&lt;br /&gt;
.oc-contents-table td:nth-child(2) {&lt;br /&gt;
    width: 16%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(3),&lt;br /&gt;
.oc-contents-table td:nth-child(3) {&lt;br /&gt;
    width: 72%;&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
    margin: 0 0 0.75em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Force MediaWiki&#039;s built-in table of contents to the right */&lt;br /&gt;
#toc,&lt;br /&gt;
.toc {&lt;br /&gt;
    float: right;&lt;br /&gt;
    clear: right;&lt;br /&gt;
&lt;br /&gt;
    width: 20em;&lt;br /&gt;
    max-width: 30%;&lt;br /&gt;
&lt;br /&gt;
    margin: 0 0 1em 1.5em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Keep very large TOCs from becoming absurdly tall */&lt;br /&gt;
#toc .tocnumber,&lt;br /&gt;
#toc .toctext {&lt;br /&gt;
    display: inline;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow/mobile screens: return TOC to normal document flow */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    #toc,&lt;br /&gt;
    .toc {&lt;br /&gt;
        float: none;&lt;br /&gt;
        clear: both;&lt;br /&gt;
&lt;br /&gt;
        width: auto;&lt;br /&gt;
        max-width: none;&lt;br /&gt;
&lt;br /&gt;
        margin: 1em 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OpenComputers documentation navigation tables */&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    max-width: 1400px;&lt;br /&gt;
    margin: 1em auto;&lt;br /&gt;
&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-collapse: separate;&lt;br /&gt;
    border-spacing: 0;&lt;br /&gt;
&lt;br /&gt;
    background: #f8f9fa;&lt;br /&gt;
    font-size: 0.95em;&lt;br /&gt;
    line-height: 1.55;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Main column headings */&lt;br /&gt;
.oc-contents-table &amp;gt; tbody &amp;gt; tr:first-child &amp;gt; th {&lt;br /&gt;
    padding: 0.6em 0.9em;&lt;br /&gt;
&lt;br /&gt;
    background: #eaecf0;&lt;br /&gt;
    border-bottom: 1px solid #a2a9b1;&lt;br /&gt;
&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* General cell spacing */&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    padding: 0.55em 0.9em;&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* First column: Components / API / Items / etc. */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:first-child:not(:only-child) {&lt;br /&gt;
    width: 10em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Category column */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
    width: 13em;&lt;br /&gt;
&lt;br /&gt;
    background: #f1f3f5;&lt;br /&gt;
    border-left: 1px solid #c8ccd1;&lt;br /&gt;
    border-right: 1px solid #c8ccd1;&lt;br /&gt;
&lt;br /&gt;
    text-align: right;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Documentation/link area */&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Separate category rows */&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; th,&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; td {&lt;br /&gt;
    border-top: 1px solid #eaecf0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make links breathe a little */&lt;br /&gt;
.oc-contents-table td a {&lt;br /&gt;
    line-height: 1.8;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Don&#039;t let visited links make these navigation boxes ugly */&lt;br /&gt;
.oc-contents-table a:visited {&lt;br /&gt;
    color: #36c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow displays */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    .oc-contents-table {&lt;br /&gt;
        font-size: 0.9em;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:first-child,&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
        width: auto;&lt;br /&gt;
        white-space: normal;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1728</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1728"/>
		<updated>2026-08-30T16:29:48Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Undo revision 1727 by Michiyo (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS placed here will be applied to all skins */&lt;br /&gt;
.client-darkmode .mw-parser-output img,&lt;br /&gt;
.client-darkmode .mw-parser-output .image {&lt;br /&gt;
    filter: invert(1) hue-rotate(180deg);&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    table-layout: fixed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(1),&lt;br /&gt;
.oc-contents-table td:nth-child(1) {&lt;br /&gt;
    width: 12%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(2),&lt;br /&gt;
.oc-contents-table td:nth-child(2) {&lt;br /&gt;
    width: 16%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(3),&lt;br /&gt;
.oc-contents-table td:nth-child(3) {&lt;br /&gt;
    width: 72%;&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
    margin: 0 0 0.75em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Force MediaWiki&#039;s built-in table of contents to the right */&lt;br /&gt;
#toc,&lt;br /&gt;
.toc {&lt;br /&gt;
    float: right;&lt;br /&gt;
    clear: right;&lt;br /&gt;
&lt;br /&gt;
    width: 20em;&lt;br /&gt;
    max-width: 30%;&lt;br /&gt;
&lt;br /&gt;
    margin: 0 0 1em 1.5em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Keep very large TOCs from becoming absurdly tall */&lt;br /&gt;
#toc .tocnumber,&lt;br /&gt;
#toc .toctext {&lt;br /&gt;
    display: inline;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow/mobile screens: return TOC to normal document flow */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    #toc,&lt;br /&gt;
    .toc {&lt;br /&gt;
        float: none;&lt;br /&gt;
        clear: both;&lt;br /&gt;
&lt;br /&gt;
        width: auto;&lt;br /&gt;
        max-width: none;&lt;br /&gt;
&lt;br /&gt;
        margin: 1em 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OpenComputers documentation navigation tables */&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    max-width: 1400px;&lt;br /&gt;
    margin: 1em auto;&lt;br /&gt;
&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-collapse: separate;&lt;br /&gt;
    border-spacing: 0;&lt;br /&gt;
&lt;br /&gt;
    background: #f8f9fa;&lt;br /&gt;
    font-size: 0.95em;&lt;br /&gt;
    line-height: 1.55;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Main column headings */&lt;br /&gt;
.oc-contents-table &amp;gt; tbody &amp;gt; tr:first-child &amp;gt; th {&lt;br /&gt;
    padding: 0.6em 0.9em;&lt;br /&gt;
&lt;br /&gt;
    background: #eaecf0;&lt;br /&gt;
    border-bottom: 1px solid #a2a9b1;&lt;br /&gt;
&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* General cell spacing */&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    padding: 0.55em 0.9em;&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* First column: Components / API / Items / etc. */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:first-child:not(:only-child) {&lt;br /&gt;
    width: 10em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Category column */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
    width: 13em;&lt;br /&gt;
&lt;br /&gt;
    background: #f1f3f5;&lt;br /&gt;
    border-left: 1px solid #c8ccd1;&lt;br /&gt;
    border-right: 1px solid #c8ccd1;&lt;br /&gt;
&lt;br /&gt;
    text-align: right;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Documentation/link area */&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Separate category rows */&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; th,&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; td {&lt;br /&gt;
    border-top: 1px solid #eaecf0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make links breathe a little */&lt;br /&gt;
.oc-contents-table td a {&lt;br /&gt;
    line-height: 1.8;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Don&#039;t let visited links make these navigation boxes ugly */&lt;br /&gt;
.oc-contents-table a:visited {&lt;br /&gt;
    color: #36c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow displays */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    .oc-contents-table {&lt;br /&gt;
        font-size: 0.9em;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:first-child,&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
        width: auto;&lt;br /&gt;
        white-space: normal;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1727</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1727"/>
		<updated>2026-08-30T16:28:09Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS placed here will be applied to all skins */&lt;br /&gt;
/* Do not invert normal images in dark mode */&lt;br /&gt;
.client-darkmode .mw-parser-output img,&lt;br /&gt;
.client-darkmode .mw-parser-output .image {&lt;br /&gt;
    filter: none !important;&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    table-layout: fixed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(1),&lt;br /&gt;
.oc-contents-table td:nth-child(1) {&lt;br /&gt;
    width: 12%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(2),&lt;br /&gt;
.oc-contents-table td:nth-child(2) {&lt;br /&gt;
    width: 16%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(3),&lt;br /&gt;
.oc-contents-table td:nth-child(3) {&lt;br /&gt;
    width: 72%;&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
    margin: 0 0 0.75em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Force MediaWiki&#039;s built-in table of contents to the right */&lt;br /&gt;
#toc,&lt;br /&gt;
.toc {&lt;br /&gt;
    float: right;&lt;br /&gt;
    clear: right;&lt;br /&gt;
&lt;br /&gt;
    width: 20em;&lt;br /&gt;
    max-width: 30%;&lt;br /&gt;
&lt;br /&gt;
    margin: 0 0 1em 1.5em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Keep very large TOCs from becoming absurdly tall */&lt;br /&gt;
#toc .tocnumber,&lt;br /&gt;
#toc .toctext {&lt;br /&gt;
    display: inline;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow/mobile screens: return TOC to normal document flow */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    #toc,&lt;br /&gt;
    .toc {&lt;br /&gt;
        float: none;&lt;br /&gt;
        clear: both;&lt;br /&gt;
&lt;br /&gt;
        width: auto;&lt;br /&gt;
        max-width: none;&lt;br /&gt;
&lt;br /&gt;
        margin: 1em 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OpenComputers documentation navigation tables */&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    max-width: 1400px;&lt;br /&gt;
    margin: 1em auto;&lt;br /&gt;
&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-collapse: separate;&lt;br /&gt;
    border-spacing: 0;&lt;br /&gt;
&lt;br /&gt;
    background: #f8f9fa;&lt;br /&gt;
    font-size: 0.95em;&lt;br /&gt;
    line-height: 1.55;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Main column headings */&lt;br /&gt;
.oc-contents-table &amp;gt; tbody &amp;gt; tr:first-child &amp;gt; th {&lt;br /&gt;
    padding: 0.6em 0.9em;&lt;br /&gt;
&lt;br /&gt;
    background: #eaecf0;&lt;br /&gt;
    border-bottom: 1px solid #a2a9b1;&lt;br /&gt;
&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* General cell spacing */&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    padding: 0.55em 0.9em;&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* First column: Components / API / Items / etc. */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:first-child:not(:only-child) {&lt;br /&gt;
    width: 10em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Category column */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
    width: 13em;&lt;br /&gt;
&lt;br /&gt;
    background: #f1f3f5;&lt;br /&gt;
    border-left: 1px solid #c8ccd1;&lt;br /&gt;
    border-right: 1px solid #c8ccd1;&lt;br /&gt;
&lt;br /&gt;
    text-align: right;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Documentation/link area */&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Separate category rows */&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; th,&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; td {&lt;br /&gt;
    border-top: 1px solid #eaecf0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make links breathe a little */&lt;br /&gt;
.oc-contents-table td a {&lt;br /&gt;
    line-height: 1.8;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Don&#039;t let visited links make these navigation boxes ugly */&lt;br /&gt;
.oc-contents-table a:visited {&lt;br /&gt;
    color: #36c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow displays */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    .oc-contents-table {&lt;br /&gt;
        font-size: 0.9em;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:first-child,&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
        width: auto;&lt;br /&gt;
        white-space: normal;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorials&amp;diff=1726</id>
		<title>Tutorials</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorials&amp;diff=1726"/>
		<updated>2026-08-30T16:21:58Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Tutorial]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Onethree&amp;diff=1725</id>
		<title>Onethree</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Onethree&amp;diff=1725"/>
		<updated>2026-08-30T16:21:56Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Tutorial/modding onethree]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/wlan_card&amp;diff=1724</id>
		<title>Item/wlan card</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/wlan_card&amp;diff=1724"/>
		<updated>2026-08-30T16:21:55Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/wireless network card]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/ram&amp;diff=1723</id>
		<title>Item/ram</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/ram&amp;diff=1723"/>
		<updated>2026-08-30T16:21:53Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/memory]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/processor&amp;diff=1722</id>
		<title>Item/processor</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/processor&amp;diff=1722"/>
		<updated>2026-08-30T16:21:52Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/CPU]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/lan_card&amp;diff=1721</id>
		<title>Item/lan card</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/lan_card&amp;diff=1721"/>
		<updated>2026-08-30T16:21:51Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/network card]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/hdd&amp;diff=1720</id>
		<title>Item/hdd</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/hdd&amp;diff=1720"/>
		<updated>2026-08-30T16:21:50Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/hard disk drive]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/floppy&amp;diff=1719</id>
		<title>Item/floppy</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/floppy&amp;diff=1719"/>
		<updated>2026-08-30T16:21:49Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/floppy disk]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/crafting_items&amp;diff=1718</id>
		<title>Item/crafting items</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/crafting_items&amp;diff=1718"/>
		<updated>2026-08-30T16:21:48Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/materials]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/crafting&amp;diff=1717</id>
		<title>Item/crafting</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/crafting&amp;diff=1717"/>
		<updated>2026-08-30T16:21:47Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/materials]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Item/GPU&amp;diff=1716</id>
		<title>Item/GPU</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Item/GPU&amp;diff=1716"/>
		<updated>2026-08-30T16:21:46Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Item/graphics card]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Imc&amp;diff=1715</id>
		<title>Imc</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Imc&amp;diff=1715"/>
		<updated>2026-08-30T16:21:46Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Tutorial/modding imc]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Block/rack&amp;diff=1714</id>
		<title>Block/rack</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Block/rack&amp;diff=1714"/>
		<updated>2026-08-30T16:21:39Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Block/server rack]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Block/hologram&amp;diff=1713</id>
		<title>Block/hologram</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Block/hologram&amp;diff=1713"/>
		<updated>2026-08-30T16:21:38Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Block/hologram projector]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Block/case&amp;diff=1712</id>
		<title>Block/case</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Block/case&amp;diff=1712"/>
		<updated>2026-08-30T16:21:37Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Block/computer case]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Apis&amp;diff=1711</id>
		<title>Apis</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Apis&amp;diff=1711"/>
		<updated>2026-08-30T16:21:36Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[API]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Addons&amp;diff=1710</id>
		<title>Addons</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Addons&amp;diff=1710"/>
		<updated>2026-08-30T16:21:36Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Addon]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=API/colours&amp;diff=1709</id>
		<title>API/colours</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=API/colours&amp;diff=1709"/>
		<updated>2026-08-30T16:21:32Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Convert leftover DokuWiki redirect syntax to MediaWiki redirects&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[API/colors]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Components&amp;diff=1627</id>
		<title>Components</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Components&amp;diff=1627"/>
		<updated>2026-08-27T17:23:01Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Redirected page to Component&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT[[component]]&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1626</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1626"/>
		<updated>2026-08-25T21:47:46Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS placed here will be applied to all skins */&lt;br /&gt;
.client-darkmode .mw-parser-output img,&lt;br /&gt;
.client-darkmode .mw-parser-output .image {&lt;br /&gt;
    filter: invert(1) hue-rotate(180deg);&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    table-layout: fixed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(1),&lt;br /&gt;
.oc-contents-table td:nth-child(1) {&lt;br /&gt;
    width: 12%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(2),&lt;br /&gt;
.oc-contents-table td:nth-child(2) {&lt;br /&gt;
    width: 16%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(3),&lt;br /&gt;
.oc-contents-table td:nth-child(3) {&lt;br /&gt;
    width: 72%;&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
    margin: 0 0 0.75em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Force MediaWiki&#039;s built-in table of contents to the right */&lt;br /&gt;
#toc,&lt;br /&gt;
.toc {&lt;br /&gt;
    float: right;&lt;br /&gt;
    clear: right;&lt;br /&gt;
&lt;br /&gt;
    width: 20em;&lt;br /&gt;
    max-width: 30%;&lt;br /&gt;
&lt;br /&gt;
    margin: 0 0 1em 1.5em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Keep very large TOCs from becoming absurdly tall */&lt;br /&gt;
#toc .tocnumber,&lt;br /&gt;
#toc .toctext {&lt;br /&gt;
    display: inline;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow/mobile screens: return TOC to normal document flow */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    #toc,&lt;br /&gt;
    .toc {&lt;br /&gt;
        float: none;&lt;br /&gt;
        clear: both;&lt;br /&gt;
&lt;br /&gt;
        width: auto;&lt;br /&gt;
        max-width: none;&lt;br /&gt;
&lt;br /&gt;
        margin: 1em 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* OpenComputers documentation navigation tables */&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    max-width: 1400px;&lt;br /&gt;
    margin: 1em auto;&lt;br /&gt;
&lt;br /&gt;
    border: 1px solid #a2a9b1;&lt;br /&gt;
    border-collapse: separate;&lt;br /&gt;
    border-spacing: 0;&lt;br /&gt;
&lt;br /&gt;
    background: #f8f9fa;&lt;br /&gt;
    font-size: 0.95em;&lt;br /&gt;
    line-height: 1.55;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Main column headings */&lt;br /&gt;
.oc-contents-table &amp;gt; tbody &amp;gt; tr:first-child &amp;gt; th {&lt;br /&gt;
    padding: 0.6em 0.9em;&lt;br /&gt;
&lt;br /&gt;
    background: #eaecf0;&lt;br /&gt;
    border-bottom: 1px solid #a2a9b1;&lt;br /&gt;
&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* General cell spacing */&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    padding: 0.55em 0.9em;&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* First column: Components / API / Items / etc. */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:first-child:not(:only-child) {&lt;br /&gt;
    width: 10em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Category column */&lt;br /&gt;
.oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
    width: 13em;&lt;br /&gt;
&lt;br /&gt;
    background: #f1f3f5;&lt;br /&gt;
    border-left: 1px solid #c8ccd1;&lt;br /&gt;
    border-right: 1px solid #c8ccd1;&lt;br /&gt;
&lt;br /&gt;
    text-align: right;&lt;br /&gt;
    white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Documentation/link area */&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    background: #fff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Separate category rows */&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; th,&lt;br /&gt;
.oc-contents-table tr + tr &amp;gt; td {&lt;br /&gt;
    border-top: 1px solid #eaecf0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Make links breathe a little */&lt;br /&gt;
.oc-contents-table td a {&lt;br /&gt;
    line-height: 1.8;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Don&#039;t let visited links make these navigation boxes ugly */&lt;br /&gt;
.oc-contents-table a:visited {&lt;br /&gt;
    color: #36c;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow displays */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    .oc-contents-table {&lt;br /&gt;
        font-size: 0.9em;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:first-child,&lt;br /&gt;
    .oc-contents-table tr &amp;gt; th:nth-child(2) {&lt;br /&gt;
        width: auto;&lt;br /&gt;
        white-space: normal;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1625</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=MediaWiki:Common.css&amp;diff=1625"/>
		<updated>2026-08-25T21:41:35Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS placed here will be applied to all skins */&lt;br /&gt;
.client-darkmode .mw-parser-output img,&lt;br /&gt;
.client-darkmode .mw-parser-output .image {&lt;br /&gt;
    filter: invert(1) hue-rotate(180deg);&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    table-layout: fixed;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(1),&lt;br /&gt;
.oc-contents-table td:nth-child(1) {&lt;br /&gt;
    width: 12%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(2),&lt;br /&gt;
.oc-contents-table td:nth-child(2) {&lt;br /&gt;
    width: 16%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th:nth-child(3),&lt;br /&gt;
.oc-contents-table td:nth-child(3) {&lt;br /&gt;
    width: 72%;&lt;br /&gt;
}&lt;br /&gt;
.oc-contents-table {&lt;br /&gt;
    border-collapse: collapse;&lt;br /&gt;
    margin: 0 0 0.75em 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.oc-contents-table th,&lt;br /&gt;
.oc-contents-table td {&lt;br /&gt;
    vertical-align: top;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Force MediaWiki&#039;s built-in table of contents to the right */&lt;br /&gt;
#toc,&lt;br /&gt;
.toc {&lt;br /&gt;
    float: right;&lt;br /&gt;
    clear: right;&lt;br /&gt;
&lt;br /&gt;
    width: 20em;&lt;br /&gt;
    max-width: 30%;&lt;br /&gt;
&lt;br /&gt;
    margin: 0 0 1em 1.5em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Keep very large TOCs from becoming absurdly tall */&lt;br /&gt;
#toc .tocnumber,&lt;br /&gt;
#toc .toctext {&lt;br /&gt;
    display: inline;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Narrow/mobile screens: return TOC to normal document flow */&lt;br /&gt;
@media screen and (max-width: 850px) {&lt;br /&gt;
    #toc,&lt;br /&gt;
    .toc {&lt;br /&gt;
        float: none;&lt;br /&gt;
        clear: both;&lt;br /&gt;
&lt;br /&gt;
        width: auto;&lt;br /&gt;
        max-width: none;&lt;br /&gt;
&lt;br /&gt;
        margin: 1em 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/translate_rule&amp;diff=1611</id>
		<title>Tutorial/translate rule</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/translate_rule&amp;diff=1611"/>
		<updated>2026-08-24T20:28:31Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= 翻译规则 =&lt;br /&gt;
&lt;br /&gt;
目前已经进行的 OC 官方文档翻译均按照以下规则进行：&lt;br /&gt;
&lt;br /&gt;
* 原页面的中文翻译版本会被创建在 &amp;lt;code&amp;gt;原页面:zh&amp;lt;/code&amp;gt; 位置。 举例：[[openos|Install and Use OpenOS]] 页面位于 &amp;lt;code&amp;gt;:openos&amp;lt;/code&amp;gt; 位置，其中文翻译版本 [[openos/zh|安装和使用 OpenOS]] 位于 &amp;lt;code&amp;gt;:openos:zh&amp;lt;/code&amp;gt; 位置。&lt;br /&gt;
&lt;br /&gt;
* 翻译文章时若遇到了已存在的媒体文件（图片），直接引用该媒体文件，不对媒体文件做汉化。&lt;br /&gt;
&lt;br /&gt;
* 翻译文章时若遇到站内未翻译完成页面的链接，&#039;&#039;&#039;不加入&#039;&#039;&#039; &amp;lt;code&amp;gt;（英文）&amp;lt;/code&amp;gt; 提示。若该页面的中文页面尚未创建且译者暂无时间翻译，则创建该页面、复制英文页面源码到中文页面、改动目录部分并在页面标题下方以粗体标明“本篇文章的部分/全部内容还没有进行翻译。”&lt;br /&gt;
&lt;br /&gt;
* 翻译文章时若遇到站外的英文链接，在链接外加入 &amp;lt;code&amp;gt;（英文）&amp;lt;/code&amp;gt; 提示。 举例：一个连接到 OC 论坛主页（[[https://oc.cil.li/）的链接写作|https://oc.cil.li/）的链接写作]] [https://oc.cil.li/ OC 论坛主页]（英文）。&lt;br /&gt;
&lt;br /&gt;
* 目录将绑定到中文页面，即使中文页面尚未创建。&lt;br /&gt;
&lt;br /&gt;
本站基于 Doku Wiki 创建，且已经加载了 Markdown 插件，因此 Doku Wiki 语法和 Markdown 基本语法均可使用。&lt;br /&gt;
&lt;br /&gt;
本站的所有文章均发布在公有领域（详见页脚），因此若有更好的想法，可以直接在已有的翻译上做修改。Fight_xing 在此建议应当和原译者沟通（查看页面历史以找到原译者），虽然此过程并非完全必要。&lt;br /&gt;
&lt;br /&gt;
还请所有参加编辑的 OC 爱好者们遵循 [https://github.com/sparanoid/chinese-copywriting-guidelines/blob/master/README.zh-Hans.md 中文文案排版指北]，这可以在极低的开销下无形却显著地增强文章可读性。Fight_xing 在此建议应当在链接之间加空格，并且不使用直角引号。若发现因疏漏未遵守排版指北的文章或段落，也请各位不要吝啬于改正。&lt;br /&gt;
&lt;br /&gt;
目前记录的对翻译工作的做出贡献的译者（排名不分先后）：&lt;br /&gt;
&lt;br /&gt;
* Tony🐴[https://www.mcbbs.net/home.php?mod=space&amp;amp;uid=92880 （MCBBS）] - zhou2008[https://space.bilibili.com/627104522/ （Bilibili 个人空间）] - hxt521[https://space.bilibili.com/397524306 （永远爱着 Mona 酱！）] - yhn[https://space.bilibili.com/381986728 （Bilibili 个人空间）] - Fight_xing[https://www.fightxing.com （个人网站）] - HfSr[https://center.mcmod.cn/639473/ （mod百科主页）] - Tree\_er[https://space.bilibili.com/291400420/ （Bilibili 个人空间）]&lt;br /&gt;
&lt;br /&gt;
假如你为翻译做出了贡献，也可以将你的名字登在上面！ 希望通过我们的努力，为 OC 的中文社区做出贡献，让这一优秀的模组出现在更多人的视野中！&lt;br /&gt;
&lt;br /&gt;
== 目录 ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents/zh}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/program/oppm&amp;diff=1610</id>
		<title>Tutorial/program/oppm</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/program/oppm&amp;diff=1610"/>
		<updated>2026-08-24T20:28:30Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Tutorial: The OpenPrograms Package Manager (OPPM) =&lt;br /&gt;
&lt;br /&gt;
The OpenPrograms Package Manager, called OPPM, is a program available through a dungeon [[Item/loot disks|loot disk]] in OpenComputers. It provides a large variety of programs, from now on called &amp;quot;packages&amp;quot;, for you to download and install easily. It is meant to make users able to easily distribute their programs, it is supposed to make installing, updating and uninstalling packages just as easy as creating and registering them.&lt;br /&gt;
&lt;br /&gt;
##Using OPPM For OPPM to work, you will need at least a Tier 2 case and an Internet card inserted. Once you have acquired the floppy disk containing OPPM, the first thing you should do after insertion is run &amp;lt;code&amp;gt;oppm install oppm&amp;lt;/code&amp;gt; to install the latest version of the package manager onto your main hard drive. Once that is done, you can shut down your computer, remove the floppy disk and restart. OPPM supports a few arguments, which will be explained now:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm list [filter] [-i]&amp;lt;/code&amp;gt; This command lists all the available packages by their unique name. The list is sorted by alphabet. The optional argument &amp;lt;code&amp;gt;filter&amp;lt;/code&amp;gt; will make the list only display the packages which have the specified filter in their name. The option -i makes the command only list packages which have already been installed.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm info &amp;amp;lt;package&amp;amp;gt;&amp;lt;/code&amp;gt; Lists further information about the specified program package, such as the full name, the author(s), a description of the package and additional notes the author might have added.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm install [-f] &amp;amp;lt;package&amp;amp;gt; [path]&amp;lt;/code&amp;gt; Downloads the package to a directory on you system; if no &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt; has been specified, it will install it to the default path specified in oppm.cfg (which is /usr by default, and you should always install packages to the default path if you don&#039;t have a very good reason not to do so). The option -f forces the installation, meaning that any already existing file will be overwritten/replaced by the downloaded ones, and if &amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt; points to a non-existing directory, that directory will be created.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm update &amp;amp;lt;package&amp;amp;gt;&amp;lt;/code&amp;gt; This command uninstalls the specified package and re-downloads it, making sure you have the very-most up-to-date version of the package. If &amp;lt;code&amp;gt;package&amp;lt;/code&amp;gt; is &amp;quot;all&amp;quot;, every package that is currently installed will be updated.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm uninstall &amp;amp;lt;package&amp;amp;gt;&amp;lt;/code&amp;gt; Removes every file of the specified package from your system.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm register &amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt; Adds the Github repository found at &amp;lt;code&amp;gt;https://github.com/&amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt; as an additional source of programs. The repository must have a branch named &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;, and that branch must contain a properly structured file named &amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt; in its root directory.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm unregister &amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt; Removes the Github repository found at &amp;lt;code&amp;gt;https://github.com/&amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt; from the list of additional repositories to search.&lt;br /&gt;
&lt;br /&gt;
_Note_: If you only want to use packages provided by other people and do not want to make any packages yourself, you can stop reading now.&lt;br /&gt;
&lt;br /&gt;
##The format of a packages table If you want to create your own packages, either to make it easier to install your software on multiple computers or because you want to share it with other people, you will need to create a Github repository and a packages table. Where the packages table lives depends on the method you use to register your repository, but its format is the same for all methods.&lt;br /&gt;
&lt;br /&gt;
This is the reference for what is allowed to be in your packages table (see [https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/example.cfg example.cfg]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{--This is an example for a programs.cfg file. Please do not add any comments inside actual programs.lua files&lt;br /&gt;
&lt;br /&gt;
[&amp;quot;example-package&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
    [&amp;quot;master/somefolder/bar.lua&amp;quot;] = &amp;quot;/&amp;quot;,--&amp;quot;/&amp;quot; means the file will be placed inside the folder the user specified, defaults to /usr&lt;br /&gt;
    [&amp;quot;master/somefolder/barinfo.txt&amp;quot;] = &amp;quot;//etc&amp;quot;, -- double slash for using an absolute path&lt;br /&gt;
    [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;/share/something&amp;quot;, -- A colon marks a folder, will include everything in that folder&lt;br /&gt;
    [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;//etc/something&amp;quot;, -- This also works with absolute paths&lt;br /&gt;
    [&amp;quot;master/somefolder/barlib.lua&amp;quot;] = &amp;quot;/subfolder&amp;quot;,--Places the file in a subfolder in the user-specified folder&lt;br /&gt;
    [&amp;quot;?master/somefolder/something.cfg&amp;quot;] = &amp;quot;/&amp;quot; -- This file will only be installed or updated if it doesn&#039;t exist already, unless option -f is specified&lt;br /&gt;
  },&lt;br /&gt;
  dependencies = {&lt;br /&gt;
    [&amp;quot;GML&amp;quot;] = &amp;quot;/lib&amp;quot;--This package is installed into the specified subfolder&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;Package name&amp;quot;,--This is for &amp;quot;oppm info&amp;quot;&lt;br /&gt;
  description = &amp;quot;This is an example description&amp;quot;,--This is for &amp;quot;oppm info&amp;quot;&lt;br /&gt;
  authors = &amp;quot;Someone, someone else&amp;quot;,--This is for &amp;quot;oppm info&amp;quot;&lt;br /&gt;
  note = &amp;quot;Additional installation instructions, general instructions and additional information/notes go here, this is an optional line.&amp;quot;,&lt;br /&gt;
  hidden = true, -- Add this optional line to make your package not visible in &amp;quot;oppm list&amp;quot;, useful for custom dependency libraries&lt;br /&gt;
  repo=&amp;quot;tree/master/somefolder&amp;quot; --Used by the website. This is where the package will link to on the website&lt;br /&gt;
},&lt;br /&gt;
[&amp;quot;yet-another-package&amp;quot;] = {&lt;br /&gt;
        ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is an example of a packages table for three real programs: &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
[&amp;quot;song&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
    [&amp;quot;master/song/song.lua&amp;quot;] = &amp;quot;/lib&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/song/song-example1.lua&amp;quot;] = &amp;quot;/bin&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/song/song-example2.lua&amp;quot;] = &amp;quot;/bin&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;Song API&amp;quot;,&lt;br /&gt;
  description = &amp;quot;An API to play whole songs using computer.beep&amp;quot;,&lt;br /&gt;
  authors = &amp;quot;Vexatos&amp;quot;,&lt;br /&gt;
  repo = &amp;quot;tree/master/song&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
[&amp;quot;oppm&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
    [&amp;quot;master/oppm/oppm.lua&amp;quot;] = &amp;quot;/bin&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/oppm/etc/oppm.cfg&amp;quot;] = &amp;quot;//etc&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/oppm/lib/oppm.lua&amp;quot;] = &amp;quot;/lib&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;OpenPrograms Package Manager&amp;quot;,&lt;br /&gt;
  description = &amp;quot;A program to browse, download, install and update various useful programs and libraries&amp;quot;,&lt;br /&gt;
  authors = &amp;quot;Vexatos&amp;quot;,&lt;br /&gt;
  note = &amp;quot;If you are running this program on a floppy disk, run &#039;oppm install oppm&#039; to install this program locally on your main Hard Drive.\n Consider running &#039;oppm update oppm&#039; to get the latest version of this program.&amp;quot;,&lt;br /&gt;
  repo = &amp;quot;tree/master/oppm&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
[&amp;quot;drama&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
      [&amp;quot;master/drama/drama.lua&amp;quot;] = &amp;quot;/bin&amp;quot;,&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;Drama Generator&amp;quot;,&lt;br /&gt;
  description = &amp;quot;asie&#039;s Drama Generator inside OC&amp;quot;,&lt;br /&gt;
  authors = &amp;quot;Vexatos&amp;quot;,&lt;br /&gt;
  note = &amp;quot;Run and have fun!&amp;quot;,&lt;br /&gt;
  repo = &amp;quot;tree/master/drama/drama.lua&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##Registering a repository OPPM needs to know how to find your packages table and the files that make up the packages it describes. Depending on your goal, there are three options available.&lt;br /&gt;
&lt;br /&gt;
###oppm register If you are developing packages that are only for your own use (e.g. as a way to easily install software you wrote on multiple computers, or because you want to use an external editor to edit your code and install it on an OpenComputers computer), the &amp;lt;code&amp;gt;oppm register&amp;lt;/code&amp;gt; command is probably the easiest option.&lt;br /&gt;
&lt;br /&gt;
# Create a public GitHub repository. 2. Within the repository, ensure there is a branch named &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;; OPPM will only look for &amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt; in this branch. 3. In the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch, at the top level of the repository, create a file named &amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt; containing your packages table. 4. Make sure you have pushed all your work. 5. Run &amp;lt;code&amp;gt;oppm register&amp;lt;/code&amp;gt; for your repository name; for example, if your GitHub username is &amp;lt;code&amp;gt;MyUser&amp;lt;/code&amp;gt; and you named your repository &amp;lt;code&amp;gt;MyOCPackages&amp;lt;/code&amp;gt;, you would run &amp;lt;code&amp;gt;oppm register MyUser/MyOCPackages&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Your packages will now appear in &amp;lt;code&amp;gt;oppm list&amp;lt;/code&amp;gt; and can be installed with &amp;lt;code&amp;gt;oppm install&amp;lt;/code&amp;gt; just like any other packages. Any changes you make to &amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt; or to the files making up your packages will become available to OPPM, though due to caching in GitHub’s systems you may need to wait a short time after pushing, and due to caching in OPPM you may need to reboot the OpenComputers computer before you will see the changes.&lt;br /&gt;
&lt;br /&gt;
Of course, you can share the name of your repository with other people who can also register it and use your programs if you wish.&lt;br /&gt;
&lt;br /&gt;
Note that you do not need to develop all your software on the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch. &amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt; needs to live at the top level of the &amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt; branch, but since the first path component of each element in the &amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt; table is a branch name, you can refer to programs in other branches.&lt;br /&gt;
&lt;br /&gt;
###Registering packages globally&lt;br /&gt;
&lt;br /&gt;
If you want to make your packages globally available (so that everyone that wants to is able to easily install your programs), you need to ask Vexatos on the [http://oc.cil.li/index.php?/index OpenComputers forums] or on IRC to register your GitHub repository. Your GitHub repository needs to look exactly the same as described in [[#oppm register|the previous section]].&lt;br /&gt;
&lt;br /&gt;
###oppm.cfg You can also add an additional repository by editing &amp;lt;code&amp;gt;/etc/oppm.cfg&amp;lt;/code&amp;gt;. The difference with this method is that you need to place the entire packages table in &amp;lt;code&amp;gt;/etc/oppm.cfg&amp;lt;/code&amp;gt; rather than in the repository (the repository can contain a packages table, but it will be ignored if you use this method).&lt;br /&gt;
&lt;br /&gt;
In most cases [[#oppm register|#oppm register]] is preferable because it allows you to write the packages table only once (rather than having to copy it to every computer and keep it up to date) and to keep it together in the same place as your code and data files. However, this method may be useful if you wish to install Lua programs from some repository that is not your own, and that repository does not provide a &amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
Open /etc/oppm.cfg; it should look like this: &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  --default installation path&lt;br /&gt;
  path=&amp;quot;/usr&amp;quot;,&lt;br /&gt;
  --Additional repositories and packages go here, for correct package syntax, check https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/etc/oppm.cfg&lt;br /&gt;
  repos={&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You might notice the table called &amp;lt;code&amp;gt;repos&amp;lt;/code&amp;gt;. That’s where you can register your own packages. Each entry in the table has a repository name (GitHub &amp;lt;code&amp;gt;UserOrGroup/RepoName&amp;lt;/code&amp;gt;) as its key and the corresponding packages table as its value. This is an example &amp;lt;code&amp;gt;oppm.cfg&amp;lt;/code&amp;gt; file, having added two different packages to OPPM (The first one as a [https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/etc/example-config.cfg reference], the second one as an actual example):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
--default installation path&lt;br /&gt;
path=&amp;quot;/usr&amp;quot;,&lt;br /&gt;
--Additional repositories and packages go here, for correct package syntax, check https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/etc/example-config.cfg&lt;br /&gt;
repos={&lt;br /&gt;
  [&amp;quot;YourUsername/YourRepo&amp;quot;] = {&lt;br /&gt;
    [&amp;quot;example-package&amp;quot;] = {&lt;br /&gt;
      files = {&lt;br /&gt;
        [&amp;quot;master/somefolder/bar.lua&amp;quot;] = &amp;quot;/&amp;quot;,--&amp;quot;/&amp;quot; means the file will be placed inside the folder the user specified, defaults to /usr&lt;br /&gt;
        [&amp;quot;master/somefolder/barinfo.txt&amp;quot;] = &amp;quot;//etc&amp;quot;, -- double slash for using an absolute path&lt;br /&gt;
        [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;/share/something&amp;quot;, -- A colon marks a folder, will include everything in that folder&lt;br /&gt;
        [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;//etc/something&amp;quot;, -- This also works with absolute paths&lt;br /&gt;
        [&amp;quot;master/somefolder/barlib.lua&amp;quot;] = &amp;quot;/subfolder&amp;quot;,--Places the file in a subfolder in the user-specified folder&lt;br /&gt;
        [&amp;quot;master/somefolder/libfolder/&amp;quot;] = &amp;quot;/&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      dependencies = {&lt;br /&gt;
        [&amp;quot;GML&amp;quot;] = &amp;quot;/lib&amp;quot;--This package is installed into the specified subfolder&lt;br /&gt;
      },&lt;br /&gt;
      name = &amp;quot;Package name&amp;quot;,--This is for &amp;quot;oppm info&amp;quot;&lt;br /&gt;
      description = &amp;quot;This is an example description&amp;quot;,--This is for &amp;quot;oppm info&amp;quot;&lt;br /&gt;
      authors = &amp;quot;Someone, someone else&amp;quot;,--This is for &amp;quot;oppm info&amp;quot;&lt;br /&gt;
      note = &amp;quot;Additional installation instructions, general instructions and additional information/notes go here, this is an optional line.&amp;quot;,&lt;br /&gt;
      hidden = true, -- Add this optional line to make your package not visible in &amp;quot;oppm list&amp;quot;, useful for custom dependency libraries&lt;br /&gt;
      repo=&amp;quot;tree/master/somefolder&amp;quot; --Used by the website. This is where the package will link to on the website&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;yet-another-package&amp;quot;] = {&lt;br /&gt;
            ...&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  [&amp;quot;OpenPrograms/samis-Programs&amp;quot;]={&lt;br /&gt;
    [&amp;quot;nidus&amp;quot;] = {&lt;br /&gt;
      [&amp;quot;files&amp;quot;] = {&lt;br /&gt;
        [&amp;quot;master/nidus/nidus.lua&amp;quot;] = &amp;quot;/bin&amp;quot;, --executable programs should always be installed to /bin&lt;br /&gt;
        [&amp;quot;master/nidus/core.lua&amp;quot;] = &amp;quot;/lib/nidus&amp;quot;, --libraries should always be installed to /lib&lt;br /&gt;
        [&amp;quot;master/nidus/hosts.db&amp;quot;] = &amp;quot;//var/lib/nidus&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      [&amp;quot;repo&amp;quot;] = &amp;quot;tree/master/nidus&amp;quot;,&lt;br /&gt;
      [&amp;quot;dependencies&amp;quot;] = {&lt;br /&gt;
        [&amp;quot;oop-system&amp;quot;] = &amp;quot;/&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      [&amp;quot;name&amp;quot;] = &amp;quot;NiDuS DNS Server&amp;quot;,&lt;br /&gt;
      [&amp;quot;description&amp;quot;] = &amp;quot;A DNS server that is light and easy to use. Uses its own protocol.&amp;quot;,&lt;br /&gt;
      [&amp;quot;authors&amp;quot;] = &amp;quot;samis&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I hope this tutorial helped explaining what OPPM does and how to use it. If you have any further questions, contact me (Vexatos) on the [http://oc.cil.li/index.php?/index OC forums] or on IRC.&lt;br /&gt;
&lt;br /&gt;
Thanks for reading!&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/program/oppm/zh&amp;diff=1609</id>
		<title>Tutorial/program/oppm/zh</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/program/oppm/zh&amp;diff=1609"/>
		<updated>2026-08-24T20:28:29Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= 教程：开放式软件包管理器(OPPM) =&lt;br /&gt;
&lt;br /&gt;
开放式软件包管理器（The OpenPrograms Package Manager），简称OPPM，是OpenComputers模组的一个可从[[Item/loot disks/zh|奖励箱软盘]]中获取的软件。此软件提供了种类繁多的程序（下文称为“软件包”）供您方便地下载与安装。此软件致力于帮助用户降低分发自己程序的难度，在此软件上安装、升级与卸载软件就像创建与登记软件那么容易。&lt;br /&gt;
&lt;br /&gt;
##使用OPPM 为了能够使用OPPM，你需要有至少2级的机箱，并插入因特网卡。 在你获得了装有OPPM的软盘之后，先将它插到电脑里，然后你做的第一件事应该是运行&amp;lt;code&amp;gt;oppm install oppm&amp;lt;/code&amp;gt;，来将最新版本的软件包管理器安装到你的主硬盘上。安装完成后，你可以随意关闭电脑、取出磁盘或重启电脑。&lt;br /&gt;
&lt;br /&gt;
OPPM支持若干参数，参数将会在下文讲解：&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm list [filter] [-i]&amp;lt;/code&amp;gt; 此命令将会列出所有可用软件包的名称。列表按字母顺序排列。可选参数&amp;lt;code&amp;gt;filter&amp;lt;/code&amp;gt;可以让列表只显示名称匹配指定过滤条件的软件包。&amp;lt;code&amp;gt;-i&amp;lt;/code&amp;gt;选项可以让命令仅列出已经被安装的软件包。&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm info &amp;amp;lt;package&amp;amp;gt;&amp;lt;/code&amp;gt; 列出指定软件包的详细信息，例如完整名称、作者、软件包描述以及作者可能添加的其他额外注释。&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm install [-f] &amp;amp;lt;package&amp;amp;gt; [path]&amp;lt;/code&amp;gt; 将软件包下载到你系统中的某个目录；若不指定&amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt;参数，则会安装到oppm.cfg中指定的默认路径（默认为/usr，如果你没有特别理由，就不要改动安装位置）。&amp;lt;code&amp;gt;-f&amp;lt;/code&amp;gt;选项可以强制安装，即已存在的任何文件都将被下载的文件覆盖/替换。并且如果&amp;lt;code&amp;gt;path&amp;lt;/code&amp;gt;指向不存在的目录，会先创建此目录。&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm update &amp;amp;lt;package&amp;amp;gt;&amp;lt;/code&amp;gt; 此命令将会卸载并重新安装指定软件包，以确保你能拥有软件包的最新版本。若&amp;lt;code&amp;gt;package&amp;lt;/code&amp;gt;参数为&amp;quot;all&amp;quot;，则会更新已安装的所有软件包。&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm uninstall &amp;amp;lt;package&amp;amp;gt;&amp;lt;/code&amp;gt; 从你的系统中移除指定软件包的所有文件。&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm register &amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt; 将位于&amp;lt;code&amp;gt;https://github.com/&amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt;的Github仓库作为额外软件源添加。仓库中必须有名为&amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;的分支，且此分支的根目录下必须有名为&amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt;的文件结构配置文件。&lt;br /&gt;
* &amp;lt;code&amp;gt;oppm unregister &amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt; 从额外软件源列表中移除位于&amp;lt;code&amp;gt;https://github.com/&amp;amp;lt;userorgroup&amp;amp;gt;/&amp;amp;lt;repository&amp;amp;gt;&amp;lt;/code&amp;gt;的Github仓库。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;注意：&#039;&#039;&#039;如果你只是想使用他人提供的软件包，而并不想自己制作软件包，那么可以停止阅读了。&lt;br /&gt;
&lt;br /&gt;
##软件包列表格式 如果你想创建自己的软件包，不论是因为你想更方便地在多台电脑上安装软件还是因为你想与他人分享软件，你需要创建一个Github仓库和一份软件包列表。软件包列表的位置取决你登记仓库的方式，但无论何种方式，其格式均相同。&lt;br /&gt;
&lt;br /&gt;
这是软件包列表中可存在内容的参考（参见[https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/example.cfg example.cfg]）：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{--这是programs.cfg文件的一份样例。请勿在实际的programs.lua文件中添加任何注释&lt;br /&gt;
&lt;br /&gt;
[&amp;quot;example-package&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
    [&amp;quot;master/somefolder/bar.lua&amp;quot;] = &amp;quot;/&amp;quot;,--&amp;quot;/&amp;quot;斜线代表文件将会被置于用户指定的文件夹中，默认为/usr&lt;br /&gt;
    [&amp;quot;master/somefolder/barinfo.txt&amp;quot;] = &amp;quot;//etc&amp;quot;, --双斜线代表使用绝对路径&lt;br /&gt;
    [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;/share/something&amp;quot;, --冒号代表一个文件夹，将包含此文件夹中的所有内容&lt;br /&gt;
    [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;//etc/something&amp;quot;, --此写法也可用于绝对路径&lt;br /&gt;
    [&amp;quot;master/somefolder/barlib.lua&amp;quot;] = &amp;quot;/subfolder&amp;quot;,--将文件置于用户指定文件夹的一个子文件夹中&lt;br /&gt;
    [&amp;quot;?master/somefolder/something.cfg&amp;quot;] = &amp;quot;/&amp;quot; --此文件只会在并不存在时才会安装或更新，除非指定-f选项&lt;br /&gt;
  },&lt;br /&gt;
  dependencies = {&lt;br /&gt;
    [&amp;quot;GML&amp;quot;] = &amp;quot;/lib&amp;quot;--此软件包将会被安装到指定子文件夹中&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;Package name&amp;quot;,--此属性用于&amp;quot;oppm info&amp;quot;&lt;br /&gt;
  description = &amp;quot;This is an example description&amp;quot;,--此属性用于&amp;quot;oppm info&amp;quot;&lt;br /&gt;
  authors = &amp;quot;Someone, someone else&amp;quot;,--此属性用于&amp;quot;oppm info&amp;quot;&lt;br /&gt;
  note = &amp;quot;额外安装教程、通用教程以及额外信息/注释写在此处，此行内容为可选。&amp;quot;,&lt;br /&gt;
  hidden = true, --添加此行可选内容，可以让你的软件包不显示在&amp;quot;oppm list&amp;quot;中，适用于自定义依赖库&lt;br /&gt;
  repo=&amp;quot;tree/master/somefolder&amp;quot; --供网站使用。这代表软件包应当链接到网站何处&lt;br /&gt;
},&lt;br /&gt;
[&amp;quot;yet-another-package&amp;quot;] = {&lt;br /&gt;
        ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
以下为三个真实存在软件的软件包列表样例： &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
[&amp;quot;song&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
    [&amp;quot;master/song/song.lua&amp;quot;] = &amp;quot;/lib&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/song/song-example1.lua&amp;quot;] = &amp;quot;/bin&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/song/song-example2.lua&amp;quot;] = &amp;quot;/bin&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;Song API&amp;quot;,&lt;br /&gt;
  description = &amp;quot;An API to play whole songs using computer.beep&amp;quot;,&lt;br /&gt;
  authors = &amp;quot;Vexatos&amp;quot;,&lt;br /&gt;
  repo = &amp;quot;tree/master/song&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
[&amp;quot;oppm&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
    [&amp;quot;master/oppm/oppm.lua&amp;quot;] = &amp;quot;/bin&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/oppm/etc/oppm.cfg&amp;quot;] = &amp;quot;//etc&amp;quot;,&lt;br /&gt;
    [&amp;quot;master/oppm/lib/oppm.lua&amp;quot;] = &amp;quot;/lib&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;OpenPrograms Package Manager&amp;quot;,&lt;br /&gt;
  description = &amp;quot;A program to browse, download, install and update various useful programs and libraries&amp;quot;,&lt;br /&gt;
  authors = &amp;quot;Vexatos&amp;quot;,&lt;br /&gt;
  note = &amp;quot;If you are running this program on a floppy disk, run &#039;oppm install oppm&#039; to install this program locally on your main Hard Drive.\n Consider running &#039;oppm update oppm&#039; to get the latest version of this program.&amp;quot;,&lt;br /&gt;
  repo = &amp;quot;tree/master/oppm&amp;quot;&lt;br /&gt;
},&lt;br /&gt;
[&amp;quot;drama&amp;quot;] = {&lt;br /&gt;
  files = {&lt;br /&gt;
      [&amp;quot;master/drama/drama.lua&amp;quot;] = &amp;quot;/bin&amp;quot;,&lt;br /&gt;
  },&lt;br /&gt;
  name = &amp;quot;Drama Generator&amp;quot;,&lt;br /&gt;
  description = &amp;quot;asie&#039;s Drama Generator inside OC&amp;quot;,&lt;br /&gt;
  authors = &amp;quot;Vexatos&amp;quot;,&lt;br /&gt;
  note = &amp;quot;Run and have fun!&amp;quot;,&lt;br /&gt;
  repo = &amp;quot;tree/master/drama/drama.lua&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
##登记仓库 OPPM需要知道如何找到你的软件包列表以及其中描述的组成软件包所需的文件。根据你的目标不同，你有三种选择。&lt;br /&gt;
&lt;br /&gt;
###OPPM登记 如果你开发的是只有自己使用的软件包（例如，用于更方便地将你编写的软件安装在多台电脑上，或者你想用外部编辑器来编写代码并将其安装到OC模组的电脑上），那么、&amp;lt;code&amp;gt;oppm register&amp;lt;/code&amp;gt;命令可能是最简单的选择。&lt;br /&gt;
&lt;br /&gt;
# 创建一个公开Github仓库。 2. 在仓库中，确保有名为&amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;的分支；OPPM只会在此分支内查找&amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt;文件。 3. 在&amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;分支中，在仓库的顶级目录中创建一个名为&amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt;的文件，其中包含你的软件包列表。 4. 确保你已经推送了所有文件。 5. 以你的仓库名为参数运行&amp;lt;code&amp;gt;oppm register&amp;lt;/code&amp;gt;命令。例如，你的Github用户名为&amp;lt;code&amp;gt;MyUser&amp;lt;/code&amp;gt;并且你将仓库命名为&amp;lt;code&amp;gt;MyOCPackages&amp;lt;/code&amp;gt;，你需要运行&amp;lt;code&amp;gt;oppm register MyUser/MyOCPackages&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
你的软件包现在会在&amp;lt;code&amp;gt;oppm list&amp;lt;/code&amp;gt;中显示了，并且可以用&amp;lt;code&amp;gt;oppm install&amp;lt;/code&amp;gt;安装，就像其他软件包一样。你对&amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt;或者构成你软件包的文件作出的改动都可以影响到OPPM。但是因为Github系统有缓存，你在推送更改后可能需要等一会。而且OPPM也有缓存，你可能需要重启OC模组的电脑才能看到改动。&lt;br /&gt;
&lt;br /&gt;
当然，只要你愿意，你可以把你的仓库名分享给其他人，他们也可以登记这个仓库并且使用你的软件。&lt;br /&gt;
&lt;br /&gt;
请注意，你不必把所有软件都存放到&amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;分支中。只有&amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt;必须位于&amp;lt;code&amp;gt;master&amp;lt;/code&amp;gt;分支的顶级目录中，但是鉴于&amp;lt;code&amp;gt;files&amp;lt;/code&amp;gt;表中每个元素的第一个组成部分都是分支名，你也可以指向位于其他分支的软件。&lt;br /&gt;
&lt;br /&gt;
###将软件包全球登记&lt;br /&gt;
&lt;br /&gt;
如果你想让你的软件包对全世界公开（这样喜欢你软件的人都可以很方便的安装了），那么你需要去[http://oc.cil.li/index.php?/index OpenComputers官方论坛]或官方IRC找到Vexatos，让他登记你的Github仓库。你的仓库必须与[[#oppm登记|上一章节]]所描述的一致。&lt;br /&gt;
&lt;br /&gt;
###oppm.cfg 你还可以通过编辑&amp;lt;code&amp;gt;/etc/oppm.cfg&amp;lt;/code&amp;gt;来添加额外仓库。这种做法的不同之处在于你需要把整个软件包列表都放到&amp;lt;code&amp;gt;/etc/oppm.cfg&amp;lt;/code&amp;gt;中，而不是放到仓库中（仓库中可以有软件包列表，但是如果使用此方法则会被忽略）。&lt;br /&gt;
&lt;br /&gt;
大多数情况下[[#OPPM登记|#OPPM登记]]更好，因为你只需要编写一次软件包列表（而无需将它复制到每一台电脑中，并且保持版本更新），还能将列表与你的代码和数据文件放在一起。然而，如果你想安装来自不属于你而且没有&amp;lt;code&amp;gt;programs.cfg&amp;lt;/code&amp;gt;的仓库中的Lua程序，此方法可能会有所帮助。&lt;br /&gt;
&lt;br /&gt;
打开/etc/oppm.cfg；文件内容应当如下文所示： &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  --默认安装路径&lt;br /&gt;
  path=&amp;quot;/usr&amp;quot;,&lt;br /&gt;
  --额外仓库与软件包位于此处，参阅https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/etc/oppm.cfg以获取正确的软件包语法&lt;br /&gt;
  repos={&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
你可能会注意到名为&amp;lt;code&amp;gt;repos&amp;lt;/code&amp;gt;的表。 这就是你登记你自己软件包的地方。表中的每一项都以一个仓库名（GitHub链接的&amp;lt;code&amp;gt;用户或小组名/仓库名&amp;lt;/code&amp;gt;）作为键，以对应的软件包列表作为值。下面是&amp;lt;code&amp;gt;oppm.cfg&amp;lt;/code&amp;gt;文件的一份样例，其中向OPPM添加了两个不同的软件包（第一个作为[https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/etc/example-config.cfg 参考]，第二个为真实样例）：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
--默认安装路径&lt;br /&gt;
path=&amp;quot;/usr&amp;quot;,&lt;br /&gt;
--额外仓库与软件包位于此处，参阅https://github.com/OpenPrograms/Vexatos-Programs/blob/master/oppm/etc/oppm.cfg以获取正确的软件包语法&lt;br /&gt;
repos={&lt;br /&gt;
  [&amp;quot;YourUsername/YourRepo&amp;quot;] = {&lt;br /&gt;
    [&amp;quot;example-package&amp;quot;] = {&lt;br /&gt;
      files = {&lt;br /&gt;
        [&amp;quot;master/somefolder/bar.lua&amp;quot;] = &amp;quot;/&amp;quot;,--&amp;quot;/&amp;quot;斜线代表文件将会被置于用户指定文件夹中，默认为/usr&lt;br /&gt;
        [&amp;quot;master/somefolder/barinfo.txt&amp;quot;] = &amp;quot;//etc&amp;quot;, -- 双斜线代表使用绝对路径&lt;br /&gt;
        [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;/share/something&amp;quot;, --冒号代表一个文件夹，将包含此文件夹中的所有内容&lt;br /&gt;
        [&amp;quot;:master/otherfolder&amp;quot;] = &amp;quot;//etc/something&amp;quot;, --此写法也可用于绝对路径&lt;br /&gt;
        [&amp;quot;master/somefolder/barlib.lua&amp;quot;] = &amp;quot;/subfolder&amp;quot;,--将文件置于用于指定文件夹的一个子文件夹中&lt;br /&gt;
        [&amp;quot;master/somefolder/libfolder/&amp;quot;] = &amp;quot;/&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      dependencies = {&lt;br /&gt;
        [&amp;quot;GML&amp;quot;] = &amp;quot;/lib&amp;quot;--此软件包将会被安装到指定子文件夹中&lt;br /&gt;
      },&lt;br /&gt;
      name = &amp;quot;Package name&amp;quot;,--此属性用于&amp;quot;oppm info&amp;quot;&lt;br /&gt;
      description = &amp;quot;This is an example description&amp;quot;,--此属性用于&amp;quot;oppm info&amp;quot;&lt;br /&gt;
      authors = &amp;quot;Someone, someone else&amp;quot;,--此属性用于&amp;quot;oppm info&amp;quot;&lt;br /&gt;
      note = &amp;quot;额外安装教程、通用教程以及额外信息/注释写在此处，此行内容为可选。&amp;quot;,&lt;br /&gt;
      hidden = true, --添加此行可选内容，可以让你的软件包不显示在&amp;quot;oppm list&amp;quot;中，适用于自定义依赖库&lt;br /&gt;
      repo=&amp;quot;tree/master/somefolder&amp;quot; --供网站使用。这代表软件包应当链接到网站何处&lt;br /&gt;
    },&lt;br /&gt;
    [&amp;quot;yet-another-package&amp;quot;] = {&lt;br /&gt;
            ...&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  [&amp;quot;OpenPrograms/samis-Programs&amp;quot;]={&lt;br /&gt;
    [&amp;quot;nidus&amp;quot;] = {&lt;br /&gt;
      [&amp;quot;files&amp;quot;] = {&lt;br /&gt;
        [&amp;quot;master/nidus/nidus.lua&amp;quot;] = &amp;quot;/bin&amp;quot;, --可执行文件应当安装到 /bin 目录&lt;br /&gt;
        [&amp;quot;master/nidus/core.lua&amp;quot;] = &amp;quot;/lib/nidus&amp;quot;, --运行库应当被安装到 /lib 目录&lt;br /&gt;
        [&amp;quot;master/nidus/hosts.db&amp;quot;] = &amp;quot;//var/lib/nidus&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      [&amp;quot;repo&amp;quot;] = &amp;quot;tree/master/nidus&amp;quot;,&lt;br /&gt;
      [&amp;quot;dependencies&amp;quot;] = {&lt;br /&gt;
        [&amp;quot;oop-system&amp;quot;] = &amp;quot;/&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      [&amp;quot;name&amp;quot;] = &amp;quot;NiDuS DNS Server&amp;quot;,&lt;br /&gt;
      [&amp;quot;description&amp;quot;] = &amp;quot;A DNS server that is light and easy to use. Uses its own protocol.&amp;quot;,&lt;br /&gt;
      [&amp;quot;authors&amp;quot;] = &amp;quot;samis&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
我希望这篇教程让您理解了OPPM是什么，以及如何使用它。如果你还有进一步的问题，请在[http://oc.cil.li/index.php?/index OC论坛]或IRC上联系我（Vexatos）。&lt;br /&gt;
&lt;br /&gt;
感谢阅读！&lt;br /&gt;
&lt;br /&gt;
== 目录 ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents/zh}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/program/install&amp;diff=1608</id>
		<title>Tutorial/program/install</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/program/install&amp;diff=1608"/>
		<updated>2026-08-24T20:28:29Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Tutorial: The Install Program =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; is an application that comes shipped with [[openos|OpenOS]]. For most users and in most computer configurations it is expected that the primary method of installing OpenOS is by using this very same &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; application. &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; is also designed to install the software, libraries, and help scripts that come bundled with all the craftable mod-provided [[Item/loot disks|loot disks]].&lt;br /&gt;
&lt;br /&gt;
To understand more about command line options for &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;, it is recommended to read its man pages by running &amp;lt;code&amp;gt;man install&amp;lt;/code&amp;gt;, or by reading the man page online [https://raw.githubusercontent.com/MightyPirates/OpenComputers/master-MC1.7.10/src/main/resources/assets/opencomputers/loot/openos/usr/man/install here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; takes the following actions&lt;br /&gt;
&lt;br /&gt;
* Step One: Scan for Software to Install&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;First it scans for candidate source filesystems. These are filesystems, such as [[:item:loot disks|loot disks]],  that can be used as a software package for installation. If more than one candidate source filesystem is found, it prompts the user, asking&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;What do you want to install?&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Followed by a list of disks it found that can be installed.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Step Two: Scan for Hard Drives&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;The next step is a scan for candidate target filesystems.  These are filesystems, such as hard drives, that can be the target of an install. In the example of installing OpenOS from a loot disk to the hard drive, the hard drive is the target filesystem. Like candidate sources, if install finds multiple candidate targets, it asks the user to select one:&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Where do you want to install to?&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Followed by a list of disks it found that can be installed **to**.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Step Three: Installation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Before continuing with the install, the user is asked for confirmation to install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Install OpenOS to /mnt/e03/? [Y/n]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Confirming this step will copy the files from the (e.g.) loot disk to the target filesystem. Software installs may have an optional .prop file which can tell install whether or not to set the default filesystem the computer should boot to, what label if any to set, and whether the system should reboot when installation is complete.&lt;br /&gt;
There is also the option for software disks to provide a fully custom install experience by creating an .install file at the root of the disk&#039;s filesystem. After confirming the source and target, install will invoke .install if it exists in the source filesystem.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Optional Arguments&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;It is recommend to review the install man page for greater details and a full list of supported arguments. But I considered it interesting to mention here that the label of the loot disk can be used as a command line argument for install -- which will refine the candidate search to disks matching that label.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;install openos&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Note that the argument is case insensitive. In a scenario where there would have been multiple software disks available to install, specifying the label in this manner may allow install to reduce the candidate selection without prompting the user.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Installing Additional Software ==&lt;br /&gt;
&lt;br /&gt;
Besides installing loot disks (such as openos), It is intended that users can take advantage of the &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; program for custom software disks. If you are providing software distributed on a portable filesystem, you can expect &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; to be a useful utility. For this documentation we&#039;ll assume you are distributing your software via floppy disk, though &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; does not distinguish between any filesystem component, floppy or hard disk or other.&lt;br /&gt;
&lt;br /&gt;
The most basic and default way to use &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; with your software disk is to do nothing, and it&#039;ll just sort of work. &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; checks all available filesystems that have any files and considers them candidates for installation. The user is prompted to select what to install, and &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; does a very simple copy of all files in that disk to the selected destination. This is actually how OpenOS itself installs.&lt;br /&gt;
&lt;br /&gt;
You have some control over how install behaves by creating a custom .prop and/or a custom .install file at the root of your software distribution disk. The .prop file is expected to be a valid lua table that set optional flags for &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;. For example, the openos .prop file contents are: &amp;lt;code&amp;gt;{label = &amp;quot;OpenOS&amp;quot;, reboot=true, setlabel=true, setboot=true}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;&#039;s default copy action skips .prop (.prop is not copied).&lt;br /&gt;
&lt;br /&gt;
* You can set a custom label&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; can refer to and label installation options. By default, &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; uses the filesystem label (or the filesystem address if no label is set). This label can be helpful for &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; and the user experience. The user can actually tell &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; what to install from a command line argument before even being prompted about install options. For example, if you type &amp;lt;code&amp;gt;install openos&amp;lt;/code&amp;gt;, and in the chance there were other installation options -- &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; will only give the openos option for install. It is a way for a user to specifically get what they want before being asked. In addition to this, &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; uses the same labelling logic when listing the install options.&lt;br /&gt;
&lt;br /&gt;
You can override the label &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; uses by defining the &amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt; table key in your &amp;lt;code&amp;gt;.prop&amp;lt;/code&amp;gt; file. If you want your users to be able to say: &amp;lt;code&amp;gt;install my_cool_stuff&amp;lt;/code&amp;gt;, you&#039;ll want to create a .prop file at the root of your software disk that is: &amp;lt;code&amp;gt;{label=&amp;quot;my_cool_stuff&amp;quot;}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Be ignored (hide from install)&lt;br /&gt;
&lt;br /&gt;
In the case that you are using &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; and also using multiple filesystems and floppies, it might become an annoyance that &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; always includes disks as candidate installation sources when you&#039;d really just prefer it ignore it. You can create a .prop file that has &amp;lt;code&amp;gt;{ignore=true}&amp;lt;/code&amp;gt; and the next time &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; is run, it won&#039;t list that filesystem as a source option.&lt;br /&gt;
&lt;br /&gt;
* Custom install action&lt;br /&gt;
&lt;br /&gt;
If you have a more complex set of operations than JUST copying files (or even setting the boot disk, setting labels, or rebooting), for example you might want to limit which files are copied, and where they are copied to (the default copy destination is the root of the target filesystem). Note that a user can control which sub directory are copied and to where using &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; arguments, but perhaps you want to make your software disk&#039;s installation a bit more supportive of the user, and you might want to configure the install steps yourself.&lt;br /&gt;
&lt;br /&gt;
Once a user has confirmed your software disk as the installation source, &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; will check for the existence of a &amp;lt;code&amp;gt;.install&amp;lt;/code&amp;gt; file (please note that preceding dot in the filename, just like with &amp;lt;code&amp;gt;.prop&amp;lt;/code&amp;gt;). IF that file exists, &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; does NOT copy any files, but instead it runs your custom &amp;lt;code&amp;gt;.install&amp;lt;/code&amp;gt; as a script (and then does nothing else). Once your &amp;lt;code&amp;gt;.install&amp;lt;/code&amp;gt; script is running you have full control of how to finish the install process.&lt;br /&gt;
&lt;br /&gt;
Your custom &amp;lt;code&amp;gt;.install&amp;lt;/code&amp;gt; script is given in its loaded environment a helpful &amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt; table that contains all the options that the &amp;lt;code&amp;gt;/bin/install&amp;lt;/code&amp;gt; program has been able to learn thus far.&lt;br /&gt;
&lt;br /&gt;
For example, if this was my custom &amp;lt;code&amp;gt;.install&amp;lt;/code&amp;gt; script, in its entirety (yes, NO OTHER INCLUDES):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for k,v in pairs(install) do&lt;br /&gt;
&lt;br /&gt;
io.write(k, &amp;quot; -&amp;gt; &amp;quot;, v)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would be the output (my filesystem was mounted on /mnt/c2b, and my .prop file had &amp;lt;code&amp;gt;{label=&amp;quot;foo&amp;quot;}&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from -&amp;gt; /mnt/c2b root -&amp;gt; label -&amp;gt; foo to -&amp;gt; // fromDir -&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The user could have also optionally used some command line args, such as: &amp;lt;code&amp;gt;install foo --noreboot --nosetlabel&amp;lt;/code&amp;gt;. In which case I would see those values passed to my installer script.&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/program/install/zh&amp;diff=1607</id>
		<title>Tutorial/program/install/zh</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/program/install/zh&amp;diff=1607"/>
		<updated>2026-08-24T20:28:28Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= 教程：Install程序 =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;是一个随[[openos/zh|OpenOS]]附送的软件。对于大多数用户以及大多数电脑而言，安装OpenOS的首选方法便是使用这个&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;应用。&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;的功能还包括安装本mod所提供[[Item/loot disks/zh|战利品软盘]]中的软件、运行库以及帮助脚本。&lt;br /&gt;
&lt;br /&gt;
要了解有关&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;命令行选项的更多信息，我们推荐通过执行&amp;lt;code&amp;gt;man install&amp;lt;/code&amp;gt;以阅读其man页面，也可以在[https://raw.githubusercontent.com/MightyPirates/OpenComputers/master-MC1.7.10/src/main/resources/assets/opencomputers/loot/openos/usr/man/install 此处]（英文）阅读在线版。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;按以下步骤运行：&lt;br /&gt;
&lt;br /&gt;
* 第一步：扫描要安装的软件&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;首先它会扫描可供候选的安装来源文件系统，即可以被当作安装用软件包的文件系统，例如[[:item:loot disks:zh|战利品软盘]]。如果找到了多个候选安装来源，它将会提问用户：&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;What do you want to install?（你想安装什么？）&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;下面跟着它找到的可供安装的软盘列表。&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* 第二步：扫描硬盘&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;下一步是扫描可供候选的安装目标文件系统，即可以作为安装的目标的文件系统，如硬盘。以从奖励箱软盘中向硬盘中安装OpenOS为例，此时的硬盘即为目标文件系统。就像候选安装来源一样，如果install找到了多个候选安装目标，它也会要求用户选择其中一个：&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Where do you want to install to?（你想安装到何处？）&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;下面跟着它找到的可以安装**进**的硬盘列表。&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* 第三步：安装&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;在继续安装之前，用户会被要求确认进行安装：&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;Install OpenOS to /mnt/e03/? [Y/n]（将OpenOS安装到/mnt/e03/？[Y/n]）&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;在确认了此步骤后，程序会将战利品软盘（对于此样例）中的文件复制到目标文件系统中。要安装的软件中可能会有一个可选的.prop文件，此文件可以告知install是否要设定电脑的默认启动文件系统、将其设定为什么标签（若有），以及在安装完成后是否要重启系统。&lt;br /&gt;
软件安装软盘还可以提供完全自定义的安装过程，只需在软盘文件系统的根目录下创建一个.install文件。在确认安装来源与安装目标后，只要.install文件存在于安装来源文件系统，install程序就会唤起它。&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* 可选参数&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;推荐你查看install的man页面以获取更详细的细节以及支持参数的完整列表。但是我考虑到有一点最好在这里提一下，战利品软盘的标签可以被用于install的参数——这样可以让搜索候选软盘时仅搜索标签一致的软盘。&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;install openos&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;请注意，参数忽略大小写。在同时有多个软件安装软盘可用于安装时，以此方式指定标签可能会使得install在不询问用户的情况下减少候选项。&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== 安装额外软件 ==&lt;br /&gt;
&lt;br /&gt;
除了安装战利品软盘的软件（例如OpenOS）之外，用户还可以将&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;程序用于自制的软件安装软盘。如果你打算以可移动文件系统的形式提供软件，那么可以将&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;作为你的实用工具。在此文档中我们假设你以软盘的形式分发你的软件，尽管&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;不会区分文件系统组件的类型，不论是软盘、硬盘还是其他类型。&lt;br /&gt;
&lt;br /&gt;
要将你的软件安装软盘与&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;配合使用，最基础也是默认的方式其实是什么也不做，安装过程会凑合着进行。&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;会检查所有装有文件的文件系统，将它们全部作为安装来源的候选项。程序会要求用户选择一个以进行安装，之后&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;程序会简单地将所选软盘中的所有文件复制到所选的安装位置。这也就是OpenOS自身的安装过程。&lt;br /&gt;
&lt;br /&gt;
你可以通过在你的软件发行软盘的根目录创建&amp;lt;code&amp;gt;custom.prop&amp;lt;/code&amp;gt;和/或&amp;lt;code&amp;gt;custom.install&amp;lt;/code&amp;gt;文件来控制&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;程序的行为。&amp;lt;code&amp;gt;.prop&amp;lt;/code&amp;gt;文件应当为有效的Lua表，设定了可用于&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;的可选标记 。例如&amp;lt;code&amp;gt;openos .prop&amp;lt;/code&amp;gt;文件的内容为：&amp;lt;code&amp;gt;{label = &amp;quot;OpenOS&amp;quot;, reboot=true, setlabel=true, setboot=true}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
请注意&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;的默认复制操作将会跳过&amp;lt;code&amp;gt;.prop&amp;lt;/code&amp;gt;文件（不复制&amp;lt;code&amp;gt;.prop&amp;lt;/code&amp;gt;）。&lt;br /&gt;
&lt;br /&gt;
* 你可以设置自定义标签&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;install可以将标签作为安装选项。默认情况下，install使用文件系统的标签（若未设定标签则使用其地址）。标签有助于install的运作与用户体验的提高。用户甚至可以在系统询问安装选项之前就用命令行参数告知install要安装什么。例如，如果你输入了install openos，并且还存在其他安装选项，则install只会给出OpenOS的安装选项。这使得用户可以无需经过系统询问就获取他们想要的东西。除此之外，install在列出安装选项时使用的也是同样的标签逻辑。&lt;br /&gt;
你可以通过定义.prop文件中表的label键来覆盖install程序使用的标签值。如果你想让你的用户输入install my_cool_stuff以安装你的软件，那么你需要在你的软件安装软盘根目录下创建一个.prop文件，内容为：{label=&amp;quot;my_cool_stuff&amp;quot;}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* 忽略文件系统（在install中隐藏）&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;在你有多个文件系统与软盘时使用install可能会很烦人，因为install总是会将所有磁盘都作为待选安装来源列出，因此你可能会想忽略它们。你可以创建一个内容为{ignore=true}的.prop文件，这样下次运行install时就不会将此文件系统作为安装来源列出了。&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* 自定义安装行为&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;如果你的安装需要一套更复杂的操作，而**不只是**复制文件（以及设定启动盘、设定标签和重启这些操作），比如说你想要限定仅复制哪些文件，以及将它们复制到什么位置（默认复制到目标文件系统的根目录）。请注意用户可以使用install命令的参数控制要复制哪个子目录到什么地方，但是可能你想让你软件的安装流程对用户提供更多的辅助，那么你需要自己配置安装步骤。&lt;br /&gt;
在用户将你的软件安装盘确定为安装来源后，install将会检查其中是否存在.install文件（请注意文件名开头有一个点，和.prop一样）若文件存在，install就**不会**复制任何文件，而是会将你自定义的.install文件作为脚本运行（然后就不做其他事情了）。在你的.install脚本开始运行后，你就可以完全控制如何完成安装流程了。&lt;br /&gt;
你自定义的.install脚本的加载环境中会提供一个有用的install表，表中包含了/bin/install程序当前接收到的选项。  \\&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
例如，下面是我的自定义&amp;lt;code&amp;gt;.install&amp;lt;/code&amp;gt;脚本，包括全部内容（是的，没别的了）： &amp;lt;pre&amp;gt;&lt;br /&gt;
for k,v in pairs(install) do&lt;br /&gt;
&lt;br /&gt;
io.write(k, &amp;quot; -&amp;gt; &amp;quot;, v)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
下面将会是输出内容（我的文件系统挂载于&amp;lt;code&amp;gt;/mnt/c2b&amp;lt;/code&amp;gt;，我的&amp;lt;code&amp;gt;.prop&amp;lt;/code&amp;gt;文件中有&amp;lt;code&amp;gt;{label=&amp;quot;foo&amp;quot;}&amp;lt;/code&amp;gt;）&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from -&amp;gt; /mnt/c2b root -&amp;gt; label -&amp;gt; foo to -&amp;gt; // fromDir -&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
用户可能也会选择使用一些命令行参数，例如：&amp;lt;code&amp;gt;install foo --noreboot --nosetlabel&amp;lt;/code&amp;gt;。此时我也会看到这些值被传递给了我的安装脚本。&lt;br /&gt;
&lt;br /&gt;
== 目录 ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents/zh}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/oc3_hard_drives&amp;diff=1606</id>
		<title>Tutorial/oc3 hard drives</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/oc3_hard_drives&amp;diff=1606"/>
		<updated>2026-08-24T20:28:28Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= OC Tutorial: Hard Drives =&lt;br /&gt;
&lt;br /&gt;
When [[Tutorial/oc2 writing code|writing programs]] you&#039;ll usually want to have your programs stick around, so the &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; folder is no long-term solution. Enter [[Item/hdd|hard drives]]. When you put a hard drive into a computer it will be automatically mounted in the &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt; folder, named after the first letters of its address. While this may be enough for experimenting, you&#039;ll want more flexibility and comfort after a while. This tutorial will introduce the concept of labeling and mounting hard drives.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note&#039;&#039;: all concepts in this tutorial also apply to [[Item/floppy|floppy disks]].&lt;br /&gt;
&lt;br /&gt;
First, insert a fresh hard drive into a computer:&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/Nr81Dx2.png|700px|https://i.imgur.com/Nr81Dx2.png]]&lt;br /&gt;
&lt;br /&gt;
You&#039;ll notice in the tooltip of the hard drive that it has been assigned an address (that shortened, alpha-numeric string). You don&#039;t have to remember that, it&#039;s just there for reference. Next, open the screen&#039;s GUI and type &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt;. This program lists all present file systems and their current mount points. Among them you&#039;ll notice one that&#039;s only known by address (unlike the ROM and temporary file system). That should be the address you saw in the tooltip, earlier.&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/22yfaRh.png|700px|https://i.imgur.com/22yfaRh.png]]&lt;br /&gt;
&lt;br /&gt;
What we want to do now, is to assign a name to our hard drive, so we don&#039;t have to reference it by address all the time. Run &amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt; to get the usage information of the labeling program. We&#039;ll use the &amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt; option here, but you could also label it by providing the path to its default mount point in the &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt; folder. Run the program with the desired parameters, i.e. name the disk as you&#039;d like. Note that the &amp;lt;code&amp;gt;92a&amp;lt;/code&amp;gt; in the screen shot is the beginning of the hard drive&#039;s address. For most programs that need an address you can abbreviate the address, as long as it&#039;s still unique among the other components connected to the computer.&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve named your hard disk that name is also shown in the hard disk&#039;s tooltip. Run &amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt; again to verify the hard disk is now named:&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/X1ws656.png|700px|https://i.imgur.com/X1ws656.png]]&lt;br /&gt;
&lt;br /&gt;
We can now mount the hard drive while referring to it by name. Type &amp;lt;code&amp;gt;mount &amp;amp;lt;label&amp;amp;gt; &amp;amp;lt;path&amp;amp;gt;&amp;lt;/code&amp;gt; to mount the hard drive with the specified label at the specified path. Hard drives can be mounted in multiple locations, so doing that will not invalidate the default mount in the &amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt; folder.&lt;br /&gt;
&lt;br /&gt;
Now, computers do not store any state across reboots. Meaning if you shut down or reboot your computer, it crashes or runs out of power, you&#039;ll have to mount your disk again after restarting. This can get annoying very quickly, so let&#039;s create an autorun script that mounts the disk for us when it is inserted. Every hard drive can have such an autorun script. It has to be named &amp;lt;code&amp;gt;autorun&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;autorun.lua&amp;lt;/code&amp;gt; and must be in the root of the hard drives folder structure. This script is automatically executed when the disk is installed in a computer - unless you disable autorun via the [[API/filesystem|the file system API]].&lt;br /&gt;
&lt;br /&gt;
Switch into your freshly mounted folder, and run &amp;lt;code&amp;gt;edit autorun.lua&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/QYgrS3z.png|700px|https://i.imgur.com/QYgrS3z.png]]&lt;br /&gt;
&lt;br /&gt;
Autorun scripts get the proxy of the storage medium they&#039;re on as their first parameter, so we can use Lua&#039;s varargs syntax to access it. We&#039;ll pass that to the &amp;lt;code&amp;gt;fs.mount()&amp;lt;/code&amp;gt; function, which you find documented in [[API/filesystem|the file system API]].&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/J5inxhI.png|700px|https://i.imgur.com/J5inxhI.png]]&lt;br /&gt;
&lt;br /&gt;
Save that file and close it. After removing the hard disk from the computer and inserting it again, run &amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt; and you should see the disk has been automatically mounted again.&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/Us0kTvl.png|700px|https://i.imgur.com/Us0kTvl.png]]&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/oc3_hard_drives/zh&amp;diff=1605</id>
		<title>Tutorial/oc3 hard drives/zh</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/oc3_hard_drives/zh&amp;diff=1605"/>
		<updated>2026-08-24T20:28:28Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= OC 教程：硬盘 =&lt;br /&gt;
&lt;br /&gt;
当你[[Tutorial/oc2 writing code/zh|编写程序]]时，通常是希望程序文件一直存在的，所以使用 &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; 目录并非长久之计。这就需要用到[[Item/hard disk drive/zh|硬盘]]。将硬盘装进电脑时，它将自动挂载到&amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;目录下。并以其地址的前几个字母命名。尽管这对于体验其功能可能够用了，但是你在之后可能会想要更灵活且舒适地使用它。本教程将会介绍修改硬盘标签与挂载硬盘的概念。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;注意：&#039;&#039;&#039;本教程的所有概念同样适用于 [[Item/floppy disk/zh|软盘]]。&lt;br /&gt;
&lt;br /&gt;
首先，将一个全新的硬盘插入电脑：&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/Nr81Dx2.png|700px|https://i.imgur.com/Nr81Dx2.png]]&lt;br /&gt;
&lt;br /&gt;
你会在硬盘的提示文本中注意到，它已经被分配好了一个地址（那个被截短的、字母数字混合的字符串）。你不必记住它，因为它显示的目的仅为参考。接下来，打开屏幕的GUI并且输入&amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt;。该程序将列出当前所有的文件系统与挂载点。在它们中，你可以注意到只有一个是以地址显示的（不像 ROM 和临时文件系统）。那个地址应当是你先前在提示中看到的地址。&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/22yfaRh.png|700px|https://i.imgur.com/22yfaRh.png]]&lt;br /&gt;
&lt;br /&gt;
现在我们要做的是为硬盘指定一个名称，这样我们不必一直用地址来引用它。运行&amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt;命令即可获取关于如何使用标签程序的信息。我们要使用&amp;lt;code&amp;gt;-a&amp;lt;/code&amp;gt;参数，但你也可以通过提供指向其在&amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;目录下默认挂载点的路径来给它设定标签。 使用所需参数运行程序，参数也就是你想给硬盘取的名字。请注意屏幕截图中的&amp;lt;code&amp;gt;92a&amp;lt;/code&amp;gt;是硬盘地址的开头。对于大部分需要地址的程序，你都可以使用简写地址，只要简写地址在连接到电脑的组件中仍然唯一。&lt;br /&gt;
&lt;br /&gt;
当你命名好你的硬盘后，这个名字也会显示在硬盘的提示文本中。再次运行&amp;lt;code&amp;gt;df&amp;lt;/code&amp;gt;来确认硬盘已被命名：&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/X1ws656.png|700px|https://i.imgur.com/X1ws656.png]]&lt;br /&gt;
&lt;br /&gt;
我们现在可以通过使用硬盘名称的方法挂载它了。输入&amp;lt;code&amp;gt;mount &amp;amp;lt;标签名&amp;amp;gt; &amp;amp;lt;路径&amp;amp;gt;&amp;lt;/code&amp;gt;来将指定标签名对应的硬盘挂载到指定路径。硬盘可以被同时挂载到多个位置，因此执行此操作并不会使&amp;lt;code&amp;gt;/mnt&amp;lt;/code&amp;gt;文件夹下的默认挂载点失效。&lt;br /&gt;
&lt;br /&gt;
当前状态下，电脑不会在重启后保存任何状态。这意味着如果你关闭或重启你的电脑、电脑崩溃或断电后，你就必须重新挂载硬盘。这样的话很快就会让人厌烦，所以创建一个autorun（自动运行）脚本吧，让硬盘在插入时自动挂载。每块硬盘都可以有自己的autorun脚本，脚本必须被命名为&amp;lt;code&amp;gt;autorun&amp;lt;/code&amp;gt;或&amp;lt;code&amp;gt;autorun.lua&amp;lt;/code&amp;gt;，且必须放在硬盘文件夹结构的根目录下。这个脚本会在硬盘连接到电脑时自动执行——除非你通过[[API/filesystem/zh|filesystem(文件系统) API]]禁用自动运行功能。&lt;br /&gt;
&lt;br /&gt;
切换到你新挂载的文件夹中，执行&amp;lt;code&amp;gt;edit autorun.lua&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/QYgrS3z.png|700px|https://i.imgur.com/QYgrS3z.png]]&lt;br /&gt;
&lt;br /&gt;
autorun脚本接收其所在存储介质的代理对象作为其的第一个参数，所以我们可以使用Lua的变长参数语法获取它。我们会将它传递到&amp;lt;code&amp;gt;fs.mount()&amp;lt;/code&amp;gt;函数，你可以在[[API/filesystem/zh|filesystem(文件系统) API]]中找到它的文档。&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/J5inxhI.png|700px|https://i.imgur.com/J5inxhI.png]]&lt;br /&gt;
&lt;br /&gt;
保存并关闭文件。在从电脑移除并重新插入硬盘后，运行&amp;lt;code&amp;gt;ls&amp;lt;/code&amp;gt;，你会发现硬盘已经被自动挂载了。&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/Us0kTvl.png|700px|https://i.imgur.com/Us0kTvl.png]]&lt;br /&gt;
&lt;br /&gt;
== 目录 ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents/zh}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/oc2_writing_code&amp;diff=1604</id>
		<title>Tutorial/oc2 writing code</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/oc2_writing_code&amp;diff=1604"/>
		<updated>2026-08-24T20:28:28Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= OC Tutorial: Writing Code =&lt;br /&gt;
&lt;br /&gt;
So, you&#039;ve [[Tutorial/oc1 basic computer|built your first computer]] and wonder what to do with it?&lt;br /&gt;
&lt;br /&gt;
Well, let&#039;s start with the good old, proven, beloved first program. You know which one. First off, let&#039;s switch to the &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; folder, which is a small, writable file system each computer comes with. Remember, this gets &#039;&#039;wiped on reboot&#039;&#039;, so don&#039;t put anything valuable in here!&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/7YdcUbE.png|700px|https://i.imgur.com/7YdcUbE.png]]&lt;br /&gt;
&lt;br /&gt;
As shown in the image, type &amp;lt;code&amp;gt;edit &amp;amp;lt;filename&amp;amp;gt;&amp;lt;/code&amp;gt;, where &amp;lt;code&amp;gt;filename&amp;lt;/code&amp;gt; is the name of the file you wish to edit. The &amp;lt;code&amp;gt;.lua&amp;lt;/code&amp;gt; extension is absolutely optional, I just like to keep it for syntax highlighting when opening the files in an external editor.&lt;br /&gt;
&lt;br /&gt;
Once in the editor, start coding. In our case it&#039;s just the boring, uncreative-but-proven &amp;lt;code&amp;gt;print(&amp;quot;Hello World!&amp;quot;)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/5F16EEV.png|700px|https://i.imgur.com/5F16EEV.png]]&lt;br /&gt;
&lt;br /&gt;
When you&#039;re done, press Ctrl+S to save, then press Ctrl+W to close the editor. If you list the contents of the &amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt; folder now, you&#039;ll see the file has been created. Type its name and press enter to run it:&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/KiHVUMu.png|700px|https://i.imgur.com/KiHVUMu.png]]&lt;br /&gt;
&lt;br /&gt;
Notice that I didn&#039;t type the &amp;lt;code&amp;gt;.lua&amp;lt;/code&amp;gt; extension when running it. The shell will look for files with that extension if it can&#039;t find the file with the exact name you specified.&lt;br /&gt;
&lt;br /&gt;
If want your programs to survive reboots, &#039;&#039;&#039;continue by reading on [[Tutorial/oc3 hard drives|how to use hard drives]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/oc2_writing_code/zh&amp;diff=1603</id>
		<title>Tutorial/oc2 writing code/zh</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/oc2_writing_code/zh&amp;diff=1603"/>
		<updated>2026-08-24T20:28:27Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= OC 教程：编写代码 =&lt;br /&gt;
&lt;br /&gt;
所以，你已经[[Tutorial/oc1 basic computer/zh|搭建了你的第一台电脑]]，现在想知道可以用它做什么对吗？&lt;br /&gt;
&lt;br /&gt;
让我们从那个历史悠久，久经考验且为世人爱戴的程序开始。你知道是哪一个。首先，让我们切换到&amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;目录，它是每个操作系统都自带的一个小的、可写入的文件系统。请记住，这个目录&#039;&#039;&#039;重启后会清空&#039;&#039;&#039;，所以不要在里面放重要的内容！&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/7YdcUbE.png|700px|https://i.imgur.com/7YdcUbE.png]]&lt;br /&gt;
&lt;br /&gt;
如图所示，输入&amp;lt;code&amp;gt;edit &amp;amp;lt;文件名&amp;amp;gt;&amp;lt;/code&amp;gt;，其中的&amp;lt;code&amp;gt;&amp;amp;lt;文件名&amp;amp;gt;&amp;lt;/code&amp;gt;指你想要编辑的文件名称。&amp;lt;code&amp;gt;.lua&amp;lt;/code&amp;gt;后缀是可选的，我愿意保留这个后缀，因为这样用外部编辑器打开代码时会有代码高亮。&lt;br /&gt;
&lt;br /&gt;
打开编辑器后就可以开始编写代码了。我们要输入的代码是无聊、没有创新但久经沙场的 &amp;lt;code&amp;gt;print(&amp;quot;Hello World!&amp;quot;)&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/5F16EEV.png|700px|https://i.imgur.com/5F16EEV.png]]&lt;br /&gt;
&lt;br /&gt;
当你输入完成时，按下Ctrl+S保存，然后按下Ctrl+W退出编辑器。现在如果你列出&amp;lt;code&amp;gt;/tmp&amp;lt;/code&amp;gt;文件夹的内容，会发现新的文件已经被创建，输入它的文件名然后按下回车键来运行它：&lt;br /&gt;
&lt;br /&gt;
[[File:https://i.imgur.com/KiHVUMu.png|700px|https://i.imgur.com/KiHVUMu.png]]&lt;br /&gt;
&lt;br /&gt;
请注意，我没有输入后缀 &amp;lt;code&amp;gt;.lua&amp;lt;/code&amp;gt; 就能运行它。当 shell 无法找到名称与你输入的文件名完全相符的文件时，它将会自动搜索是否有包含后缀且名称相同的文件。&lt;br /&gt;
&lt;br /&gt;
如果你想让文件在重启后仍然保留，&#039;&#039;&#039;那么请继续阅读[[Tutorial/oc3 hard drives/zh|如何使用硬盘]]&#039;&#039;&#039;。&lt;br /&gt;
&lt;br /&gt;
== 目录 ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents/zh}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/oc1_basic_computer&amp;diff=1602</id>
		<title>Tutorial/oc1 basic computer</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/oc1_basic_computer&amp;diff=1602"/>
		<updated>2026-08-24T20:28:27Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= OC Tutorial: Basic Computer =&lt;br /&gt;
&lt;br /&gt;
In this tutorial you will learn how to build a basic (Tier 1) computer. This tutorial is using the default config file.&lt;br /&gt;
&lt;br /&gt;
You will need:&lt;br /&gt;
&lt;br /&gt;
= [[Block/case|Tier 1 Case]] =&lt;br /&gt;
= [[Block/screen|Tier 1 Screen]] =&lt;br /&gt;
= [[Block/keyboard|Keyboard]] =&lt;br /&gt;
= [[Item/graphics card|Tier 1 Graphics Card]] =&lt;br /&gt;
= [[Item/CPU|Tier 1 CPU]] =&lt;br /&gt;
= [[Item/memory|Two Tier 1 RAM or 1 Tier 1.5 RAM]] =&lt;br /&gt;
= [[Block/disk drive|Disk Drive]] =&lt;br /&gt;
= [[Item/openos floppy|OpenOS Floppy Disk]] =&lt;br /&gt;
= [[Item/hard disk drive|Hard Disk Drive]] =&lt;br /&gt;
= [[Item/EEPROM|Lua BIOS]] =&lt;br /&gt;
= Source of Power =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Versions prior to 1.2.2 require a [[Block/power converter|Power Converter]]&lt;br /&gt;
&lt;br /&gt;
= Setting up your environment =&lt;br /&gt;
&lt;br /&gt;
The basic computer setup consists of a [[Block/case|Computer Case]] with a [[Block/screen|Screen]] on top and a [[Block/keyboard|Keyboard]] attached.&lt;br /&gt;
&lt;br /&gt;
Here is an example with the power source on the left and a basic setup on the right [[File:http://i.imgur.com/TDIiBJr.png|http://i.imgur.com/TDIiBJr.png]]&lt;br /&gt;
&lt;br /&gt;
Now, you will need to right click on the computer case and put the items in here as shown [[File:http://i.imgur.com/OrMN600.png|http://i.imgur.com/OrMN600.png]]&lt;br /&gt;
&lt;br /&gt;
Now, if you try to start the computer by clicking on the power button, you will end up getting an error:&lt;br /&gt;
&lt;br /&gt;
[[File:http://i.imgur.com/jiGZKSR.png|http://i.imgur.com/jiGZKSR.png]] This means that there is no OS for the computer to run! To fix that, we will need to put a [[Block/disk drive|Disk Drive]] directly next to the computer and place the [[Item/openos floppy|OpenOS Floppy Disk]] in it.&lt;br /&gt;
&lt;br /&gt;
Now, it should turn on. [[File:http://i.imgur.com/SeEd9Xb.png|http://i.imgur.com/SeEd9Xb.png]]&lt;br /&gt;
&lt;br /&gt;
Congratulations! You now have a fully working computer, but if you want more features, you will need to install OpenOS!&lt;br /&gt;
&lt;br /&gt;
To install OpenOS, you will need to run the &amp;quot;install&amp;quot; command on the computer, which will allow you run the computer with out the OS Floppy.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Next up: [[Tutorial/oc2 writing code|Writing Programs]].&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/oc1_basic_computer/zh&amp;diff=1601</id>
		<title>Tutorial/oc1 basic computer/zh</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/oc1_basic_computer/zh&amp;diff=1601"/>
		<updated>2026-08-24T20:28:27Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= OC 教程：基础计算机 =&lt;br /&gt;
&lt;br /&gt;
在这个教程中你将学到如何构建基础的（1级）计算机。本教程使用默认配置文件。&lt;br /&gt;
&lt;br /&gt;
你需要：&lt;br /&gt;
&lt;br /&gt;
= [[Block/computer case/zh|1级机箱]] =&lt;br /&gt;
= [[Block/screen/zh|1级屏幕]] =&lt;br /&gt;
= [[Block/keyboard/zh|键盘]] =&lt;br /&gt;
= [[Item/graphics card/zh|1级显卡]] =&lt;br /&gt;
= [[Item/CPU/zh|1级CPU]] =&lt;br /&gt;
= [[Item/memory/zh|两条1级内存条或一条1.5级内存条]] =&lt;br /&gt;
= [[Block/disk drive/zh|硬盘]] =&lt;br /&gt;
= [[Item/openos floppy/zh|OpenOS软盘]] =&lt;br /&gt;
= [[Item/hard disk drive/zh|硬盘]] =&lt;br /&gt;
= [[Item/EEPROM/zh|Lua BIOS]] =&lt;br /&gt;
= 电源 =&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;注意：&#039;&#039;&#039;在1.2.2版本前你还需要[[Block/power converter/zh|能量转换器]]。&lt;br /&gt;
&lt;br /&gt;
= 搭建运行环境 =&lt;br /&gt;
&lt;br /&gt;
最基础的电脑系统构成为：一个[[Block/computer case/zh|机箱]]，上方有一个[[Block/screen/zh|屏幕]]，且连接了[[Block/keyboard/zh|键盘]]。&lt;br /&gt;
&lt;br /&gt;
此样例中左侧为电源，右侧为电脑系统&lt;br /&gt;
&lt;br /&gt;
[[File:http://i.imgur.com/TDIiBJr.png|http://i.imgur.com/TDIiBJr.png]]&lt;br /&gt;
&lt;br /&gt;
现在，你需要右键单击机箱并将配件如下图所示放进去：&lt;br /&gt;
&lt;br /&gt;
[[File:http://i.imgur.com/OrMN600.png|http://i.imgur.com/OrMN600.png]]&lt;br /&gt;
&lt;br /&gt;
如果现在你尝试按下电源键启动电脑，会收到如下错误：&lt;br /&gt;
&lt;br /&gt;
[[File:http://i.imgur.com/jiGZKSR.png|http://i.imgur.com/jiGZKSR.png]]&lt;br /&gt;
&lt;br /&gt;
这说明当前电脑没有可运行的操作系统！修复的方法是在机箱旁边紧挨着放置一个[[Block/disk drive/zh|软盘驱动器]]，并且插入一张[[Item/openos floppy/zh|OpenOS 软盘]]。&lt;br /&gt;
&lt;br /&gt;
现在，它应该能正常启动了。&lt;br /&gt;
&lt;br /&gt;
[[File:http://i.imgur.com/SeEd9Xb.png|http://i.imgur.com/SeEd9Xb.png]]&lt;br /&gt;
&lt;br /&gt;
恭喜！您现在有了一台可以正常工作的计算机了。但是如果需要更多功能，还应安装OpenOS。&lt;br /&gt;
&lt;br /&gt;
要安装OpenOS，你应该在计算机里输入&amp;lt;code&amp;gt;install&amp;lt;/code&amp;gt;命令，当安装完成后你的电脑就不需要软盘也能运行了。&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;下一篇：[[Tutorial/oc2 writing code/zh|编写代码]]。&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== 目录 ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents/zh}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/modding_imc&amp;diff=1598</id>
		<title>Tutorial/modding imc</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/modding_imc&amp;diff=1598"/>
		<updated>2026-08-24T20:28:25Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Templates ==&lt;br /&gt;
&lt;br /&gt;
Templates allow defining recipes for items that can be assembled using the Assembler block. A template defines which slots get displayed in the Assembler GUI based on the item put in its first slot (where e.g. the computer cases go for building robots). The basic layout remains identical, i.e. the assembler is intended as a pure computer/electronical device assembler, with the three sections &amp;quot;containers&amp;quot;, &amp;quot;upgrades&amp;quot; and &amp;quot;computer parts&amp;quot; (cards, CPU, RAM, HDDs, etc).&lt;br /&gt;
&lt;br /&gt;
== Registration ==&lt;br /&gt;
&lt;br /&gt;
The IMC message &amp;lt;code&amp;gt;registerAssemblerTemplate&amp;lt;/code&amp;gt; takes an NBTTagCompound of the following format: &amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
name : String // Optional, name of the template, used in logging only.&lt;br /&gt;
select : String // Name of a static method that&#039;s queried to determine whether the template applies to an item stack.&lt;br /&gt;
validate : String // Name of static method to call for checking if assembly may be started. Returns additional lines for the assemble button and text for the status bar.&lt;br /&gt;
assemble : String // Name of a static method that is called to create the resulting ItemStack and energy to consume.&lt;br /&gt;
hostClass : String // Name of a class or interface that is the environment or implemented by the environment that will represent the assembled device. Passed to HostAware drivers.&lt;br /&gt;
componentSlots : NBTTagList {&lt;br /&gt;
  NBTTagCompound {&lt;br /&gt;
    type : String // The component type, as a string (see below). None if not specified. Determines which slot background is shown and used for item validation (unless validate is specified).&lt;br /&gt;
    tier : Int // The max supported tier of the slot. Any if not specified.&lt;br /&gt;
    validate : String // Name of a static method to call for custom validation in isItemValidForSlot, for further custom checks, if necessary. If not specified, normal type and tier checks apply.&lt;br /&gt;
  } // Up to 9 of these. Use empty tag compounds to skip slots.&lt;br /&gt;
}&lt;br /&gt;
upgradeSlots : NBTTagList {&lt;br /&gt;
  NBTTagCompound {&lt;br /&gt;
    tier : Int // Same as above.&lt;br /&gt;
    validate : String // Same as above.&lt;br /&gt;
  } // Up to 9 of these. Use empty tag compounds to skip slots.&lt;br /&gt;
}&lt;br /&gt;
containerSlots : NBTTagList {&lt;br /&gt;
  NBTTagCompound {&lt;br /&gt;
    tier : Int // Same as above.&lt;br /&gt;
    validate : String // Same as above.&lt;br /&gt;
  } // Up to 3 of these. Use empty tag compounds to skip slots.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Signatures for callbacks:&lt;br /&gt;
&lt;br /&gt;
* Template selector: &amp;lt;code&amp;gt;boolean select(ItemStack stack)&amp;lt;/code&amp;gt; - Build validator: &amp;lt;code&amp;gt;Object[] validate(IInventory inventory)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Values in the returned array: a boolean indicating whether the current configuration is valid for assembly, an IChatComponent to display in the progress bar and an IChatComponent array of lines to display in the assemble button tooltip. All are optional, defaulting to false, null and empty, respectively.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Assembly callback: &amp;lt;code&amp;gt;Object[] assemble(IInventory inventory)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Values in the returned array: the ItemStack to produce and a Double value, indicating the energy to consume (and thus the time it takes to build the object).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Slot validator: &amp;lt;code&amp;gt;boolean validate(IInventory inventory, int slot, int tier, ItemStack stack)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Where inventory is the inventory of the assembler, slot is the slot index the item stack stack should be inserted into, and tier is the tier specified in template&#039;s NBT for the slot.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Inventory Layout ==&lt;br /&gt;
&lt;br /&gt;
The inventory passed to the callbacks is the inventory of the assembler. It is arranged as follows:&lt;br /&gt;
&lt;br /&gt;
* 0: The &amp;quot;base item&amp;quot; that was used to select the template, e.g. a computer case. - 1-3: Containers. - 4-12: Upgrades. - 13-21: Components.&lt;br /&gt;
&lt;br /&gt;
== Slot Types and Tiers ==&lt;br /&gt;
&lt;br /&gt;
Slot types are passed as string constants (which is what the overall API will transition to in 1.4, away from the Slot enum, by the way). These are the applicable built-in slot types for components accepted by the template:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;none&amp;lt;/code&amp;gt;: No slot / slot will be unavailable. - &amp;lt;code&amp;gt;card&amp;lt;/code&amp;gt;: Card slot. - &amp;lt;code&amp;gt;component_bus&amp;lt;/code&amp;gt;: Component Bus slot. - &amp;lt;code&amp;gt;container&amp;lt;/code&amp;gt;: Container slot. - &amp;lt;code&amp;gt;cpu&amp;lt;/code&amp;gt;: CPU slot. - &amp;lt;code&amp;gt;floppy&amp;lt;/code&amp;gt;: Floppy slot. - &amp;lt;code&amp;gt;hdd&amp;lt;/code&amp;gt;: HDD slot. - &amp;lt;code&amp;gt;memory&amp;lt;/code&amp;gt;: Memory slot. - &amp;lt;code&amp;gt;upgrade&amp;lt;/code&amp;gt;: Upgrade slot.&lt;br /&gt;
&lt;br /&gt;
Tiers are simply numeric, starting with &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; as tier 1 and ending with &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; as tier 3. &amp;lt;code&amp;gt;Int.MaxInt&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;Integer.MAX_VALUE&amp;lt;/code&amp;gt; indicate &amp;quot;any tier&amp;quot;, meaning no tier indicator will be shown in the GUI slot.&lt;br /&gt;
&lt;br /&gt;
== Disassembly ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;registerDisassemblerTemplate&amp;lt;/code&amp;gt; takes an NBT tag with the following format: &amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
name : String // This is optional, used only in logging.&lt;br /&gt;
select : String // Name of static method to call for checking if a stack can be disassembled.&lt;br /&gt;
disassemble : String // Name of static method to call to to get results for a disassembly operation.&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Signatures for callbacks:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;boolean select(ItemStack stack)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Gets the stack to check for validity, returns true if the stack can be disassembled.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ItemStack[] disassemble(ItemStack stack, ItemStack[] ingredients)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Compute the items to output from the disassembler for the specified item stack. The passed list of ingredients is for convenience, and is what OC thinks are the inputs used to craft the passed item stack. The general idea is that you can return that list &#039;&#039;plus&#039;&#039; additional items, such as stuff that was &amp;quot;in&amp;quot; the passed stack, if the stack has some kind of inventory (e.g. for servers the installed components are also returned).&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
It will be called whenever &#039;&#039;any&#039;&#039; item is disassembled, and should only return a list of (additional) ingredients where appropriate. It will also be called to check if an item can be disassembled at all.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
In your &amp;lt;code&amp;gt;FMLInitializationEvent&amp;lt;/code&amp;gt; handler: &amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
NBTCompoundTag nbt = new NBTCompoundTag(); nbt.setString(&amp;quot;select&amp;quot;, &amp;quot;com.example.mod.Callbacks.ocAssemblerSelect&amp;quot;); nbt.setString(&amp;quot;validate&amp;quot;, &amp;quot;com.example.mod.Callbacks.ocAssemblerValidate&amp;quot;); nbt.setString(&amp;quot;assemble&amp;quot;, &amp;quot;com.example.mod.Callbacks.ocAssemblerAssemble&amp;quot;); // ... FMLInterModComms.sendMessage(&amp;quot;OpenComputers&amp;quot;, &amp;quot;registerDisassemblerTemplate&amp;quot;, nbt);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Be careful to use a separate NBT compound for each registered template, since they are evaluated when the IMC message is received by OC, which happens asynchronously at a later point in time.&lt;br /&gt;
&lt;br /&gt;
For a &amp;quot;reference&amp;quot; implementation, see how the [https://github.com/MightyPirates/OpenComputers/blob/master/src/main/scala/li/cil/oc/common/template/RobotTemplate.scala robot templates are implemented].&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/modding_architecture&amp;diff=1597</id>
		<title>Tutorial/modding architecture</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/modding_architecture&amp;diff=1597"/>
		<updated>2026-08-24T20:28:23Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Modding: Custom Architectures =&lt;br /&gt;
&lt;br /&gt;
In this article I&#039;ll explain how architectures for OpenComputers work, and how to go about adding a custom architecture. A word of warning: implementing an architecture is &#039;&#039;not trivial&#039;&#039;. Don&#039;t expect this to be a two-step-way-to-satisfaction kind of thing.&lt;br /&gt;
&lt;br /&gt;
== What&#039;s an architecture? ==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s first clarify what I mean when I talk about an &amp;quot;architecture&amp;quot; in the context of OpenComputers. An architecture is the glue code between some kind of code-executor (henceforth: VM) and OC&#039;s components / Minecraft. OC strongly separates component logic from computer logic. In other words, a component - like a graphics card - does not know what kind of architecture it is used by. Vice-versa, an architecture doesn&#039;t know anything about how components work. All of this message passing is handled by a &#039;&#039;Machine&#039;&#039;. The machine is what&#039;s driving the architecture and provides callbacks for the architecture to communicate with the outside.&lt;br /&gt;
&lt;br /&gt;
== Using an architecture ==&lt;br /&gt;
&lt;br /&gt;
To make the whole thing a little less abstract, here&#039;s how an architecture is actually &#039;&#039;used&#039;&#039;. This will hopefully provide a better understanding of where the architecture implementation fits into the OC ecosystem. To use an architecture, a machine using that architecture must be created.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039; &#039;&#039;Note*: OC 1.4 allows addons to register architectures that can be provided by the standard CPUs, so you will not have to implement an actual computer block / item yourself. The architecture a CPU provides can be changed by sneak-activating the CPU while holding it in your hand.&lt;br /&gt;
&lt;br /&gt;
The machine encapsulates the architecture and takes care of thread scheduling, signal queuing, network connecticity and so on. All you have to do for the created machine is call &amp;lt;code&amp;gt;update()&amp;lt;/code&amp;gt; each tick (and maybe forward some calls such as &amp;lt;code&amp;gt;start()&amp;lt;/code&amp;gt; to it).&lt;br /&gt;
&lt;br /&gt;
== Lifecycle ==&lt;br /&gt;
&lt;br /&gt;
The lifecycle of an architecture is roughly: instantiation, &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt;, repeatedly &amp;lt;code&amp;gt;runThreaded()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;runSynchronized()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt; [, goto &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt;].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039; The architecture is created when its machine is created. - Whenever a machine (re)starts, it is asked to &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt; itself. This usually means setting up the underlying VM. - The standard way of driving an architecture is via &amp;lt;code&amp;gt;runThreaded()&amp;lt;/code&amp;gt;. It is called from an executor thread. The architecture&#039;s state should progressed in here. The returned result type is used by the machine for scheduling. &#039;&#039;Important&#039;&#039;: this method must never throw. It is your responsibility to handle all errors and convert them to an error result, if necessary. - A special execution result is the request to perform a &#039;synchronized call&#039;. This indicates to the machine that the next time it should drive the architecture using &amp;lt;code&amp;gt;runSynchronized()&amp;lt;/code&amp;gt;. This call will &#039;&#039;always be made from the MC server thread*, meaning it is safe to interact with the world in this callback. - When a machine is stopped, &amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt; is called, where any open resources may then be disposed.&lt;br /&gt;
&lt;br /&gt;
== Providing context ==&lt;br /&gt;
&lt;br /&gt;
In the most simple case, you just call some &#039;run&#039; method in your VM each time &amp;lt;code&amp;gt;runThreaded()&amp;lt;/code&amp;gt; is called. This is pretty useless, though, since you&#039;ll want to communicate with the outside somehow. For this, communication with components has to be provided to the VM. It is your responsibility to define the APIs inside the VM that are used to communicate with the machine&#039;s API (e.g. &amp;lt;code&amp;gt;components()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;invoke()&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
== Synchronized calls and call limits ==&lt;br /&gt;
&lt;br /&gt;
Callbacks on components may declare to be &amp;quot;direct&amp;quot;, and in addition to that they may declare a &amp;quot;call limit&amp;quot;. Per default, all component callbacks are &#039;&#039;synchronized&#039;&#039; calls, i.e. the architecture must ensure that they are only called from runSynchronized(). The call limit for direct calls limits how often per tick any single method may be called from any single machine. The limit itself is enforced in the machine&#039;s invoke method, which will throw a &amp;lt;code&amp;gt;LimitReachedException&amp;lt;/code&amp;gt; once there were too many call. At that point the architecture should either perform a short sleep, or fall back to perform a synchronized call instead.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039; &#039;&#039;Note*: while writing this I realized I forgot to pull some methods required to perform checks on this into the API. This will be remedied in the next API update, making methods for this available in the Component interface. For now, either reflect into it or build against the OC sources. Sorry about that.&lt;br /&gt;
&lt;br /&gt;
== Example code ==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say you have some VM that provides the following interfaces:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
/** The VM itself. This is just an example, it&#039;s not a &amp;quot;real&amp;quot; interface. */ public interface PseudoVM {&lt;br /&gt;
&lt;br /&gt;
Object[] run(Object[] args) throws Exception;&lt;br /&gt;
&lt;br /&gt;
void setApiFunction(String name, PseudoNativeFunction value);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** Interface defining callbacks provided by the host. */ public interface PseudoNativeFunction {&lt;br /&gt;
&lt;br /&gt;
Object invoke(Object[] args);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A very primitive architecture implementation might look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
/** This is the class you implement; Architecture is from the OC API. */ @Architecture.Name(&amp;quot;Pseudolang&amp;quot;) public class PseudoArchitecture implements Architecture {&lt;br /&gt;
&lt;br /&gt;
private final Machine machine;&lt;br /&gt;
&lt;br /&gt;
private PseudoVM vm;&lt;br /&gt;
&lt;br /&gt;
/** The constructor must have exactly this signature. */&lt;br /&gt;
public PseudoArchitecture(Machine machine) {&lt;br /&gt;
  this.machine = machine;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public boolean isInitialized() { return true; }&lt;br /&gt;
&lt;br /&gt;
public void recomputeMemory() {}&lt;br /&gt;
&lt;br /&gt;
public boolean initialize() {&lt;br /&gt;
  // Set up new VM here, and register all API callbacks you want to&lt;br /&gt;
  // provide to it.&lt;br /&gt;
  vm = new PseudoVM();&lt;br /&gt;
  vm.setApiFunction(&amp;quot;invoke&amp;quot;, new PseudoNativeFunction() {&lt;br /&gt;
    public Object invoke(Object[] args) {&lt;br /&gt;
      final String address = (String)args[0];&lt;br /&gt;
      final String method = (String)args[1];&lt;br /&gt;
      final Object[] params = (Object[])args[2];&lt;br /&gt;
      try {&lt;br /&gt;
        return new Object[]{true, machine.invoke(address, method, params)};&lt;br /&gt;
      }&lt;br /&gt;
      catch (e LimitReachedException) {&lt;br /&gt;
        // Perform logic also used to sleep / perform synchronized calls.&lt;br /&gt;
        // In this example we&#039;ll follow a protocol where if this returns&lt;br /&gt;
        // (true, something) the call succeeded, if it returns (false)&lt;br /&gt;
        // the limit was reached.&lt;br /&gt;
        // The script running in the VM is then supposed to return control&lt;br /&gt;
        // to the caller initiating the current execution (e.g. by yielding&lt;br /&gt;
        // if supported, or just returning, when in an event driven system).&lt;br /&gt;
        return new Object[]{false};&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
  vm.setApiFunction(&amp;quot;isDirect&amp;quot;, new PseudoNativeFunction() {&lt;br /&gt;
    public Object invoke(Object[] args) {&lt;br /&gt;
      final String address = (String)args[0];&lt;br /&gt;
      final String method = (String)args[1];&lt;br /&gt;
      final Node node = machine.node().network().node(address);&lt;br /&gt;
      if (node instanceof Component) {&lt;br /&gt;
        final Component component = (Component) node;&lt;br /&gt;
        if (component.canBeSeenFrom(machine.node())) {&lt;br /&gt;
          final Callback callback = machine.methods(node.host()).get(method);&lt;br /&gt;
          if (callback != null) {&lt;br /&gt;
            return callback.direct();&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
  // ... more callbacks.&lt;br /&gt;
  return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void close() {&lt;br /&gt;
  vm = null;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ExecutionResult runThreaded(boolean isSynchronizedReturn) {&lt;br /&gt;
  // Perform stepping in here. Usually you&#039;ll want to resume the VM&lt;br /&gt;
  // by passing it the next signal from the queue, but you may decide&lt;br /&gt;
  // to allow your VM to poll for signals manually.&lt;br /&gt;
  try {&lt;br /&gt;
    final Signal signal;&lt;br /&gt;
    if (isSynchronizedReturn) {&lt;br /&gt;
      // Don&#039;t pull signals when we&#039;re coming back from a sync call,&lt;br /&gt;
      // since we&#039;re in the middle of something else!&lt;br /&gt;
      signal = null;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      signal = machine.popSignal();&lt;br /&gt;
    }&lt;br /&gt;
    final Object[] result;&lt;br /&gt;
    if (signal != null) {&lt;br /&gt;
      result = vm.run(new Object[]{signal.name(), signal.args()});&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      result = vm.run(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // You&#039;ll want to define some internal protocol by which to decide&lt;br /&gt;
    // when to perform a synchronized call. Let&#039;s say we expect the VM&lt;br /&gt;
    // to return either a number for a sleep, a boolean to indicate&lt;br /&gt;
    // shutdown/reboot and anything else a pending synchronous call.&lt;br /&gt;
    if (result != null) {&lt;br /&gt;
      if (result[0] instanceof Boolean) {&lt;br /&gt;
        return new ExecutionResult.Shutdown((Boolean)result[0]);&lt;br /&gt;
      }&lt;br /&gt;
      if (result[0] instanceof Integer) {&lt;br /&gt;
        return new ExecutionResult.Sleep((Integer)result[0]);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    // If this is returned, the next &#039;resume&#039; will be runSynchronized.&lt;br /&gt;
    // The next call to runThreaded after that call will have the&lt;br /&gt;
    // isSynchronizedReturn argument set to true.&lt;br /&gt;
    return new ExecutionResult.SynchronizedCall();&lt;br /&gt;
  }&lt;br /&gt;
  catch (Throwable t) {&lt;br /&gt;
    return new ExecutionResult.Error(t.toString);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void runSynchronized() {&lt;br /&gt;
  // Synchronized calls are run from the MC server thread, making it&lt;br /&gt;
  // easier for callbacks to interact with the world (because sync is&lt;br /&gt;
  // taken care for them by the machine / architecture).&lt;br /&gt;
  // This means that if some code in the VM starts a sync call it has&lt;br /&gt;
  // to &#039;&#039;pause&#039;&#039; and relinquish control to the host, where we then&lt;br /&gt;
  // switch to sync call mode (see runThreaded), wait for the MC server&lt;br /&gt;
  // thread, and then do the actual call. It&#039;d be possible to pass the&lt;br /&gt;
  // info required for the call out in runThreaded, keep it around in&lt;br /&gt;
  // the arch and do the call directly here. For this example, let&#039;s&lt;br /&gt;
  // assume the state info is kept inside the VM, and the next resume&lt;br /&gt;
  // makes it perform the &#039;&#039;actual&#039;&#039; call. For some pseudo-code handling&lt;br /&gt;
  // this in the VM, see below.&lt;br /&gt;
  vm.run(null);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onConnect() {}&lt;br /&gt;
&lt;br /&gt;
// Use this to load the VM state, if it can be persisted.&lt;br /&gt;
void load(NBTTagCompound nbt) {}&lt;br /&gt;
&lt;br /&gt;
// Use this to save the VM state, if it can be persisted.&lt;br /&gt;
void save(NBTTagCompound nbt) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Some pseudo-code for handling synchronized calls in the VM:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;scala&amp;quot;&amp;gt;&lt;br /&gt;
private def invokeSynchronous(address, method, ...) {&lt;br /&gt;
&lt;br /&gt;
yield; // This is where it returns to runThreaded().&lt;br /&gt;
 // This is where we enter in runSynchronized();&lt;br /&gt;
val result = native.invoke(address, method, ...);&lt;br /&gt;
// See definition of invoke in initialize() for values of result.&lt;br /&gt;
yield; // And return to runSynchronized();&lt;br /&gt;
// And the next runThreaded() enters again.&lt;br /&gt;
return result[1];&lt;br /&gt;
&lt;br /&gt;
} private def invokeDirect(address, method, ...) {&lt;br /&gt;
&lt;br /&gt;
val result = native.invoke(address, method, ...);&lt;br /&gt;
// See definition of invoke in initialize() for values of result.&lt;br /&gt;
if (result[0] == true) {&lt;br /&gt;
  return result[1];&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
  return invokeSynchronous(address, method, ...);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
} def invoke(address, method, ...) {&lt;br /&gt;
&lt;br /&gt;
if (isDirect(address, method)) {&lt;br /&gt;
  return invokeDirect(address, method, ...);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
  return invokeSynchronous(address, method, ...);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Registering an architecture ==&lt;br /&gt;
&lt;br /&gt;
This one&#039;s pretty simple: just do &amp;lt;code&amp;gt;li.cil.oc.api.Machine.add(PseudoArchitecture.class)&amp;lt;/code&amp;gt;. This will allow CPUs to provide the registered architecture. If you do not wish OC CPUs to provide your architecture, you can either add your own &amp;lt;code&amp;gt;Processor&amp;lt;/code&amp;gt; driver that can be used to get access to the architecture, or go one step further and add your own computer block, that will exclusively run your architecture, for example.&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/modding_architecture/zh&amp;diff=1596</id>
		<title>Tutorial/modding architecture/zh</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/modding_architecture/zh&amp;diff=1596"/>
		<updated>2026-08-24T20:28:22Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= 模组开发：自定义架构 =&lt;br /&gt;
&lt;br /&gt;
在此文章中我会讲解OC模组的架构是如何工作的，以及如何添加自定义架构。一句警告：架构的实现&#039;&#039;&#039;并非易事&#039;&#039;&#039;，别指望着轻而易举就能完成。&lt;br /&gt;
&lt;br /&gt;
== 架构是什么？ ==&lt;br /&gt;
&lt;br /&gt;
首先让我们来申明OC模组语境下的“架构”是什么。架构指将某种代码执行器（以下称虚拟机）和OC模组的组件/Minecraft结合起来的程序。OC将组件逻辑与电脑逻辑严格分开。换言之，组件（例如显卡）不知道它自身在使用什么架构。反过来，架构也不知道组件如何工作。传递的一切信息都由&#039;&#039;&#039;执行机&#039;&#039;&#039;处理。执行机驱动架构，并提供回调函数使架构能与外界通信。&lt;br /&gt;
&lt;br /&gt;
== 使用架构 ==&lt;br /&gt;
&lt;br /&gt;
为了让内容不那么难以理解，这里将讲解如何实际&#039;&#039;&#039;应用&#039;&#039;&#039;架构。这大概能让读者更好地理解架构实现是怎么融入OC模组生态的。要使用架构，则必须创建一个使用此架构的执行机。&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;注意：&#039;&#039;&#039;OC 1.4版本后，附属模组可以注册通过CPU提供的架构，因此你无需添加一种电脑方块/物品了。可以通过手持CPU时潜行右键单击来切换其提供的架构。&lt;br /&gt;
&lt;br /&gt;
执行机封装了架构，并负责线程调度、信号队列、网络连接等。执行机创建好以后，你只需每tick调用一次&amp;lt;code&amp;gt;update()&amp;lt;/code&amp;gt;函数（还可能需要向其传递一些调用，例如&amp;lt;code&amp;gt;start()&amp;lt;/code&amp;gt;）。&lt;br /&gt;
&lt;br /&gt;
== 生命周期 ==&lt;br /&gt;
&lt;br /&gt;
架构的生命周期大致为：实例化，&amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt;，反复执行&amp;lt;code&amp;gt;runThreaded()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;runSynchronized()&amp;lt;/code&amp;gt;，&amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt; [, 转到 &amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt;]。&lt;br /&gt;
&lt;br /&gt;
* 执行机被创建时，架构即被创建。 - 执行机启动或重启时，会要求&amp;lt;code&amp;gt;initialize()&amp;lt;/code&amp;gt;自身。通常来说代表着创建底层虚拟机。 - 驱动架构的标准方法是通过&amp;lt;code&amp;gt;runThreaded()&amp;lt;/code&amp;gt;函数，它由执行器线程调用。架构的状态应在此处处理。其返回值类型被执行机用于调度。&#039;&#039;&#039;注意：&#039;&#039;&#039;此方法不应抛出异常。你有责任处理所有错误，并在需要的时候将它们转换为代表报错的返回值。 - 需要有一个特殊执行结果来表示进行“同步调用”。这个返回值会告诉执行机下次需要用&amp;lt;code&amp;gt;runSynchronized()&amp;lt;/code&amp;gt;来驱动架构。这种调用&#039;&#039;&#039;一定会由MC服务端线程执行&#039;&#039;&#039;，代表着可以安全地在此回调函数中与世界交互。 - 执行机停止时会调用&amp;lt;code&amp;gt;close()&amp;lt;/code&amp;gt;函数。然后释放所有打开的资源。&lt;br /&gt;
&lt;br /&gt;
== 提供上下文 ==&lt;br /&gt;
&lt;br /&gt;
在最简单的样例中，每次调用&amp;lt;code&amp;gt;runThreaded()&amp;lt;/code&amp;gt;时在你的虚拟机中仅仅进行若干&amp;lt;code&amp;gt;run&amp;lt;/code&amp;gt;调用。不过这样做实际用处不大，因为你会想要以某种方式与外界通信。为了实现此功能，需要将与组件的通信提供给虚拟机。你需要在虚拟机内定义用于与执行机API通信的API（例如：&amp;lt;code&amp;gt;components()&amp;lt;/code&amp;gt;和&amp;lt;code&amp;gt;invoke()&amp;lt;/code&amp;gt;）。&lt;br /&gt;
&lt;br /&gt;
== 同步调用与调用上限 ==&lt;br /&gt;
&lt;br /&gt;
组件可能有一些回调函数声明为“直接调用”，此外它们可能还会定义“调用上限”。默认情况下，所有的组件回调函数都是&#039;&#039;&#039;同步调用&#039;&#039;&#039;，即架构必须保证这些函数只能由&amp;lt;code&amp;gt;runSynchronized()&amp;lt;/code&amp;gt;调用。 直接调用的调用上限限制了单个执行机每tick调用某一个函数的次数。限制本身由执行机的&amp;lt;code&amp;gt;invoke&amp;lt;/code&amp;gt;方法确保实现，此方法会在调用次数过多时抛出&amp;lt;code&amp;gt;LimitReachedException&amp;lt;/code&amp;gt;异常。此时架构应当短暂休眠，或者改为执行非同步调用。&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;注意：&#039;&#039;&#039;在写这段内容的时候，我发现我忘记了把一些执行检查所需的方法放入API中。这将在下一个API更新中得到修复，使这些方法在&amp;lt;code&amp;gt;Component&amp;lt;/code&amp;gt;接口中可用。现在的话，要么将它们反射进去，要么从OC源码自行构建模组。我对此表示抱歉。&lt;br /&gt;
&lt;br /&gt;
== 样例代码 ==&lt;br /&gt;
&lt;br /&gt;
假设你有能提供以下接口的虚拟机：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
/**虚拟机自身。这里的仅为一个样例，不是“真的”接口。*/ public interface PseudoVM {&lt;br /&gt;
&lt;br /&gt;
Object[] run(Object[] args) throws Exception;&lt;br /&gt;
&lt;br /&gt;
void setApiFunction(String name, PseudoNativeFunction value);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/**定义了主机提供的回调的接口。*/ public interface PseudoNativeFunction {&lt;br /&gt;
&lt;br /&gt;
Object invoke(Object[] args);&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
一个简单的架构实现大概看起来像这样子：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;java&amp;quot;&amp;gt;&lt;br /&gt;
/**这是你实现的类；这里的架构来自OC的API。*/ @Architecture.Name(&amp;quot;Pseudolang&amp;quot;) public class PseudoArchitecture implements Architecture {&lt;br /&gt;
&lt;br /&gt;
private final Machine machine;&lt;br /&gt;
&lt;br /&gt;
private PseudoVM vm;&lt;br /&gt;
&lt;br /&gt;
/**构造函数的签名必须与此处完全一致。*/&lt;br /&gt;
public PseudoArchitecture(Machine machine) {&lt;br /&gt;
  this.machine = machine;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public boolean isInitialized() { return true; }&lt;br /&gt;
&lt;br /&gt;
public void recomputeMemory() {}&lt;br /&gt;
&lt;br /&gt;
public boolean initialize() {&lt;br /&gt;
  //在此处新建虚拟机，并在里面注册你想要的所有API回调函数。&lt;br /&gt;
  vm = new PseudoVM();&lt;br /&gt;
  vm.setApiFunction(&amp;quot;invoke&amp;quot;, new PseudoNativeFunction() {&lt;br /&gt;
    public Object invoke(Object[] args) {&lt;br /&gt;
      final String address = (String)args[0];&lt;br /&gt;
      final String method = (String)args[1];&lt;br /&gt;
      final Object[] params = (Object[])args[2];&lt;br /&gt;
      try {&lt;br /&gt;
        return new Object[]{true, machine.invoke(address, method, params)};&lt;br /&gt;
      }&lt;br /&gt;
      catch (e LimitReachedException) {&lt;br /&gt;
        //这里的执行逻辑也可用于休眠/执行同步调用。&lt;br /&gt;
        //在此样例中我们遵循这样的协议：&lt;br /&gt;
        //成功时返回(true,某物)，到达上限时返回(false)。&lt;br /&gt;
        //此后虚拟机中运行的脚本需要将控制权交还给&lt;br /&gt;
        //初始化了当前执行的任务的调用者（例如，若支持的话&lt;br /&gt;
        //可以yield，或者在事件驱动的系统中直接return）&lt;br /&gt;
        return new Object[]{false};&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
  vm.setApiFunction(&amp;quot;isDirect&amp;quot;, new PseudoNativeFunction() {&lt;br /&gt;
    public Object invoke(Object[] args) {&lt;br /&gt;
      final String address = (String)args[0];&lt;br /&gt;
      final String method = (String)args[1];&lt;br /&gt;
      final Node node = machine.node().network().node(address);&lt;br /&gt;
      if (node instanceof Component) {&lt;br /&gt;
        final Component component = (Component) node;&lt;br /&gt;
        if (component.canBeSeenFrom(machine.node())) {&lt;br /&gt;
          final Callback callback = machine.methods(node.host()).get(method);&lt;br /&gt;
          if (callback != null) {&lt;br /&gt;
            return callback.direct();&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      return false;&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
  // ... 更多回调。&lt;br /&gt;
  return true;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void close() {&lt;br /&gt;
  vm = null;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
ExecutionResult runThreaded(boolean isSynchronizedReturn) {&lt;br /&gt;
  //在此处执行所需步骤。通常你会希望通过将队列中下一个信号传递给&lt;br /&gt;
  //虚拟机的方式来唤醒它，但你也可能会选择让你的虚拟机手动拉取信号。&lt;br /&gt;
  try {&lt;br /&gt;
    final Signal signal;&lt;br /&gt;
    if (isSynchronizedReturn) {&lt;br /&gt;
      //正在从同步调用中返回时不要拉取信号！因为我们正在执行其他事情。&lt;br /&gt;
      signal = null;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      signal = machine.popSignal();&lt;br /&gt;
    }&lt;br /&gt;
    final Object[] result;&lt;br /&gt;
    if (signal != null) {&lt;br /&gt;
      result = vm.run(new Object[]{signal.name(), signal.args()});&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
      result = vm.run(null);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    //你可能会想定义一些内部协议，用以决定何时执行同步调用。&lt;br /&gt;
    //假设我们希望虚拟机在出现待决定的同步调用时返回数字值，代表休眠&lt;br /&gt;
    //或者返回布尔值，代表关机/重启或其他东西。&lt;br /&gt;
    if (result != null) {&lt;br /&gt;
      if (result[0] instanceof Boolean) {&lt;br /&gt;
        return new ExecutionResult.Shutdown((Boolean)result[0]);&lt;br /&gt;
      }&lt;br /&gt;
      if (result[0] instanceof Integer) {&lt;br /&gt;
        return new ExecutionResult.Sleep((Integer)result[0]);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    //若返回此值，下次&#039;resume&#039;时会调用runSynchronized。&lt;br /&gt;
    //此次调用后的下次对runThreaded函数的调用中&lt;br /&gt;
    //isSynchronizedReturn参数将会设定为true。&lt;br /&gt;
    return new ExecutionResult.SynchronizedCall();&lt;br /&gt;
  }&lt;br /&gt;
  catch (Throwable t) {&lt;br /&gt;
    return new ExecutionResult.Error(t.toString);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void runSynchronized() {&lt;br /&gt;
  //同步调用在MC的服务端线程中执行，让回调与世界交互更方便&lt;br /&gt;
  //（因为它们之间的同步由执行机/架构完成）&lt;br /&gt;
  //这意味着若虚拟机中的代码开始了一次同步调用，就需要&#039;&#039;暂停&#039;&#039;&lt;br /&gt;
  //并且放弃对主机的控制，然后我们切换到同步调用模式（参看runThreaded），&lt;br /&gt;
  //并等待MC服务端线程，接着再进行实际的调用。&lt;br /&gt;
  //可以在runThread中传递调用所需的信息，将其存储在架构中，&lt;br /&gt;
  //然后在此处直接进行调用。&lt;br /&gt;
  //对此样例而言，让我们假定状态信息存储于虚拟机内部，并且&lt;br /&gt;
  //下次resume使其进行&#039;&#039;实际&#039;&#039;调用。下面给出了处理它们的伪代码。&lt;br /&gt;
  vm.run(null);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void onConnect() {}&lt;br /&gt;
&lt;br /&gt;
//用这行代码加载虚拟机状态，假如虚拟机可持续。&lt;br /&gt;
void load(NBTTagCompound nbt) {}&lt;br /&gt;
&lt;br /&gt;
//用这行代码保存虚拟机状态，假如虚拟机可持续。&lt;br /&gt;
void save(NBTTagCompound nbt) {}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
一些用于在虚拟机中处理同步调用的伪代码：&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;scala&amp;quot;&amp;gt;&lt;br /&gt;
private def invokeSynchronous(address, method, ...) {&lt;br /&gt;
&lt;br /&gt;
yield; //此处为返回到runThreaded()的地方。&lt;br /&gt;
 //此处为进入runSynchronized();的地方&lt;br /&gt;
val result = native.invoke(address, method, ...);&lt;br /&gt;
//查看initialize()中invoke的定义以获取结果值的信息。&lt;br /&gt;
yield; //并返回runSynchronized();&lt;br /&gt;
//并且下一个runThreaded()又进入了。&lt;br /&gt;
return result[1];&lt;br /&gt;
&lt;br /&gt;
} private def invokeDirect(address, method, ...) {&lt;br /&gt;
&lt;br /&gt;
val result = native.invoke(address, method, ...);&lt;br /&gt;
//查看initialize()中invoke的定义以获取结果值的信息。&lt;br /&gt;
if (result[0] == true) {&lt;br /&gt;
  return result[1];&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
  return invokeSynchronous(address, method, ...);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
} def invoke(address, method, ...) {&lt;br /&gt;
&lt;br /&gt;
if (isDirect(address, method)) {&lt;br /&gt;
  return invokeDirect(address, method, ...);&lt;br /&gt;
}&lt;br /&gt;
else {&lt;br /&gt;
  return invokeSynchronous(address, method, ...);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 注册架构 ==&lt;br /&gt;
&lt;br /&gt;
这一步非常简单，只需调用&amp;lt;code&amp;gt;li.cil.oc.api.Machine.add(PseudoArchitecture.class)&amp;lt;/code&amp;gt;即可。注册架构后CPU即可提供被注册的架构。如果你不想让OC模组的CPU提供你的架构，那么你可以添加自己的&amp;lt;code&amp;gt;Processor&amp;lt;/code&amp;gt;驱动程序以用于访问架构。或者更进一步，添加你自己的电脑方块，专用于运行你的架构。&lt;br /&gt;
&lt;br /&gt;
== 目录 ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents/zh}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/debug_1.7&amp;diff=1595</id>
		<title>Tutorial/debug 1.7</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/debug_1.7&amp;diff=1595"/>
		<updated>2026-08-24T20:28:22Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ====== Steps to Build and Run master-MC1.7.10 from source =&lt;br /&gt;
&lt;br /&gt;
These instructions assume you have cloned the repo, &amp;lt;code&amp;gt;git clone git@github.com:MightyPirates/OpenComputers.git&amp;lt;/code&amp;gt; and are working from the master-MC1.7.10 branch, currently the default branch.&lt;br /&gt;
&lt;br /&gt;
== IDEA instructions ==&lt;br /&gt;
&lt;br /&gt;
= run &amp;lt;code&amp;gt;./gradlew setupDecompWorkspace&amp;lt;/code&amp;gt; =&lt;br /&gt;
= run &amp;lt;code&amp;gt;./gradlew build&amp;lt;/code&amp;gt; =&lt;br /&gt;
= run &amp;lt;code&amp;gt;./gradlew idea&amp;lt;/code&amp;gt; =&lt;br /&gt;
= From IDEA, open the OpenComputers.ipr created from &amp;lt;code&amp;gt;./gradlew idea&amp;lt;/code&amp;gt; =&lt;br /&gt;
= Open File-&amp;gt;Project Structure (or press Shift+Control+Alt+S) =&lt;br /&gt;
= In the left pane select Modules =&lt;br /&gt;
= Depending on how you imported the project there may be mulitple top level modules. There should only be one. Keep only the one named OpenComputers which has three sub modules named &amp;lt;code&amp;gt;OpenComputers_api&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OpenComputers_main&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;OpenComputers_test&amp;lt;/code&amp;gt;. Right click on the others and choose to delete them. =&lt;br /&gt;
= Set separate output paths for &amp;lt;code&amp;gt;OpenComputers_api&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;OpenComputers_main&amp;lt;/code&amp;gt;. In the middle pane select &amp;lt;code&amp;gt;OpenComputers_api&amp;lt;/code&amp;gt;, in the right pane select &amp;quot;Paths&amp;quot; and change the &amp;quot;Output path:&amp;quot; from ending in /main to end in /api =&lt;br /&gt;
= Set the scope of other minecraft mods in Dependencies &amp;quot;provided&amp;quot;. This needs to be done for &amp;lt;code&amp;gt;OpenComputers_api&amp;lt;/code&amp;gt; as well as &amp;lt;code&amp;gt;OpenComputers_main&amp;lt;/code&amp;gt;. Select &amp;quot;Dependencies&amp;quot; in the right pane. A large list of dependencies are listed. All, by default due to importing the gradle properties, will default to &amp;quot;Compile&amp;quot;. Set all of the following to &amp;quot;Provided&amp;quot; =&lt;br /&gt;
#* factorization-api&lt;br /&gt;
#* ic2classic-api&lt;br /&gt;
#* AgriCraft&lt;br /&gt;
#* Gradle: appeng:RotaryCraft:api&lt;br /&gt;
#* Gradle: appeng:appliedenergistics2&lt;br /&gt;
#* Gradle: codechicken:EnderStorage&lt;br /&gt;
#* Gradle: codechicken:WR-CBE&lt;br /&gt;
#* Gradle: com.bluepowermod:BluePower&lt;br /&gt;
#* Gradle: com.gregoriust.gregtech&lt;br /&gt;
#* Gradle: igwmod:IGW-Mod-1.7.10&lt;br /&gt;
#* Gradle: li.cil.tis3d:TIS-3D&lt;br /&gt;
#* Gradle: mcp.mobius.waila&lt;br /&gt;
#* Gradle: net.industrial-craft:industrialcraft-2&lt;br /&gt;
#* Gradle: net.sengir.forestry&lt;br /&gt;
#* Gradle: dev.modwarriors.notenoughkeys:NotEnoughKeys&lt;br /&gt;
#* Gradle: qmunity:QmunityLib&lt;br /&gt;
#* Gradle: tmech:TMechworks&lt;br /&gt;
#* Gradle: mrtjp:ProjectRed&lt;br /&gt;
#* Gradle: :buildcraft&lt;br /&gt;
#* Gradle: :GalacticraftCoreAll&lt;br /&gt;
#* Gradle: :MekanismAll&lt;br /&gt;
#* Gradle: :redlogic&lt;br /&gt;
#* Gradle: :CoFHLib&lt;br /&gt;
#* Gradle: :CoFHCore&lt;br /&gt;
#* Gradle: :MineFactoryReloaded&lt;br /&gt;
#* Gradle: :ComputerCraft&lt;br /&gt;
#* Gradle: :EnderIO&lt;br /&gt;
#* Gradle: :Railcraft&lt;br /&gt;
#* Gradle: :BloodMagic&lt;br /&gt;
#* Gradle: :ExtraCells&lt;br /&gt;
#* Gradle: :ThaumicEnergistics&lt;br /&gt;
#* Gradle: api:rf&lt;br /&gt;
#* Gradle: codechicken:CodeChickenCore&lt;br /&gt;
#* Gradle: api:mfr&lt;br /&gt;
#* Gradle: api:railcraft&lt;br /&gt;
#* Gradle: appeng:mekanism&lt;br /&gt;
#* Gradle: api:ic2&lt;br /&gt;
#* Gradle: api:betterstorage&lt;br /&gt;
#* Gradle: api:immibis&lt;br /&gt;
#* Gradle: api:coloredlightscore&lt;br /&gt;
#* Gradle: api:craftguide&lt;br /&gt;
#* Gradle: api:rblocks&lt;br /&gt;
#* Gradle: api:invtweaks&lt;br /&gt;
#* Gradle: appeng:Waila&lt;br /&gt;
#* Gradle: tconstruct:TConstruct&lt;br /&gt;
#* Gradle: codechicken:NotEnoughItems&lt;br /&gt;
#* Gradle: codechicken:CodeChickenLib&lt;br /&gt;
#* Gradle: codechicken:ForgeMultipart&lt;br /&gt;
#* Gradle: mantle:Mantle&lt;br /&gt;
= Again, make sure you repeat this process for both &amp;lt;code&amp;gt;OpenComputers_api&amp;lt;/code&amp;gt; as well as &amp;lt;code&amp;gt;OpenComputers_main&amp;lt;/code&amp;gt;. =&lt;br /&gt;
= Close Project Structure window (press OK) =&lt;br /&gt;
= Open Run-&amp;gt;Edit configurations, select Application on the left pane, and set &amp;lt;code&amp;gt;Use classpath of module&amp;lt;/code&amp;gt; (left Alt+O) to &amp;lt;code&amp;gt;OpenComputers.main&amp;lt;/code&amp;gt; for both &amp;lt;code&amp;gt;Minecraft Client&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Minecraft Server&amp;lt;/code&amp;gt;. This fixes the error &amp;lt;code&amp;gt;Error: Could not find or load main class GradleStart&amp;lt;/code&amp;gt; if you forgot to do this step. =&lt;br /&gt;
= Build Project (Shift F9) =&lt;br /&gt;
= Make symlink called &amp;lt;code&amp;gt;assets&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;build\resources\main\assets&amp;lt;/code&amp;gt; in folders &amp;lt;code&amp;gt;build\classes\java\main&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;build\classes\scala\main&amp;lt;/code&amp;gt;. Otherwise textures and other assets won&#039;t work. =&lt;br /&gt;
= Select &amp;quot;Minecraft Client&amp;quot; as the run target, and run! =&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;Keep in mind that if you want to build a .jar that can be used outside of the development environment, you need to run ./gradlew clean and then ./gradlew assemble to produce .jar with obfuscated names so that it works with normal minecraft.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/custom_oses&amp;diff=1594</id>
		<title>Tutorial/custom oses</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/custom_oses&amp;diff=1594"/>
		<updated>2026-08-24T20:28:21Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Writing custom OSes =&lt;br /&gt;
&lt;br /&gt;
Starting with OpenComputers 1.3 computers need an &amp;quot;Operating System&amp;quot; to function - OpenOS is no longer built-in. As of 1.4(.2?) you even need an EEPROM in your computers, which acts as the BIOS of the machine. While this makes it slightly more annoying to set up computers, it opens up a lot more possibilities for people that want to write their own OSes; which is what this article is all about!&lt;br /&gt;
&lt;br /&gt;
== Booting ==&lt;br /&gt;
&lt;br /&gt;
The only thing built-in now is the &amp;quot;machine&amp;quot; booting the BIOS. This &amp;quot;machine&amp;quot; wrapper is a non-editable Lua script responsible for setting up the sandbox and super-low-level interaction with the &amp;quot;host&amp;quot; side of things (that being Java/Scala). When a computer is powered on, that script is loaded and run. After estabilishing the sandboxed environment it looks for an EEPROM and tries to run the data on it as a Lua script. If that fails the computer crashes. Otherwise that BIOS is run. For the provided one (Lua BIOS, crafted using EEPROM + book) it will perform the actions previously done by the machine itself:&lt;br /&gt;
&lt;br /&gt;
# It looks for a file system with address set via &amp;lt;code&amp;gt;computer.setBootAddress&amp;lt;/code&amp;gt; (read via &amp;lt;code&amp;gt;computer.getBootAddress&amp;lt;/code&amp;gt;). 2. On that file system it tries to load and execute a Lua script from a file named &amp;lt;code&amp;gt;init.lua&amp;lt;/code&amp;gt;. 3. If it succeeds, that&#039;s it. The user script is now in control. If it fails, it iterates over all present file systems, performing step 2 again until it succeeds. 4. If no working &amp;lt;code&amp;gt;init.lua&amp;lt;/code&amp;gt; script is found, the computer crashes with a &amp;lt;code&amp;gt;no bootable medium found&amp;lt;/code&amp;gt; error.&lt;br /&gt;
&lt;br /&gt;
But since you can program the EEPROM however you like you could also boot from a network device, for example.&lt;br /&gt;
&lt;br /&gt;
== What&#039;s Available? ==&lt;br /&gt;
&lt;br /&gt;
There are a few libraries documented on the wiki that are in fact part of OpenOS, so when you&#039;re implementing your own operation system, those won&#039;t be at your disposal. Most notably that includes the &amp;lt;code&amp;gt;io&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;package&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;filesystem&amp;lt;/code&amp;gt; libraries. For a definite reference for what&#039;s available in an init script, check the kernel or [https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/src/main/resources/assets/opencomputers/lua/machine.lua machine.lua script for your current OpenComputers version]. The following list isn&#039;t guaranteed to be complete and/or up-to-date!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
_G _VERSION&lt;br /&gt;
&lt;br /&gt;
assert error getmetatable ipairs load next pairs pcall rawequal rawget rawlen rawset select setmetatable tonumber tostring type xpcall&lt;br /&gt;
&lt;br /&gt;
bit32.arshift bit32.band bit32.bnot bit32.bor bit32.btest bit32.bxor bit32.extract bit32.lrotate bit32.lshift bit32.replace bit32.rrotate bit32.rshift&lt;br /&gt;
&lt;br /&gt;
coroutine.create coroutine.resume coroutine.running coroutine.status coroutine.wrap coroutine.yield&lt;br /&gt;
&lt;br /&gt;
debug.getinfo debug.traceback debug.getlocal debug.getupvalue&lt;br /&gt;
&lt;br /&gt;
math.abs math.acos math.asin math.atan math.atan2 math.ceil math.cos math.cosh math.deg math.exp math.floor math.fmod math.frexp math.huge math.ldexp math.log math.max math.min math.modf math.pi math.pow math.rad math.random math.randomseed math.sin math.sinh math.sqrt math.tan math.tanh&lt;br /&gt;
&lt;br /&gt;
os.clock os.date os.difftime os.time&lt;br /&gt;
&lt;br /&gt;
string.byte string.char string.dump string.find string.format string.gmatch string.gsub string.len string.lower string.match string.rep string.reverse string.sub string.upper&lt;br /&gt;
&lt;br /&gt;
table.concat table.insert table.pack table.remove table.sort table.unpack&lt;br /&gt;
&lt;br /&gt;
checkArg&lt;br /&gt;
&lt;br /&gt;
component.doc component.fields component.invoke component.list component.methods component.proxy component.slot component.type&lt;br /&gt;
&lt;br /&gt;
computer.address computer.addUser computer.beep computer.energy computer.freeMemory computer.getArchitectures computer.getArchitecture computer.getBootAddress computer.getDeviceInfo computer.getProgramLocations computer.isRobot computer.maxEnergy computer.pullSignal computer.pushSignal computer.removeUser computer.setArchitecture computer.setBootAddress computer.shutdown computer.tmpAddress computer.totalMemory computer.uptime computer.users&lt;br /&gt;
&lt;br /&gt;
unicode.char unicode.charWidth unicode.isWide unicode.len unicode.lower unicode.reverse unicode.sub unicode.upper unicode.wlen unicode.wtrunc&lt;br /&gt;
&lt;br /&gt;
-- Lua 5.3 only: coroutine.isyieldable&lt;br /&gt;
&lt;br /&gt;
string.pack string.unpack string.packsize&lt;br /&gt;
&lt;br /&gt;
table.move&lt;br /&gt;
&lt;br /&gt;
math.maxinteger math.mininteger math.tointeger math.type math.ult&lt;br /&gt;
&lt;br /&gt;
utf8.char utf8.charpattern utf8.codes utf8.codepoint utf8.len utf8.offset&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Accessing components in BIOS ==&lt;br /&gt;
&lt;br /&gt;
Unlike in OpenOS you don&#039;t have the primary system for components in BIOS. That means you cannot just write &amp;lt;code&amp;gt;component.redstone.setOutput&amp;lt;/code&amp;gt;, you have to manually create your proxy for the component first. For several reasons the proxy system is provided via the machine itself, so you can still use that. To get a proxy, use the &amp;lt;code&amp;gt;component.proxy&amp;lt;/code&amp;gt; method. It expects the address of the component to wrap. You can either write that down manually, or get the list of components using &amp;lt;code&amp;gt;component.list&amp;lt;/code&amp;gt; and go from there.&lt;br /&gt;
&lt;br /&gt;
For example, to get the first best redstone component, you can do the following: &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local r = component.proxy(component.list(&amp;quot;redstone&amp;quot;)())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; This works because &amp;lt;code&amp;gt;component.list&amp;lt;/code&amp;gt; returns a Lua iterator: a function that can be called repeatedly to get the next element in whatever it iterates over, returning &amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; once there are no more elements.&lt;br /&gt;
&lt;br /&gt;
== A few more things ==&lt;br /&gt;
&lt;br /&gt;
When an init script returns, the computer powers down, so you&#039;ll want to have a &amp;quot;main loop&amp;quot; in there, regardless of how you structure your OS otherwise. Signals must be processed using &amp;lt;code&amp;gt;computer.pullSignal&amp;lt;/code&amp;gt;, you&#039;re free to use any model to provide this to programs running in your OS you please - this is what OpenOS&#039; &amp;lt;code&amp;gt;event.pull&amp;lt;/code&amp;gt; does, for example.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll also want to take care of setting up attached components during your OS&#039; boot routine, such as binding GPUs to screens.&lt;br /&gt;
&lt;br /&gt;
Aside from that, go crazy, be creative!&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/custom_oses/zh&amp;diff=1593</id>
		<title>Tutorial/custom oses/zh</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/custom_oses/zh&amp;diff=1593"/>
		<updated>2026-08-24T20:28:20Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= 编写自制操作系统 =&lt;br /&gt;
&lt;br /&gt;
从OpenComputers 1.3开始，电脑需要“操作系统”才能运行，OpenOS不再内置。 从1.4（.2？）开始，您甚至需要在电脑中安装EEPROM，以充当机器的BIOS。 虽然这使得组装电脑变麻烦了一点，但这也为想要编写自己的操作系统的人提供了更多可能；本文的主题就是编写操作系统！&lt;br /&gt;
&lt;br /&gt;
== 引导启动 ==&lt;br /&gt;
&lt;br /&gt;
现在唯一还内置的是用来启动BIOS的“机器”。这个“机器”包装器是一个不可编辑的Lua脚本，负责创建沙箱和与“主机”端事物（即Java / Scala）的特别底层的交互。电脑上电后，将加载并运行该脚本。在建立完成沙箱环境后，它会查找EEPROM，并尝试将其数据作为Lua脚本运行。如果失败，计算机将崩溃。否则，将运行该BIOS。对于模组提供的Lua BIOS（使用 EEPROM +手册制作），它将执行以前由机器本身完成的操作：&lt;br /&gt;
&lt;br /&gt;
= 1.它将查找先前由&amp;lt;code&amp;gt;computer.setBootAddress&amp;lt;/code&amp;gt;设定的地址对应的文件系统（可通过&amp;lt;code&amp;gt;computer.getBootAddress&amp;lt;/code&amp;gt;读取此地址）。 =&lt;br /&gt;
= 2.在该文件系统上，它尝试加载并执行名为&amp;lt;code&amp;gt;init.lua&amp;lt;/code&amp;gt;的文件。 =&lt;br /&gt;
= 3.如果成功了，那么任务完成，由用户脚本接管控制权。如果失败，它将遍历所有当前文件系统，再次执行步骤2，直到成功为止。 =&lt;br /&gt;
= 4.如果没有找到可用的&amp;lt;code&amp;gt;init.lua&amp;lt;/code&amp;gt;脚本。计算机将崩溃并显示&amp;lt;code&amp;gt;no bootable medium found&amp;lt;/code&amp;gt;错误. =&lt;br /&gt;
&lt;br /&gt;
但是，由于您可以随心所欲地对EEPROM进行编程，因此您也可以从网络设备启动。&lt;br /&gt;
&lt;br /&gt;
== 什么可用？ ==&lt;br /&gt;
&lt;br /&gt;
Wiki 上记录了一些库，它们实际上是 OpenOS 的一部分，所以当你实现自己的操作系统时这些库无法使用。最值得注意的是，其中包括&amp;lt;code&amp;gt;io&amp;lt;/code&amp;gt;、&amp;lt;code&amp;gt;package&amp;lt;/code&amp;gt;和&amp;lt;code&amp;gt;filesystem&amp;lt;/code&amp;gt;库。要获取init脚本中可用内容的明确参考，请查看系统内核代码或[[&amp;lt;nowiki/&amp;gt;[[https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/src/main/resources/assets/opencomputers/lua/machine.lua%7C你当前OpenComputer模组版本对应的machine.lua脚本|https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/src/main/resources/assets/opencomputers/lua/machine.lua|你当前OpenComputer模组版本对应的machine.lua脚本]]]（英文）。 以下列表不能保证完整性与/或时效性！&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
_G _VERSION&lt;br /&gt;
&lt;br /&gt;
assert error getmetatable ipairs load next pairs pcall rawequal rawget rawlen rawset select setmetatable tonumber tostring type xpcall&lt;br /&gt;
&lt;br /&gt;
bit32.arshift bit32.band bit32.bnot bit32.bor bit32.btest bit32.bxor bit32.extract bit32.lrotate bit32.lshift bit32.replace bit32.rrotate bit32.rshift&lt;br /&gt;
&lt;br /&gt;
coroutine.create coroutine.resume coroutine.running coroutine.status coroutine.wrap coroutine.yield&lt;br /&gt;
&lt;br /&gt;
debug.getinfo debug.traceback debug.getlocal debug.getupvalue&lt;br /&gt;
&lt;br /&gt;
math.abs math.acos math.asin math.atan math.atan2 math.ceil math.cos math.cosh math.deg math.exp math.floor math.fmod math.frexp math.huge math.ldexp math.log math.max math.min math.modf math.pi math.pow math.rad math.random math.randomseed math.sin math.sinh math.sqrt math.tan math.tanh&lt;br /&gt;
&lt;br /&gt;
os.clock os.date os.difftime os.time&lt;br /&gt;
&lt;br /&gt;
string.byte string.char string.dump string.find string.format string.gmatch string.gsub string.len string.lower string.match string.rep string.reverse string.sub string.upper&lt;br /&gt;
&lt;br /&gt;
table.concat table.insert table.pack table.remove table.sort table.unpack&lt;br /&gt;
&lt;br /&gt;
checkArg&lt;br /&gt;
&lt;br /&gt;
component.doc component.fields component.invoke component.list component.methods component.proxy component.slot component.type&lt;br /&gt;
&lt;br /&gt;
computer.address computer.addUser computer.beep computer.energy computer.freeMemory computer.getArchitectures computer.getArchitecture computer.getBootAddress computer.getDeviceInfo computer.getProgramLocations computer.isRobot computer.maxEnergy computer.pullSignal computer.pushSignal computer.removeUser computer.setArchitecture computer.setBootAddress computer.shutdown computer.tmpAddress computer.totalMemory computer.uptime computer.users&lt;br /&gt;
&lt;br /&gt;
unicode.char unicode.charWidth unicode.isWide unicode.len unicode.lower unicode.reverse unicode.sub unicode.upper unicode.wlen unicode.wtrunc&lt;br /&gt;
&lt;br /&gt;
-- 仅Lua 5.3: coroutine.isyieldable&lt;br /&gt;
&lt;br /&gt;
string.pack string.unpack string.packsize&lt;br /&gt;
&lt;br /&gt;
table.move&lt;br /&gt;
&lt;br /&gt;
math.maxinteger math.mininteger math.tointeger math.type math.ult&lt;br /&gt;
&lt;br /&gt;
utf8.char utf8.charpattern utf8.codes utf8.codepoint utf8.len utf8.offset&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 在BIOS中访问组件 ==&lt;br /&gt;
&lt;br /&gt;
与OpenOS不同，BIOS没有首选组件系统。这意味着你不能只写&amp;lt;code&amp;gt;component.redstone.setOutput&amp;lt;/code&amp;gt;，而是必须先手动为组件创建代理对象。出于多种原因，代理对象系统是通过机器本身提供的，因此您仍然可以使用它。若要获取代理对象，请使用&amp;lt;code&amp;gt;component.proxy&amp;lt;/code&amp;gt;方法。此函数需要你提供打算包装的组件地址。 您可以手动写下来，或者用&amp;lt;code&amp;gt;component.list&amp;lt;/code&amp;gt;来获得组件的列表并从中获取。&lt;br /&gt;
&lt;br /&gt;
比如说，要获得第一个红石组件，您可以执行以下操作： &amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local r = component.proxy(component.list(&amp;quot;redstone&amp;quot;)())&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt; 这行代码可以工作，因为&amp;lt;code&amp;gt;component.list&amp;lt;/code&amp;gt;返回一个 Lua 迭代器——一个可重复调用来获得下一个元素的函数，当没有元素时返回&amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt;。&lt;br /&gt;
&lt;br /&gt;
== 其他东西 ==&lt;br /&gt;
&lt;br /&gt;
当&amp;lt;code&amp;gt;init.lua&amp;lt;/code&amp;gt;脚本返回（结束运行）的时候，电脑将会关机，所以无论您如何构建操作系统，在脚本里都需要有一个“主循环”。 信号必须使用&amp;lt;code&amp;gt;computer.pullSignal&amp;lt;/code&amp;gt;处理，您可以自由使用任何模块将此函数的功能提供给在操作系统中运行的程序——例如，OpenOS中的&amp;lt;code&amp;gt;event.pull&amp;lt;/code&amp;gt;就是这么操作的。&lt;br /&gt;
&lt;br /&gt;
您还需要在操作系统的启动流程中设置连接到电脑的组件，例如将GPU绑定到屏幕。&lt;br /&gt;
&lt;br /&gt;
除此之外，发挥你最疯狂的想象吧！&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
	<entry>
		<id>https://ocr.michiyo.me/w/index.php?title=Tutorial/autorun_options&amp;diff=1592</id>
		<title>Tutorial/autorun options</title>
		<link rel="alternate" type="text/html" href="https://ocr.michiyo.me/w/index.php?title=Tutorial/autorun_options&amp;diff=1592"/>
		<updated>2026-08-24T20:28:19Z</updated>

		<summary type="html">&lt;p&gt;Michiyo: Clean up Markdown artifacts from DokuWiki migration&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Custom Startup Programs =&lt;br /&gt;
&lt;br /&gt;
OpenOS provides a variety of tools for running programs automatically. This document describes all of those tools, how to use them, and their comparative advantages and disadvantages. Note that some of these options were not available or as robust an option in older versions of the operating system. The 1.7.2 development builds (across all supported minecraft versions) have full support as documented here. The next general available OC 1.7.3 will include all of the development build fixes.&lt;br /&gt;
&lt;br /&gt;
Before we cover the access points for running programs automatically, this document reviews background vs foreground, blocking calls, threads, and event registrants (listeners and timers).&lt;br /&gt;
&lt;br /&gt;
=== Background vs Foreground Interactive Processes ===&lt;br /&gt;
&lt;br /&gt;
A foreground interactive program interrupts or delays OpenOS from loading the shell. This type of modal process is a natural fit for programs that need to wait for user input. For developing background processes, OpenOS provides threads and event registrants (listeners and timers). When designing a custom startup program, a user is at liberty to choose either background or foreground approach, as may suit their needs.&lt;br /&gt;
&lt;br /&gt;
=== Blocking Calls ===&lt;br /&gt;
&lt;br /&gt;
When building a background process you need to know what is a blocking call, and what is blocked when you call it. There are two distinct types of blocking calls: machine blocking and system yielding.&lt;br /&gt;
&lt;br /&gt;
Machine blocking calls are a subset of component api, such as a filesystem file read, which cause the entire OpenComputer machine state to wait. These blocking calls are outside the control of the operating system, are truly blocking, and outside our control. Thus we generally are not referring to machine blocking calls when we discuss whether an operation is blocking or not. During a machine blocking call nothing in the system is running.&lt;br /&gt;
&lt;br /&gt;
In contrast, the system call &amp;lt;code&amp;gt;computer.pullSignal&amp;lt;/code&amp;gt; yields the current thread (event.pull and os.sleep also call computer.pullSignal). OpenOS creates a single init thread and runs all processes inside that one thread. If you create multiple coroutines and resume them in a continuous loop, once any single coroutine makes a system yield call -- your loop would be blocked on that one coroutine. You would not see it yield back to you. For all intents and purposes, all coroutines in that thread are essentially suspended. When developing a background application this is a critical workflow to understand. If you need your application to sleep for 10s before resuming its work, and you call os.sleep(10), you block execution of the the foreground application (because are in the same thread). System yielding methods include: &amp;lt;code&amp;gt;term.read&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;os.sleep&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;event.pull&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Threads ===&lt;br /&gt;
&lt;br /&gt;
Review the threading library api documentation [[API/thread|here]]. Threads are ideal for developing a background application whose design, for the most part, can disregard the rest of the system.&lt;br /&gt;
&lt;br /&gt;
= ==== OpenOS threads are nonblocking processes =&lt;br /&gt;
&lt;br /&gt;
As we discussed previously, making a system yield call (anything that calls &amp;lt;code&amp;gt;computer.pullSignal&amp;lt;/code&amp;gt;), will suspend all coroutines in the current thread. However, you have the option to create your own thread. Any other thread that makes a system yield call does not block your thread, and your thread is free to make system yield calls without blocking any other thread. If you want your background process to broadcast a network message once every second, you can easily do so with a short while loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
thread.create(function()&lt;br /&gt;
&lt;br /&gt;
while true do&lt;br /&gt;
  modem.broadcast(port, msg)&lt;br /&gt;
  os.sleep(1)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= ==== OpenOS threads are not reentrant =&lt;br /&gt;
&lt;br /&gt;
The thread is programmed entirely from a single entry point. This paradigm can be easier to grasp, and allows the programmer to build the code as subsystem of its own. When the one and only thread function exits, the thread is dead and will not resume. Also note that when you create your thread (as described in the [[API/thread|thread]] documentation) it will attach to the current process. A thread blocks its parent process from closing until itself closes. If you want to run a truly background thread, not attached to any process, call &amp;lt;code&amp;gt;:detach()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
= ==== OpenOS threads are &amp;quot;event cooperative&amp;quot; =&lt;br /&gt;
&lt;br /&gt;
Event signals are queued and are removed from that queue when you pull them. If you have multiple and distinct areas of code making &amp;lt;code&amp;gt;pullSignal&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;event.pull&amp;lt;/code&amp;gt;) calls, one process may rob signals that another process was expecting (event handlers registered via event.listen is not affected by this). Threads pull from their own queue (there is no additional memory cost for this, technically a thread is a registered event handler that is unaffected by signal robbers), and when designing your background application running on an OpenOS thread, you need not concern yourself with the rest of the system and the events it may care about.&lt;br /&gt;
&lt;br /&gt;
= ==== OpenOS threads are not suitable for low memory systems =&lt;br /&gt;
&lt;br /&gt;
OpenOS needs less than 130k of ram to boot and run the interactive shell. One stick of tier 1 ram provides 196k of ram, thus leaving you with more than 60k of &amp;quot;wiggle&amp;quot; room. This low memory state is already severe as it is. However, the boot process does not fully load the available system libraries. Loading the thread library allocates an additional ~20k, and each user thread costs ~5k. These are conservative measurements and, frankly, imprecise due to the nature of the Lua VM and how it allocates memory in chunks. I will admit, the threading library could be optimized for memory costs. However, the threading library has thus far been strongly focused on correctness and robustness. Future versions will likely have reduced cost. Ideally, your system has &amp;gt;100k free even with all the libraries you need loaded, and all of your programs running.&lt;br /&gt;
&lt;br /&gt;
=== Event Registrants ===&lt;br /&gt;
&lt;br /&gt;
Event &#039;&#039;&#039;listeners&#039;&#039;&#039; and event &#039;&#039;&#039;timers&#039;&#039;&#039; are types of an event registrant.&lt;br /&gt;
&lt;br /&gt;
Review the event library api documentation [[API/event|here]]. Event registrants are ideal for developing background &amp;quot;responders&amp;quot;; tasks that spin up for short lived jobs in response to some expected event signal.&lt;br /&gt;
&lt;br /&gt;
= ==== Event Registrants are reentrant =&lt;br /&gt;
&lt;br /&gt;
The event handler function is a callback that is called every time the condition under its registration is satisfied.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
event.listen(&amp;quot;key_down&amp;quot;, function(...)&lt;br /&gt;
&lt;br /&gt;
handle_key_down(...)&lt;br /&gt;
&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;handle_key_down()&amp;lt;/code&amp;gt; is called every time there is a key_down signal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
event.timer(1, function()&lt;br /&gt;
  onTimeout()&lt;br /&gt;
end, 10)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;onTimeout()&amp;lt;/code&amp;gt; is called every 1s, and 10 times (see the [[API/event|event]] api for details).&lt;br /&gt;
&lt;br /&gt;
The callback also has the option to return &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;nil&amp;lt;/code&amp;gt; is not the same, specifically &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt;) to unregister itself. Returning anything else, or nothing, does not unregister the callback. Timers auto self unregister when the &amp;lt;code&amp;gt;times&amp;lt;/code&amp;gt; (3rd parameter) is met.&lt;br /&gt;
&lt;br /&gt;
This reentrant behavior can be easy to use when your program needs to do a small reoccuring task based on event, but it can be difficult to build a long running background system that needs to complete various and disparate jobs. Event registrants may not be well suited for programs that may need to keep state or complete a long list of jobs, or are wholely unrelated to a system event. Obviously, these are not rules, just subjective considerations lacking any context of your specific needs.&lt;br /&gt;
&lt;br /&gt;
= ==== Event Registrants are lightweight =&lt;br /&gt;
&lt;br /&gt;
Unlike threads, event registrations have a tiny footprint. The most basic registration &amp;lt;code&amp;gt;event.listen(&amp;quot;key_down&amp;quot;, function()end)&amp;lt;/code&amp;gt; could be as cheap as 400 bytes (you might find that a huge cost, welcome to the Lua VM). OpenOS already employs many event registrations, and the system has been heavily optimized for reliability and memory cost around these.&lt;br /&gt;
&lt;br /&gt;
= ==== Event Registrants are blocking =&lt;br /&gt;
&lt;br /&gt;
In case you skipped it, read about [[Tutorial/autorun options#blocking_calls|blocking calls]] earlier in this document. As stated, OpenOS runs on the singular init thread, and all processes and event registrants made thereon are suspended when any actor in that thread makes the system yield call, &amp;lt;code&amp;gt;computer.pullSignal&amp;lt;/code&amp;gt; (or any method that calls it, such as &amp;lt;code&amp;gt;event.pull&amp;lt;/code&amp;gt;). Thus, it would be unwise to call &amp;lt;code&amp;gt;os.sleep&amp;lt;/code&amp;gt; in your event registrant if you also expect the system to respond interactively with another foreground application, such as the shell.&lt;br /&gt;
&lt;br /&gt;
== Access Points For Automatic Program Execution ==&lt;br /&gt;
&lt;br /&gt;
The following access points are like &amp;quot;hooks&amp;quot;, or script locations, where you can start your background or foreground application during machine startup.&lt;br /&gt;
&lt;br /&gt;
=== Interactive Shell Startup (.shrc) ===&lt;br /&gt;
&lt;br /&gt;
The last boot process to load is the OpenOS shell. The shell blocks until a tty output is available. This means that if there is no gpu or no screen, the shell startup will wait.&lt;br /&gt;
&lt;br /&gt;
After a stdout for tty becomes available, the shell will finish loading and will execute &amp;lt;code&amp;gt;/etc/profile.lua&amp;lt;/code&amp;gt; which loads aliases and sets environment variables. The last thing &amp;lt;code&amp;gt;/etc/profile.lua&amp;lt;/code&amp;gt; will do is [https://ss64.com/bash/source.html source] your &amp;lt;code&amp;gt;/home/.shrc&amp;lt;/code&amp;gt; file, which by default is an empty file. &amp;lt;code&amp;gt;source&amp;lt;/code&amp;gt; does not run lua code, but instead runs each line in the file as a shell command. If you have a script you want to run when the shell loads, put the path to your script in your &amp;lt;code&amp;gt;.shrc&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;.shrc&amp;lt;/code&amp;gt; is run each time the shell is loaded, which may be more than once per boot. The user could type &#039;exit&#039;, or ^d, or even send a hard interrupt signal and kill the shell (and the init process will load a new one).&lt;br /&gt;
&lt;br /&gt;
I recommend editing &amp;lt;code&amp;gt;/home/.shrc&amp;lt;/code&amp;gt; rather than &amp;lt;code&amp;gt;/etc/profile.lua&amp;lt;/code&amp;gt; purely for organizational purposes.&lt;br /&gt;
&lt;br /&gt;
=== Runscripts (rc) ===&lt;br /&gt;
&lt;br /&gt;
Review the [[API/rc|rc]] documentation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/bin/rc&amp;lt;/code&amp;gt; can be used to enable boot level scripts. RC scripts are started even on systems with no shell, no gpu, no screen, no keyboard.&lt;br /&gt;
&lt;br /&gt;
=== Filesystem Autorun (autorun.lua) ===&lt;br /&gt;
&lt;br /&gt;
Relative to the root of any filesystem, you can create a file named &amp;lt;code&amp;gt;autorun.lua&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;.autorun.lua&amp;lt;/code&amp;gt;). When that filesystem component is first detected OpenOS will automatically run the file. Note that &amp;lt;code&amp;gt;/home/autorun.lua&amp;lt;/code&amp;gt; is not at the root of &#039;&#039;rootfs&#039;&#039;. This also applies to the &#039;&#039;rootfs&#039;&#039;. This autorun will execute each and every time the filesystem component is added to the system (e.g. you can remove and re-insert a floppy disk with an autorun).&lt;br /&gt;
&lt;br /&gt;
The feature is enabled by default, and can be disabled on a rw filesystem by either calling &amp;lt;code&amp;gt;filesystem.setAutorunEnabled(false)&amp;lt;/code&amp;gt;, or by modifying /etc/filesystem.cfg directly: &amp;lt;code&amp;gt;autorun=false&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Boot Scripts (/boot/) ===&lt;br /&gt;
&lt;br /&gt;
This option is really a non-option, documented here to disuade users with reasonable arguments against doing so.&lt;br /&gt;
&lt;br /&gt;
OpenOS runs boot scripts (sorted by their filenames) in &amp;lt;code&amp;gt;/boot/&amp;lt;/code&amp;gt; for its core operations. While it is possible to install custom boot scripts along side the kernel boot scripts, it is quite unadvisable to do so.&lt;br /&gt;
&lt;br /&gt;
Installing a custom boot script (in &amp;lt;code&amp;gt;/boot/&amp;lt;/code&amp;gt;) poses the risk that your boot script may be run before core libraries are available. There is no guarantee that even invoking &amp;lt;code&amp;gt;require&amp;lt;/code&amp;gt; in a boot script is safe in the current version OpenOS, or will be safe in future OpenOS updates (as I may change the boot order).&lt;br /&gt;
&lt;br /&gt;
There may not be a fully initialized io, there may be an incomplete init process, there may even be incomplete lua libraries. Depending on the code you execute in your boot script, you may even unintentionally circumvent the event dispatching system causing the system to miss component updates. Yes, there is a lot that the boot process is responsible for.&lt;br /&gt;
&lt;br /&gt;
With all of that said, here are a couple examples of &amp;lt;code&amp;gt;/boot&amp;lt;/code&amp;gt; scripts that would probably work now and for the foreseeable future. Prefix your script filename 99_ so that it loads at the end of the boot sequence. If anything doesn&#039;t work like you&#039;d expect (such as printing to stdout, or reading from stdin), it isn&#039;t a bug and isn&#039;t supported. In other words, use the &amp;lt;code&amp;gt;/boot/&amp;lt;/code&amp;gt; script directory at you own risk. If you need stdout, you can also wait for the &amp;lt;code&amp;gt;term_available&amp;lt;/code&amp;gt; signal. Again, this is not an officially supported option.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local event = require(&amp;quot;event&amp;quot;) -- the init signal is fired by the boot process, means the system is ready event.listen(&amp;quot;init&amp;quot;, function()&lt;br /&gt;
&lt;br /&gt;
local thread = require(&amp;quot;thread&amp;quot;)&lt;br /&gt;
thread.create(function()&lt;br /&gt;
  --[[&lt;br /&gt;
    your custom service code as a background thread&lt;br /&gt;
  ]]--&lt;br /&gt;
end):detach()&lt;br /&gt;
&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local event = require(&amp;quot;event&amp;quot;) event.listen(&amp;quot;component_added&amp;quot;, function(...)&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
  your custom service code as a background event responder&lt;br /&gt;
]]--&lt;br /&gt;
&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Contents ==&lt;br /&gt;
&lt;br /&gt;
{{:Tutorial/contents}}&lt;/div&gt;</summary>
		<author><name>Michiyo</name></author>
	</entry>
</feed>