*Hinweis: Dieses Dokument folgt dem markdown Standard und ist mit Typora erstellt. Damit hat man links das Inhaltsverzeichnis zur Übersicht und zum Navigieren. Zur Not geht aber jeder Editor.*
Da Techage auf tubelib2 aufsetzt, soll auch diese Mod hier soweit behandelt werden, wie notwendig.
`tubelib2` dient zur Verknüpfung von Blöcken über tubes/pipes/cables. Tubes sind dabei "primary nodes", die Blöcke "secundary nodes". Die Features dabei sind:
- platzieren von Tubes, so dass diese mit benachbarten Tubes oder registrierten Blöcken eine Verbindung eingehen
- Event-Handling, so dass registrierte Blöcke über Änderungen an den Tube-Verbindungen informiert werden
- API-Funktionen, um die Position des Blockes gegenüber (peer node) zu bestimmen
```lua
-- From source node to destination node via tubes.
-- pos is the source node position, dir the output dir
-- The returned pos is the destination position, dir
-- is the direction into the destination node.
function Tube:get_connected_node_pos(pos, dir)
local key = S(pos)
if self.connCache[key] and self.connCache[key][dir] then
Diese müssen in jedem Fall aufgerufen werden, sonst werden die Daten der benachbarten Tubes nicht aktualisiert. Der Parameter `tube_dir` ist optional, macht aber Sinn, so dass nicht alle 6 Seiten geprüft werden müssen.
#### Änderungen an Tubes/anderen Nodes
Damit der Block über Änderungen an Tubes oder Peer-Blöcken informiert wird, gibt es zwei Möglichkeiten:
In Ergänzung zu `tubelib2` sind in `command` Funktionen für den Austausch von Items von Inventar zu Inventar (Tubing) und Kommandos für Datenaustausch definiert.
Zusätzlich etabliert `command` das Knoten-Nummern-System für die Addressierung bei Kommandos.
Dazu muss jeder Knoten bei `command` an- und abgemeldet werden:
```lua
techage.add_node(pos, name) --> number
techage.remove_node(pos)
```
Soll der Knoten Kommandos empfangen und/oder Items austauschen können, ist folgende Registrierung notwendig (alle Funktionen sind optional):
```lua
techage.register_node(names, {
on_pull_item = func(pos, in_dir, num),
on_push_item = func(pos, in_dir, item),
on_unpull_item = func(pos, in_dir, item),
on_recv_message = func(pos, src, topic, payload),
on_node_load = func(pos), -- LBM function
on_transfer = func(pos, in_dir, topic, payload),
})
```
#### Client API
Bspw. der Pusher als Client nutzt:
```lua
techage.pull_items(pos, out_dir, num)
techage.push_items(pos, out_dir, stack)
techage.unpull_items(pos, out_dir, stack)
```
#### Server API
Für den Server (chest mit Inventar) existieren dazu folgende Funktionen:
```lua
techage.get_items(inv, listname, num)
techage.put_items(inv, listname, stack)
techage.get_inv_state(inv, listname)
```
#### Hopper API
Es gibt bspw. mit dem Hopper aber auch einen Block, der nicht über Tubes sondern nur mit direkten Nachbarn Items austauschen soll. Dazu dient dieser Satz an Funktionen:
-- B) a neighbor position, specified by caller pos/outdir, or pos/side
-- C) a tubelib2 network connection, specified by caller pos/outdir, or pos/side
-- outdir is one of: 1..6 or alternative a 'side'
-- side is one of: "B", "R", "F", "L", "D", "U"
-- network is a tuebelib2 network instance
-- opt: nodenames is a table of valid callee node names
```
#### Sonstige API
```lua
techage.side_to_indir(side, param2) --> indir
techage.get_node_info(dest_num) --> { pos, name }
techage.get_node_number(pos) --> number
techage.get_new_number(pos, name) --> should ne be needed (repair function)
```
## Wrapper `power`
Im Gegensatz zu `tubelib2` und `command` verwaltet `power` ganze Netzwerke und nicht nur Einzelverbindungen zwischen zwei Knoten. Dazu muss `power` in jedem Knoten eine Connection-Liste anlegen, die alle angeschlossenen Tubes beinhaltet.
Nur so können mit der internen Funktion `connection_walk` alle Knoten im Netzwerk erreicht werden.
`power` besitzt die Funktion:
```lua
techage.power.register_node(names, {
conn_sides = {"L", "R", "U", "D", "F", "B"},
on_power = func(pos, mem), -- für Verbraucher (einschalten)
on_nopower = func(pos, mem), -- für Verbraucher (ausschalten)
on_getpower = func(pos, mem), -- für Solarzellen (Strom einsammeln)
power_network = Tube, -- tubelib2 Instanz
})
```
Durch die Registrierung des Nodes werden auch die folgenden Funktionen überschrieben bzw. erhalten einen Wrapper (Code nur symbolhaft):
**Damit ist es nicht mehr notwendig, die `tubelib2` callback Funktionen `after_place_node` und `after_dig_node` sowie `after_tube_update` selbst zu codieren.**
**Soll aber der Knoten außer Power auch Kommandos empfangen oder senden können, oder am Tubing teilnehmen, so müssen die `command` bezogenen Funktionen zusätzlich beachtet werden.**
**Wird `NodeStates` verwendet, muss der Knoten die definierten Zustände unterstützen und sollte die formspec mit dem Button und die callbacks `can_start`, `start_node` und `stop_node` implementieren.**
### Methods
```lua
node_init(pos, mem, number) -- to be called once
stop(pos, mem)
start(pos, mem)
start_from_timer(pos, mem) -- to be used from node timer functions
standby(pos, mem)
blocked(pos, mem)
nopower(pos, mem)
fault(pos, mem, err_string) -- err_string is optional
get_state(mem) --> state
is_active(mem)
start_if_standby(pos) -- used from allow_metadata_inventory functions
idle(pos, mem) -- To be called if node is idle
keep_running(pos, mem, val, num_items) -- to keep the node in state RUNNING
state_button_event(pos, mem, fields) -- called from on_receive_fields
get_state_button_image -- see techage.state_button()
on_receive_message(pos, topic, payload) -- for command interface
on_node_load(pos, not_start_timer) -- LBM actions
```
### Helper API
```lua
techage.state_button(state) --> button layout for formspec
techage.get_power_image(pos, mem) --> power symbol for formspec
techage.is_operational(mem) -- true if node_timer should be executed
techage.needs_power(mem) --> true/false state dependent
techage.needs_power2(state) --> true/false state dependent
techage.get_state_string(mem) --> "running"
NodeStates:node_init(pos, mem, number)
```
## Wrapper `consumer`
Wie auch `power` bietet `consumer` einen Registrierungs-Wrapper, der dem Knoten einige Eigenschaften und Funktionen hinzufügt.
Diese `register_consumer` Funktion deckt alles generische ab, was ein Knoten bzgl. Power, Tubing, Kommandos (Status, on/off), formspec, swap_node(act/pas) benötigt, damit auch node_states, tubelib2.
Dabei werden auch bereits definiert:
-`push` und `pull` Richtung für das Tubing (links/rechts)
- Umschalten des Knotens zwischen aktiv und passiv
Es ist nicht möglich, einen Knoten in zwei unterschiedlichen Netzwerken (bspw. Strom, Dampf) über `techage.power.register_node()` anzumelden. `power` würde zweimal übereinander die gleichen Knoten-internen Variablen wie `mem.connections` im Knoten anlegen und nutzen. Das geht und muss schief gehen. Aktuell ist es zusätzlich so, dass sich Lua in einer Endlosschleife aufhängt. Also insgesamt keine gute Idee.
Ein Lösungsansatz wäre, den Datensatz in mem, node und meta eine Indirektion tiefer unter dem Netzwerknamen abzuspeichern. Dies hat aber Auswirkungen auf die Performance.
Der **Inverter** hat mit Power und Solar zwei Netzwerkanschlüsse. Da dies aber aktuell (03.10.2019) nicht geht, ist nur Power normal registiert, Solar wird nur "krückenhaft" angesteuert. Damit taucht der Inverter aber nicht im Solar-Netzwerk auf. Dies ermöglicht es, mehrere Inverter in ein Solar-Netzwerk zu hängen und jeder liefert die volle Leistung.
Weiteres Problem: Die Funktion `matching_nodes()` (power2.lua) prüft, ob beide Knoten beim `after_tube_update` den gleichen Netzwerktyp haben. Der Inverter liefert hier "power", müsste aber "solar" liefern. Dadurch wird der Verbindungsaufbau von der Junctionbox abgelehnt.
- power2 hat eine Funktion `techage.power.add_connection()` um vom Inverter aus beim nächsten angeschlossenen Knoten den Inverter in der connection Liste einzutragen.
- Der Inverter gibt jetzt bei `techage.power.get_power()` seinen eigenen Namen an. `techage.power.get_power()` liefert die Anzahl der gefundenen Inverter zurück. Sind es mehr als einer, gibt es eine Fehlermeldung.
- Der Inverter hat auch noch eine `after_tube_update` bekommen, um auch von hier die `techage.power.add_connection()`aufrufen zu können.