parent
c16b4396ad
commit
bb2b4d2b0a
|
@ -54,7 +54,7 @@ local function keep_running(pos, elapsed)
|
|||
local mem = tubelib2.get_mem(pos)
|
||||
local crd = CRD(pos)
|
||||
pushing(pos, crd, M(pos), mem)
|
||||
return crd.State:is_active(mem)
|
||||
crd.State:is_active(mem)
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, node, clicker)
|
||||
|
|
|
@ -84,7 +84,7 @@ Syntax error, try help=Syntax Fehler, nutze help
|
|||
TA Ceiling Lamp=TA Deckenlampe
|
||||
TA Construction Board=TA Konstruktionsplan
|
||||
TA Electric Cable=TA Stromkabel
|
||||
TA Electric Junction Box=TA Verteilerbox
|
||||
TA Electric Junction Box=TA Verteilerdose
|
||||
TA Industrial Lamp 1=TA Industrielampe 1
|
||||
TA Industrial Lamp 2=TA Industrielampe 2
|
||||
TA Industrial Lamp 3=TA Industrielampe 3
|
||||
|
|
|
@ -0,0 +1,490 @@
|
|||
# Techage APIs und Design
|
||||
|
||||
*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.*
|
||||
|
||||
## History
|
||||
|
||||
- v1.0 - 03.10.2019 - Erster Entwurf
|
||||
|
||||
## Hierarchiediagramm
|
||||
|
||||
```
|
||||
+-------------------------------------------------------------+
|
||||
| consumer |
|
||||
| (tubing/commands/states/formspec/power/connections/node) |
|
||||
+-------------------------------------------------------------+
|
||||
| | |
|
||||
V V V
|
||||
+-----------------+ +-----------------+ +-------------------+
|
||||
| command | | node_states | | power |
|
||||
|(tubing/commands)| |(states/formspec)| |(power,connections)|
|
||||
+-----------------+ +-----------------+ +-------------------+
|
||||
| | |
|
||||
V V V
|
||||
+-------------------------------------------------------------+
|
||||
| Tube/tubelib2 |
|
||||
| (tubes, mem, get_node_pos) |
|
||||
+-------------------------------------------------------------+
|
||||
```
|
||||
|
||||
## Klasse `Tube` (Mod tubelib2)
|
||||
|
||||
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
|
||||
local item = self.connCache[key][dir]
|
||||
return item.pos2, Turn180Deg[item.dir2]
|
||||
end
|
||||
local fpos,fdir = self:walk_tube_line(pos, dir)
|
||||
local spos = get_pos(fpos,fdir)
|
||||
self:add_to_cache(pos, dir, spos, Turn180Deg[fdir])
|
||||
self:add_to_cache(spos, Turn180Deg[fdir], pos, dir)
|
||||
return spos, fdir
|
||||
end
|
||||
|
||||
-- Check if node at given position is a tubelib2 compatible node,
|
||||
-- able to receive and/or deliver items.
|
||||
-- If dir == nil then node_pos = pos
|
||||
-- Function returns the result (true/false), new pos, and the node
|
||||
function Tube:compatible_node(pos, dir)
|
||||
local npos = vector.add(pos, Dir6dToVector[dir or 0])
|
||||
local node = self:get_node_lvm(npos)
|
||||
return self.secondary_node_names[node.name], npos, node
|
||||
end
|
||||
```
|
||||
|
||||
Um mit `tubelib2` arbeiten zu können, muss zuvor eine Tube Instanz angelegt werden:
|
||||
|
||||
```lua
|
||||
local Tube = tubelib2.Tube:new(...)
|
||||
```
|
||||
|
||||
wird eine Instanz von tubes/pipes/cables angelegt. Hier die Parameter:
|
||||
|
||||
```lua
|
||||
dirs_to_check = attr.dirs_to_check or {1,2,3,4,5,6},
|
||||
max_tube_length = attr.max_tube_length or 1000,
|
||||
primary_node_names = Tbl(attr.primary_node_names or {}),
|
||||
secondary_node_names = Tbl(attr.secondary_node_names or {}),
|
||||
show_infotext = attr.show_infotext or false,
|
||||
force_to_use_tubes = attr.force_to_use_tubes or false, -- Block an Block oder Tubes dazw.
|
||||
clbk_after_place_tube = attr.after_place_tube, -- hiermit wird die Tube ausgetauscht (1)
|
||||
tube_type = attr.tube_type or "unknown", -- hier einen eindeutigen Namen für die Instanz
|
||||
```
|
||||
|
||||
zu (1): Bei einfachen Tubes reicht hier:
|
||||
|
||||
```lua
|
||||
minetest.swap_node(pos, {name = "tubelib2:tube"..tube_type, param2 = param2})
|
||||
```
|
||||
|
||||
tube_type bei "swap_node" ist "S" oder "A" (straight or angled)
|
||||
|
||||
### Registrierung
|
||||
|
||||
Alle Blöcke mit Tube-Support müssen bei `tubelib2` registriert werden über:
|
||||
|
||||
```lua
|
||||
Tube:add_secondary_node_names({names})
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
#### Änderungen an den Nodes
|
||||
|
||||
Damit die Tubes und die gegenüber angeschlossenen Blöcke über Änderungen informiert werden, existieren 2 Funktionen:
|
||||
|
||||
```lua
|
||||
after_place_node = function(pos, placer)
|
||||
Tube:after_place_node(pos [, {tube_dir}])
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
Tube:after_dig_node(pos [, {tube_dir}])
|
||||
end,
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
1. Knoten-spezifische callback Funktionen
|
||||
2. Zentrale callback Funktionen
|
||||
|
||||
##### 1. Knoten-spezifische callback Funktion `tubelib2_on_update`
|
||||
|
||||
```
|
||||
tubelib2_on_update(node, pos, out_dir, peer_pos, peer_in_dir)
|
||||
```
|
||||
|
||||
Die Funktion muss Teil von `minetest.register_node()` sein.
|
||||
|
||||
##### 2. Zentrale callback Funktion `register_on_tube_update`
|
||||
|
||||
```lua
|
||||
Tube:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir)
|
||||
...
|
||||
end)
|
||||
```
|
||||
|
||||
Wird 1) aufgerufen, wird 2) **nicht** mehr gerufen!
|
||||
|
||||
### Dir vs. Side
|
||||
|
||||
`tubelib2` arbeitet nur mit dirs (siehe oben). Oft ist aber die Arbeitsweise mit `sides` einfacher.
|
||||
|
||||
Techage defiiert `sides` , die wie folgt definiert sind `{B=1, R=2, F=3, L=4, D=5, U=6}`:
|
||||
|
||||
sides: dirs:
|
||||
U
|
||||
| B
|
||||
| / 6
|
||||
+--|-----+ | 1
|
||||
/ o /| | /
|
||||
+--------+ | |/
|
||||
L <----| |o----> R 4 <-------+-------> 2
|
||||
| o | | /|
|
||||
| / | + / |
|
||||
| / |/ 3 |
|
||||
+-/------+ 5
|
||||
/ |
|
||||
F |
|
||||
D
|
||||
|
||||
`techage/command.lua` definiert hier:
|
||||
|
||||
|
||||
```lua
|
||||
techage.side_to_outdir(side, param2) -- "B/R/F/L/D/U", node.param2
|
||||
```
|
||||
Weitere API Funktionen von `command.lua`
|
||||
|
||||
## Techage `command`
|
||||
|
||||
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:
|
||||
|
||||
```lua
|
||||
techage.neighbour_pull_items(pos, out_dir, num)
|
||||
techage.neighbour_push_items(pos, out_dir, stack)
|
||||
techage.neighbour_unpull_items(pos, out_dir, stack)
|
||||
```
|
||||
|
||||
#### Nummern bezogene Kommando API
|
||||
|
||||
Kommunikation ohne Tubes, Addressierung nur über Knoten-Nummern
|
||||
|
||||
```lua
|
||||
techage.not_protected(number, placer_name, clicker_name) --> true/false
|
||||
techage.check_numbers(numbers, placer_name) --> true/false (for send_multi)
|
||||
techage.send_multi(src, numbers, topic, payload) --> to many nodes
|
||||
techage.send_single(src, number, topic, payload) --> to one node with response
|
||||
```
|
||||
|
||||
#### Positions bezogene Kommando API
|
||||
|
||||
Kommunikation mit Tubes oder mit direkten Nachbar-Knoten über pos/dir. Im Falle von Tubes muss bei `network` die Tube Instanz angegeben werden.
|
||||
|
||||
```lua
|
||||
techage.transfer(pos, outdir, topic, payload, network, nodenames)
|
||||
-- The destination node location is either:
|
||||
-- A) a destination position, specified by pos
|
||||
-- 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):
|
||||
|
||||
```lua
|
||||
-- after_place_node decorator
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
<Tube>:after_place_node(pos)
|
||||
return <node>.after_place_node(pos, placer, itemstack, pointed_thing)
|
||||
end,
|
||||
-- after_dig_node decorator
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
<Tube>:after_dig_node(pos)
|
||||
minetest.after(0.1, tubelib2.del_mem, pos) -- At latest...
|
||||
return <node>.after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
end,
|
||||
-- tubelib2 callback, called after any connection change
|
||||
after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir)
|
||||
mem.connections = ... -- aktualisieren/löschen
|
||||
-- To be called delayed, so that all network connections have been established
|
||||
minetest.after(0.2, network_changed, pos, mem)
|
||||
return <node>.after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir)
|
||||
end,
|
||||
|
||||
```
|
||||
|
||||
Und es erfolgt eine Registrierung bei Tube:
|
||||
|
||||
```
|
||||
<Tube>:add_secondary_node_names({name})
|
||||
```
|
||||
|
||||
**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.**
|
||||
|
||||
### `power`/`power2` API
|
||||
|
||||
```lua
|
||||
techage.power.side_to_dir(param2, side) --> outdir
|
||||
techage.power.side_to_outdir(pos, side) --> outdir
|
||||
techage.power.set_conn_dirs(pos, sides) --> store as meta "power_dirs"
|
||||
techage.get_pos(pos, side) --> new pos
|
||||
techage.power.after_rotate_node(pos, cable) -- update cables
|
||||
techage.power.percent(max_val, curr_val) --> percent value
|
||||
techage.power.formspec_power_bar(max_power, current_power) --> formspec string
|
||||
techage.power.power_cut(pos, dir, cable, cut) -- for switches
|
||||
|
||||
techage.power.network_changed(pos, mem) -- for each network change from any node
|
||||
|
||||
techage.power.generator_start(pos, mem, available) -- on start
|
||||
techage.power.generator_update(pos, mem, available) -- on any change of performance
|
||||
techage.power.generator_stop(pos, mem) -- on stop
|
||||
techage.power.generator_alive(pos, mem) -- every 2 s
|
||||
|
||||
techage.power.consumer_start(pos, mem, cycle_time, needed)
|
||||
techage.power.consumer_stop(pos, mem)
|
||||
techage.power.consumer_alive(pos, mem)
|
||||
techage.power.power_available(pos, mem, needed) -- lamp turn on function
|
||||
|
||||
techage.power.secondary_start(pos, mem, available, needed)
|
||||
techage.power.secondary_stop(pos, mem)
|
||||
techage.power.secondary_alive(pos, mem, capa_curr, capa_max)
|
||||
|
||||
techage.power.power_accounting(pos, mem) --> {network data...} (used by terminal)
|
||||
techage.power.get_power(start_pos) --> sum (used by solar cells)
|
||||
techage.power.power_network_available(start_pos) --> bool (used by TES generator)
|
||||
techage.power.mark_nodes(name, start_pos) -- used by debugging tool
|
||||
```
|
||||
|
||||
## Klasse `NodeStates`
|
||||
|
||||
`NodeStates` abstrahiert die Zustände einer Maschine:
|
||||
|
||||
```lua
|
||||
techage.RUNNING = 1 -- in normal operation/turned on
|
||||
techage.BLOCKED = 2 -- a pushing node is blocked due to a full destination inventory
|
||||
techage.STANDBY = 3 -- nothing to do (e.g. no input items), or node (world) not loaded
|
||||
techage.NOPOWER = 4 -- only for power consuming nodes, no operation
|
||||
techage.FAULT = 5 -- any fault state (e.g. wrong source items)
|
||||
techage.STOPPED = 6 -- not operational/turned off
|
||||
```
|
||||
|
||||
Dazu muss eine Instanz von `NodeStates` angelegt werden:
|
||||
|
||||
```lua
|
||||
State = techage.NodeStates:new({
|
||||
node_name_passive = "mymod:name_pas",
|
||||
node_name_active = "mymod:name_act",
|
||||
infotext_name = "MyBlock",
|
||||
cycle_time = 2,
|
||||
standby_ticks = 6,
|
||||
formspec_func = func(self, pos, mem), --> string
|
||||
on_state_change = func(pos, old_state, new_state),
|
||||
can_start = func(pos, mem, state) --> true or false/<error string>
|
||||
has_power = func(pos, mem, state), --> true/false (for consumer)
|
||||
start_node = func(pos, mem, state),
|
||||
stop_node = func(pos, mem, state),
|
||||
})
|
||||
```
|
||||
|
||||
**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.
|
||||
|
||||
```lua
|
||||
techage.register_consumer("autocrafter", S("Autocrafter"), tiles, {
|
||||
drawtype = "normal",
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing -- anstatt 'techage.register_node'
|
||||
after_place_node = func(pos, placer), -- knotenspezifischer Teil
|
||||
can_dig = fubnc(pos, player), -- knotenspezifischer Teil
|
||||
node_timer = func(pos, elapsed), -- knotenspezifischer Teil
|
||||
on_receive_fields = func(pos, formname, fields, player), -- knotenspez. Teil
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,2,4}, -- Verarbeitungsleistung in items/cycle
|
||||
power_consumption = {0,4,6,9}, -- Stromverbrauch (optional)
|
||||
}) --> node_name_ta2, node_name_ta3, node_name_ta4
|
||||
```
|
||||
|
||||
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
|
||||
- `has_power` / `start_node` / `stop_node` / `on_power` / `on_nopower`
|
||||
- Unterstützung Achsenantrieb (TA2) oder Strom (TA3+)
|
||||
- Strom/Achsen von vorne oder hinten (alles andere muss selbst definiert werden)
|
||||
|
||||
Ein einfaches Beispiele dafür wäre: `pusher.lua`
|
||||
|
||||
**Es darf in `after_place_node` kein `tubelib2.init_mem(pos)` aufgerufen werden, sonst werden die Definitionen wieder zerstört!!!**
|
||||
|
||||
## Anhang
|
||||
|
||||
### Unschönheiten
|
||||
|
||||
#### Problem: Verbindungen zu zwei Netzwerken
|
||||
|
||||
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 insgesammt 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.
|
||||
|
||||
#### Lösung
|
||||
|
||||
|
||||
|
||||
### ToDo
|
||||
|
||||
- tubelib2.mem beschreiben
|
||||
-
|
||||
|
||||
### Neue Ideen
|
||||
|
||||
Aktuell triggert jeder Generator irgendwann selbst das Netzwerk. Besser wäre es, es gäbe eine globale Timer Funktion, bei der sich alle Generatoren registrieren, die dann alle nacheinander aufruft.
|
||||
|
||||
Damit könnte die Berechnung über die 2 Phasen entfallen, den jeder Generator verteilt seinen Teil an alle Verbraucher, die wo noch nichts haben.
|
||||
|
|
@ -46,7 +46,7 @@ Die Turbine ist Teil des Kraftwerk. Sie muss neben den Generator gesetzt und üb
|
|||
|
||||
### TA3 Generator
|
||||
|
||||
Der Generator dient zur Stromerzeugung. Er muss über Stromkabel und Verteilerboxen mit den Maschinen verbunden werden.
|
||||
Der Generator dient zur Stromerzeugung. Er muss über Stromkabel und Verteilerdosen mit den Maschinen verbunden werden.
|
||||
|
||||
[ta3_generator|image]
|
||||
|
||||
|
@ -66,7 +66,7 @@ Tech Age besitzt 2 Arten von Stromkabel:
|
|||
- Isolierte Kabel (TA Stromkabel) für die lokale Verkabelung im Boden oder in Gebäuden. Diese Kabel lassen sich in der Wand oder im Boden verstecken (können mit der Kelle "verputzt" werden).
|
||||
- Überlandleitungen (TA Stromleitung) für Freiluftverkabelung über große Strecken. Diese Kabel sind geschützt, können also von anderen Spielern nicht entfernt werden.
|
||||
|
||||
Mehrere Verbraucher und Generatoren können in einem Stromnetzwerk zusammen betrieben werden. Mit Hilfe der Verteilerboxen können so große Netzwerke aufgebaut werden.
|
||||
Mehrere Verbraucher und Generatoren können in einem Stromnetzwerk zusammen betrieben werden. Mit Hilfe der Verteilerdosen können so große Netzwerke aufgebaut werden.
|
||||
Wird zu wenig Strom bereitgestellt, gehen Teile der Verbraucher aus, bzw. Lampen beginnen zu flackern.
|
||||
In diesem Zusammenhang ist auch wichtig, dass die Funktionsweise von Forceload Blöcken verstanden wurde, denn bspw. Generatoren liefern nur Strom, wenn der entsprechende Map-Block geladen ist. Dies kann mit einen Forceload Block erzwungen werden.
|
||||
|
||||
|
@ -76,7 +76,7 @@ In diesem Zusammenhang ist auch wichtig, dass die Funktionsweise von Forceload B
|
|||
### TA Stromkabel / Electric Cable
|
||||
|
||||
Für die lokale Verkabelung im Boden oder in Gebäuden.
|
||||
Abzweigungen können mit Hilfe von Verteilerboxen realisiert werden. Die maximale Kabellänge zwischen Maschinen oder Verteilerboxen beträgt 1000 m. Es können maximale 1000 Knoten in einem Strom-Netzwerk verbunden werden. Als Knoten zählen alle Generatoren, Akkus, Verteilerboxen und Maschinen.
|
||||
Abzweigungen können mit Hilfe von Verteilerdosen realisiert werden. Die maximale Kabellänge zwischen Maschinen oder Verteilerdosen beträgt 1000 m. Es können maximale 1000 Knoten in einem Strom-Netzwerk verbunden werden. Als Knoten zählen alle Generatoren, Akkus, Verteilerdosen und Maschinen.
|
||||
|
||||
Da die Stromkabel nicht automatisch geschützt sind, wird für längere Strecken die Überlandleitungen (TA Stromleitung) empfohlen.
|
||||
|
||||
|
@ -85,15 +85,15 @@ Stromkabel können mit der Kelle verputzt also in der Wand oder im Boden verstec
|
|||
Zum Verputzen muss mit der Kelle auf das Kabel geklickt werden. Das Material, mit dem das Kabel verputzt werden soll, muss sich im Spieler-Inventar ganz links befinden.
|
||||
Die Kabel können wieder sichtbar gemacht werden, indem man mit der Kelle wieder auf den Block klickt.
|
||||
|
||||
Außer Kabel können auch die TA Verteilerbox und die TA Stromschalterbox verputzt werden.
|
||||
Außer Kabel können auch die TA Verteilerdose und die TA Stromschalterbox verputzt werden.
|
||||
|
||||
[ta3_powercable|image]
|
||||
|
||||
|
||||
### TA Verteilerbox / Electric Junction Box
|
||||
### TA Verteilerdose / Electric Junction Box
|
||||
|
||||
Mit der Verteilerbox kann Strom in bis zu 6 Richtungen verteilt werden. Verteilerboxen können auch mit der Kelle verputzt (versteckt) und wieder sichtbar gemacht werden.
|
||||
Wird mit dem TechAge Info Werkzeug (Schraubenschlüssel) auf die Verteilerbox geklickt, wird angezeigt, wie viel Leistung die Generatoren liefern bzw. die Verbraucher im Netzwerk beziehen.
|
||||
Mit der Verteilerdose kann Strom in bis zu 6 Richtungen verteilt werden. Verteilerdosen können auch mit der Kelle verputzt (versteckt) und wieder sichtbar gemacht werden.
|
||||
Wird mit dem TechAge Info Werkzeug (Schraubenschlüssel) auf die Verteilerdose geklickt, wird angezeigt, wie viel Leistung die Generatoren liefern bzw. die Verbraucher im Netzwerk beziehen.
|
||||
|
||||
[ta3_powerjunction|image]
|
||||
|
||||
|
@ -169,7 +169,7 @@ Bei Volllast kann ein Akku 400 s lang Strom aufnehmen und wenn er voll ist, auch
|
|||
|
||||
### TA3 Strom Terminal / Power Terminal
|
||||
|
||||
Das Strom-Terminal muss von eine Verteilerbox platziert werden. Es zeigt Daten aus dem Stromnetz an wie:
|
||||
Das Strom-Terminal muss von eine Verteilerdose platziert werden. Es zeigt Daten aus dem Stromnetz an wie:
|
||||
- Leistung alle Generatoren
|
||||
- Leistung alles Akkus (Sekundärquellen)
|
||||
- Leistungsaufnahme aller Maschinen
|
||||
|
|
|
@ -155,9 +155,9 @@ minetest.register_node("techage:electric_cableA", {
|
|||
})
|
||||
|
||||
Cable:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir)
|
||||
if minetest.registered_nodes[node.name].after_tube_update then
|
||||
if minetest.registered_nodes[node.name].after_tube_update then -- primay/secondary nodes
|
||||
minetest.registered_nodes[node.name].after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir)
|
||||
else
|
||||
else -- all kind of nodes, used as cable filler/grout
|
||||
techage.power.after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir, Cable)
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -194,6 +194,14 @@ minetest.register_lbm({
|
|||
|
||||
-------------------------------------------------- Migrate
|
||||
|
||||
local function conection_color(t)
|
||||
local count = 0
|
||||
for _ in pairs(t) do count = count + 1 end
|
||||
if count == 0 then return count, "#FF0000" end
|
||||
if count == 1 then return count, "#FFFF00" end
|
||||
return count, "#00FF00"
|
||||
end
|
||||
|
||||
local function pos_already_reached(pos)
|
||||
local key = minetest.hash_node_position(pos)
|
||||
if not Route[key] and NumNodes < MAX_NUM_NODES then
|
||||
|
@ -470,6 +478,10 @@ function techage.power.generator_start(pos, mem, available)
|
|||
end
|
||||
end
|
||||
|
||||
function techage.power.generator_update(pos, mem, available)
|
||||
mem.pwr_available = available
|
||||
end
|
||||
|
||||
function techage.power.generator_stop(pos, mem)
|
||||
mem.pwr_node_alive_cnt = 0
|
||||
mem.pwr_available = 0
|
||||
|
@ -588,19 +600,28 @@ end
|
|||
|
||||
--
|
||||
-- Read the current power value from all connected devices (used for solar cells)
|
||||
--
|
||||
function techage.power.get_power(start_pos)
|
||||
-- Only used by the solar inverter to collect the power of all solar cells.
|
||||
-- Only one inverter per network is allowed. Therefore, we have to check,
|
||||
-- if additional inverters are in the network.
|
||||
-- Function returns in addition the number of found inverters.
|
||||
function techage.power.get_power(start_pos, inverter)
|
||||
Route = {}
|
||||
NumNodes = 0
|
||||
pos_already_reached(start_pos)
|
||||
local sum = 0
|
||||
local num_inverter = 0
|
||||
connection_walk(start_pos, function(pos, mem)
|
||||
local pwr = PWR(pos)
|
||||
if pwr and pwr.on_getpower then
|
||||
sum = sum + pwr.on_getpower(pos, mem)
|
||||
else
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == inverter then
|
||||
num_inverter = num_inverter + 1
|
||||
end
|
||||
end
|
||||
end)
|
||||
return sum
|
||||
return sum, num_inverter
|
||||
end
|
||||
|
||||
function techage.power.power_network_available(start_pos)
|
||||
|
@ -620,6 +641,7 @@ function techage.power.mark_nodes(name, start_pos)
|
|||
pos_already_reached(start_pos)
|
||||
techage.unmark_position(name)
|
||||
connection_walk2(start_pos, 3, 100, function(pos, mem, max_hops)
|
||||
techage.mark_position(name, pos, S(pos).." : "..(4 - max_hops), "#60FF60")
|
||||
local num, color = conection_color(mem.connections or {})
|
||||
techage.mark_position(name, pos, S(pos).." : "..num, color)
|
||||
end)
|
||||
end
|
||||
|
|
|
@ -125,7 +125,7 @@ function techage.power.register_node(names, pwr_def)
|
|||
return pwr.after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
end
|
||||
end,
|
||||
-- tubelib2 callback, called after any connection change
|
||||
-- tubelib2->Cable:register_on_tube_update callback, called after any connection change
|
||||
after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir)
|
||||
local pwr = PWR(pos)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
|
@ -149,6 +149,8 @@ function techage.power.register_node(names, pwr_def)
|
|||
end
|
||||
end
|
||||
|
||||
-- Called from tubelib2 via Cable:register_on_tube_update()
|
||||
-- For all kind of nodes, used as cable filler/grout
|
||||
function techage.power.after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir, power)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.connections = mem.connections or {}
|
||||
|
@ -166,6 +168,22 @@ function techage.power.after_tube_update(node, pos, out_dir, peer_pos, peer_in_d
|
|||
end
|
||||
end
|
||||
|
||||
-- Used for "Ad hoc" networking (nodes with support for two different network types)
|
||||
function techage.power.add_connection(pos, out_dir, network, add)
|
||||
local peer_pos, peer_in_dir = network:get_connected_node_pos(pos, out_dir)
|
||||
if peer_pos then
|
||||
local in_dir = tubelib2.Turn180Deg[out_dir]
|
||||
local peer_out_dir = tubelib2.Turn180Deg[peer_in_dir]
|
||||
local mem = tubelib2.get_mem(peer_pos)
|
||||
mem.connections = mem.connections or {}
|
||||
if add then
|
||||
mem.connections[peer_out_dir] = {pos = pos, in_dir = in_dir}
|
||||
else
|
||||
mem.connections[peer_out_dir] = nil -- del connection
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function techage.power.percent(max_val, curr_val)
|
||||
return math.min(math.ceil(((curr_val or 0) * 100.0) / (max_val or 1.0)), 100)
|
||||
|
|
|
@ -203,7 +203,7 @@ minetest.register_node("techage:power_pole", {
|
|||
})
|
||||
|
||||
minetest.register_node("techage:power_pole_conn", {
|
||||
description = "TA Power Pole Top 4 conn",
|
||||
description = "TA Power Pole Top (for up to 6 connections)",
|
||||
tiles = {
|
||||
"default_wood.png^techage_power_pole_top.png",
|
||||
"default_wood.png^techage_power_pole_top.png",
|
||||
|
|
|
@ -28,7 +28,7 @@ local function powerpole_found(pos, name, range)
|
|||
local pos2 = {x=pos.x+range, y=pos.y+range, z=pos.z+range}
|
||||
for _,npos in ipairs(minetest.find_nodes_in_area(pos1, pos2, {
|
||||
"techage:power_pole", "techage:power_pole_conn",
|
||||
"techage:power_pole2", "techage:power_pole3"})) do
|
||||
"techage:power_pole2"})) do
|
||||
if minetest.get_meta(npos):get_string("owner") ~= name then
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -26,37 +26,40 @@ local CYCLE_TIME = 2
|
|||
local PWR_PERF = 120
|
||||
|
||||
local function determine_power(pos, mem)
|
||||
-- We have to use get_connected_node_pos, because the inverter has already
|
||||
-- a AC power connection. An additional DC power connection is not possibe,
|
||||
-- so we have to start the connection_walk on the next node.
|
||||
local dir = M(pos):get_int("left_dir")
|
||||
local pos1 = Solar:get_connected_node_pos(pos, dir)
|
||||
local max_power, num_inverter = power.get_power(pos1, "techage:ta4_solar_inverter")
|
||||
if num_inverter == 1 then
|
||||
mem.max_power = math.min(PWR_PERF, max_power)
|
||||
else
|
||||
mem.max_power = 0
|
||||
end
|
||||
return max_power, num_inverter
|
||||
end
|
||||
|
||||
local function determine_power_from_time_to_time(pos, mem)
|
||||
local time = minetest.get_timeofday() or 0
|
||||
if time < 6.00/24.00 or time > 18.00/24.00 then
|
||||
mem.ticks = 0
|
||||
mem.max_power = 0
|
||||
power.generator_update(pos, mem, mem.max_power)
|
||||
return
|
||||
end
|
||||
mem.ticks = mem.ticks or 0
|
||||
if (mem.ticks % 20) == 0 then -- calculate max_power not to often
|
||||
local dir = M(pos):get_int("left_dir")
|
||||
-- We have to use get_connected_node_pos, because the inverter has already
|
||||
-- a AC power connection. An additional DC power connection is not possibe,
|
||||
-- so we have to start the connection_walk on the next node.
|
||||
local pos1 = Solar:get_connected_node_pos(pos, dir)
|
||||
mem.max_power = math.min(PWR_PERF, power.get_power(pos1))
|
||||
if (mem.ticks % 10) == 0 then -- calculate max_power not to often
|
||||
determine_power(pos, mem)
|
||||
power.generator_update(pos, mem, mem.max_power)
|
||||
else
|
||||
mem.max_power = mem.max_power or 0
|
||||
end
|
||||
mem.ticks = mem.ticks + 1
|
||||
end
|
||||
|
||||
local function determine_power_now(pos, mem)
|
||||
local dir = M(pos):get_int("left_dir")
|
||||
-- We have to use get_connected_node_pos, because the inverter has already
|
||||
-- a AC power connection. An additional DC power connection is not possibe,
|
||||
-- so we have to start the connection_walk on the next node.
|
||||
local pos1 = Solar:get_connected_node_pos(pos, dir)
|
||||
mem.max_power = math.min(PWR_PERF, power.get_power(pos1))
|
||||
end
|
||||
|
||||
local function formspec(self, pos, mem)
|
||||
determine_power_now(pos, mem)
|
||||
determine_power(pos, mem)
|
||||
local max_power = mem.max_power or 0
|
||||
local delivered = mem.delivered or 0
|
||||
local bar_in = techage.power.formspec_power_bar(max_power, max_power)
|
||||
|
@ -76,8 +79,10 @@ local function formspec(self, pos, mem)
|
|||
end
|
||||
|
||||
local function can_start(pos, mem, state)
|
||||
determine_power(pos, mem)
|
||||
return mem.max_power > 0 or "no solar power"
|
||||
local max_power, num_inverter = determine_power(pos, mem)
|
||||
if num_inverter > 1 then return "solar network error" end
|
||||
if max_power == 0 then return "no solar power" end
|
||||
return true
|
||||
end
|
||||
|
||||
local function start_node(pos, mem, state)
|
||||
|
@ -152,16 +157,29 @@ minetest.register_node("techage:ta4_solar_inverter", {
|
|||
|
||||
after_place_node = function(pos, placer)
|
||||
local mem = tubelib2.init_mem(pos)
|
||||
local number = techage.add_node(pos, "techage:ta4_solar_inverter")
|
||||
mem.running = false
|
||||
mem.delivered = 0
|
||||
local number = techage.add_node(pos, "techage:ta4_solar_inverter")
|
||||
State:node_init(pos, mem, number)
|
||||
local meta = M(pos)
|
||||
-- Solar/low power cable direction
|
||||
meta:set_int("left_dir", techage.power.side_to_outdir(pos, "L"))
|
||||
meta:set_string("formspec", formspec(State, pos, mem))
|
||||
-- Solar/low power cable direction
|
||||
local outdir = techage.power.side_to_outdir(pos, "L")
|
||||
meta:set_int("left_dir", outdir)
|
||||
techage.power.add_connection(pos, outdir, Solar, true)
|
||||
Solar:after_place_node(pos)
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
local outdir = M(pos):get_int("left_dir")
|
||||
techage.power.add_connection(pos, outdir, Solar, false)
|
||||
Solar:after_dig_node(pos)
|
||||
end,
|
||||
|
||||
after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir, power)
|
||||
techage.power.add_connection(pos, out_dir, Solar, true)
|
||||
end,
|
||||
|
||||
on_receive_fields = on_receive_fields,
|
||||
on_rightclick = on_rightclick,
|
||||
on_timer = node_timer,
|
||||
|
|
Loading…
Reference in New Issue