Skip to content

Commit

Permalink
Sync hopper submodule
Browse files Browse the repository at this point in the history
  • Loading branch information
dacmot committed Sep 3, 2024
1 parent e8e36a4 commit 1518524
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 375 deletions.
22 changes: 0 additions & 22 deletions mods/tools/hopper/.luacheckrc

This file was deleted.

21 changes: 0 additions & 21 deletions mods/tools/hopper/CHANGELOG.md

This file was deleted.

76 changes: 31 additions & 45 deletions mods/tools/hopper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,42 @@

Based on jordan4ibanez's original hoppers mod, optimized by TenPlus1 and FaceDeer, with chutes and sorters by FaceDeer

Lucky Blocks: 2

Change log: [CHANGELOG.md](CHANGELOG.md)

### Functionality description

#### Hopper node

Hoppers are nodes that can transfer items to and from the inventories of adjacent nodes. The transfer direction depends on the orientation of the hopper.

The wide end of a hopper is its "input" end, if there is a compatible container (e.g. a chest), one item per second is taken into its own internal inventory. The hopper will also draw in dropped items.

The narrow end of the hopper is its "output" end. It can be either straight or 90° bent, relative to the input. It will attempt to inject items into a compatible container connected to it. On failure, the item is either held back in the hopper inventory or ejected, depending on its configuration.

#### Chute node
Hoppers are nodes that can transfer items to and from the inventories of adjacent nodes. The wide end of a hopper is its "input" end, if there's a chest or other compatible container it will take one item per second into its own internal inventory. It will also draw in items that have been dropped here. The narrow end of the hopper is its "output" end. It will attempt to inject items into a compatible container located at its output end. If there's no compatible container and the hopper's "eject" mode has been enabled it will instead dump the items out into the world.

The "chute" node acts as a pipe for items injected by a hopper or sorter. Use a screwdriver tool to achieve the desired rotation.
The location of a hopper relative to a furnace determines which inventory slots the hopper will affect. A hopper directly above a furnace will inject items into the furnace's input slot. A hopper to the furnace's side will inject items into the furnace's fuel slot. A hopper below the furnace will pull items out of the furnace's output slot. Hoppers cannot inject items into inappropriate slots; non-fuel items will not be placed into the furnace's fuel slot and non-cookable items will not be placed into the input slot.

#### Sorter node
Also included in this mod are a "chute" node and a "sorter" node. These allow for items to be routed over short distances and distributed in sophisticated ways.

Item stacks placed into the "filter" grid of the sorter node inventory define how to distribute the incoming items into the two outputs.
A chute simply moves items into adjacent nodes. You'll need to use a hopper or sorter to inject items into it to move along. A screwdriver is a useful tool for getting chutes aimed correctly.

* Matching items are sent into the direction of the large arrow (`V`).
* Other items are sent to the 90° side output.

Special case: The "filter all" option will cause the sorter to attempt to send *all* items in the direction of the large arrow. On failure, they're sent in the direction of the smaller arrow. Use-cases:

* Protection against overflowing containers
* Protection against unaccepted items in the destination slot(s), such as the furnace fuel slot

#### Built-in mod compatibility

The following nodes are supported out-of-the-box. "above"/"below"/"side" describe the location of the hopper.

* `default:furnace`
* Above: routed to the input slot
* Below: routed to the output slots
* Sides: routed to the fuel slot
* `default:chest(_locked)`, `protector:chest`
* All sides: routed to the main slot
* `wine:wine_barrel`
* Above: routed to the destination slots
* Below: routed to the source slot
* Sides: routed to the source slot
A sorter has two different outputs. Its inventory has a "filter" grid; place items in this grid to set the sorter's filter. Items that match the filter will be sent out the output with the large arrow and other items will be sent out the output with the smaller arrow. A "filter all" option will cause the sorter to attempt to send *all* items in the direction of the large arrow and then if that fails send them in the direction of the smaller arrow. This allows you to have an "overflow" storage should the sorter's primary target fill up, or when used in combination with a selective container (like the furnace's fuel slot, for example) it allows the target inventory to do the filtering for you.

### Advanced settings

This mod has several configurable settings. See settings menu or [settingtypes.txt](settingtypes.txt) for details.

* Hopper texture size: 16x16 pixels (default) or 32x32 pixels
* Single craftable item: output is straight or rotated by 90° to the side based on how you place it (default). When disabled, straight and bent hoppers must be crafted separately.
* Eject items button: option to remove the "eject items" button from hoppers
This mod has several configurable settings found in the advanced settings menu.

* Hopper texture size: can be set to 16 pixels (matching most standard Minetest node textures) or 32 pixels
* Single craftable item: When enabled (the default) hoppers are crafted as a single item and then select whether the output has a 90-degree turn to the side based on how you place it. When disabled you can craft straight and bent hoppers as separate items.
* Eject items button: this can be used to remove the "eject items" button from hoppers, if it is not desired.

### Change log

- 0.1 - Initial release from jordan4ibanez
- 0.2 - Fixed tool glitch (wear restored by accident)
- 0.3 - transfer function added
- 0.4 - Supports locked chest and protected chest
- 0.5 - Works with 0.4.13's new shift+click for newly placed Hoppers
- 0.6 - Remove formspec from hopper nodes to improve speed for servers
- 0.7 - Halved hopper capacity, can be dug by wooden pick
- 0.8 - Added Napiophelios' new textures and tweaked code
- 0.9 - Added support for Wine mod's wine barrels
- 1.0 - New furances do not work properly with hoppers so old reverted to abm furnaces
- 1.1 - Hoppers now work with new node timer Furnaces. Reduced Abm's and tidied code.
- 1.2 - Added simple API so that hoppers can work with other containers.
- 1.3 - Hoppers now call on_metadata_inventory_put and on_metadata_inventory_take, triggering furnace timers via their standard callbacks. Updated side hopper rotation handling to allow it to function in any orientation. Added settings options to use 16-pixel or 32-pixel textures. Added settings option to allow explicit crafting of standard/side hoppers or to allow crafting of a single item that selects which type to use on place. Added in-game documentation via optional "doc" mod dependency
- 1.4 - Added intllib support
- 1.5 - Added chutes
- 1.6 - Added "eject items" button to formspecs, "group" support to the API
- 1.7 - Added sorter block to allow for more sophisticated item transfer arrangements

Lucky Blocks: 2
38 changes: 19 additions & 19 deletions mods/tools/hopper/abms.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,32 @@ minetest.register_abm({
nodenames = {"hopper:hopper", "hopper:hopper_side"},
interval = 1.0,
chance = 1,
action = function(pos, _, _, active_object_count_wider)
action = function(pos, node, active_object_count, active_object_count_wider)
if active_object_count_wider == 0 then
return
end

local inv = minetest.get_meta(pos):get_inventory()
if not inv then
return
end
local posob

for _,object in pairs(minetest.get_objects_inside_radius(pos, 1)) do
local entity = not object:is_player() and object:get_luaentity()

if entity
and entity.name == "__builtin:item"
and inv:room_for_item("main", ItemStack(entity.itemstring)) then
if not object:is_player()
and object:get_luaentity()
and object:get_luaentity().name == "__builtin:item"
and inv
and inv:room_for_item("main",
ItemStack(object:get_luaentity().itemstring)) then

local posob = object:get_pos()
posob = object:getpos()

if math.abs(posob.x - pos.x) <= 0.5
and posob.y - pos.y <= 0.85
and posob.y - pos.y >= 0.3 then

inv:add_item("main", ItemStack(entity.itemstring))
inv:add_item("main",
ItemStack(object:get_luaentity().itemstring))

entity.itemstring = ""
object:get_luaentity().itemstring = ""
object:remove()
end
end
Expand Down Expand Up @@ -85,7 +85,7 @@ minetest.register_abm({
chance = 1,
catch_up = false,

action = function(pos, node, _, _)
action = function(pos, node, active_object_count, active_object_count_wider)
local source_pos, destination_pos, destination_dir
if node.name == "hopper:hopper_side" then
source_pos = vector.add(pos, directions[node.param2].src)
Expand All @@ -96,21 +96,21 @@ minetest.register_abm({
source_pos = vector.subtract(pos, destination_dir)
destination_pos = vector.add(pos, destination_dir)
end

local output_direction
if destination_dir.y == 0 then
output_direction = "horizontal"
end

local source_node = minetest.get_node(source_pos)
local destination_node = minetest.get_node(destination_pos)

local registered_source_inventories = hopper.get_registered(source_node.name)
local registered_source_inventories = hopper.get_registered_inventories_for(source_node.name)
if registered_source_inventories ~= nil then
hopper.take_item_from(pos, source_pos, source_node, registered_source_inventories["top"])
end

local registered_destination_inventories = hopper.get_registered(destination_node.name)
local registered_destination_inventories = hopper.get_registered_inventories_for(destination_node.name)
if registered_destination_inventories ~= nil then
if output_direction == "horizontal" then
hopper.send_item_to(pos, destination_pos, destination_node, registered_destination_inventories["side"])
Expand Down
133 changes: 43 additions & 90 deletions mods/tools/hopper/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,107 +2,60 @@ hopper.containers = {}
hopper.groups = {}
hopper.neighbors = {}

local function parse_group(target_node)
local number
local identifier

local equals_index = string.find(target_node, "=")
if equals_index ~= nil then
identifier = string.sub(target_node, 7, equals_index-1)
-- it's possible that the string was of the form "group:blah = 1", in which case we want to trim spaces off the end of the group identifier
local space_index = string.find(identifier, " ")
if space_index ~= nil then
identifier = string.sub(identifier, 1, space_index-1)
end
number = tonumber(string.sub(target_node, equals_index+1, -1))
else
identifier = string.sub(target_node, 7, -1)
number = "all" -- special value to indicate no number was provided
end

return identifier, number
end

local function is_already_in_neighbors(neighbor_node)
for _, value in pairs(hopper.neighbors) do
if value == neighbor_node then
return true
end
end
return false
end

-- global function to add new containers
function hopper:add_container(list)
for _, entry in pairs(list) do

local relative_position = entry[1]

local target_node = entry[2]

local inventory_info = entry
inventory_info.inventory_name = entry[3]
table.remove(inventory_info, 1)
table.remove(inventory_info, 1)
table.remove(inventory_info, 1)

local neighbor_node

if string.sub(target_node, 1, 6) == "group:" then
local group_identifier, group_number = parse_group(target_node)

if hopper.groups[group_identifier] == nil then
hopper.groups[group_identifier] = {}
local group_identifier, group_number
local equals_index = string.find(target_node, "=")
if equals_index ~= nil then
group_identifier = string.sub(target_node, 7, equals_index-1)
-- it's possible that the string was of the form "group:blah = 1", in which case we want to trim spaces off the end of the group identifier
local space_index = string.find(group_identifier, " ")
if space_index ~= nil then
group_identifier = string.sub(group_identifier, 1, space_index-1)
end
group_number = tonumber(string.sub(target_node, equals_index+1, -1))
else
group_identifier = string.sub(target_node, 7, -1)
group_number = "all" -- special value to indicate no number was provided
end
if hopper.groups[group_identifier][group_number] == nil then
hopper.groups[group_identifier][group_number] = {}

local group_info = hopper.groups[group_identifier]
if group_info == nil then
group_info = {}
end
if hopper.groups[group_identifier][group_number].extra == nil then
hopper.groups[group_identifier][group_number].extra = {}
if group_info[group_number] == nil then
group_info[group_number] = {}
end

hopper.groups[group_identifier][group_number][relative_position] = inventory_info
group_info[group_number][entry[1]] = entry[3]
hopper.groups[group_identifier] = group_info
neighbor_node = "group:"..group_identifier
-- result is a table of the form groups[group_identifier][group_number][relative_position][inventory_name]
else
if hopper.containers[target_node] == nil then
hopper.containers[target_node] = {}
end
if hopper.containers[target_node].extra == nil then
hopper.containers[target_node].extra = {}
local node_info = hopper.containers[target_node]
if node_info == nil then
node_info = {}
end

hopper.containers[target_node][relative_position] = inventory_info
node_info[entry[1]] = entry[3]
hopper.containers[target_node] = node_info
neighbor_node = target_node
-- result is a table of the form containers[target_node_name][relative_position][inventory_name]
end

if not is_already_in_neighbors(neighbor_node) then
table.insert(hopper.neighbors, neighbor_node)
end
end
end

-- global function for additional information about containers
function hopper:set_extra_container_info(list)
for _, entry in pairs(list) do
local target_node = entry[1]
table.remove(entry, 1) -- only extra information
if string.sub(target_node, 1, 6) == "group:" then
local group_identifier, group_number = parse_group(target_node)
if not is_already_in_neighbors("group:" .. group_identifier) then
minetest.log("error","An attempt to add extra information for " ..
target_node .. " in the absence of the main one")

local already_in_neighbors = false
for _, value in pairs(hopper.neighbors) do
if value == neighbor_node then
already_in_neighbors = true
break
end
hopper.groups[group_identifier][group_number].extra = entry
-- result is a table of the form:
-- groups[group_identifier][group_number]["extra"][list of extra information]
else
if not is_already_in_neighbors(target_node) then
minetest.log("error","An attempt to add extra information for " ..
target_node .. " in the absence of the main one")
break
end
hopper.containers[target_node].extra = entry
-- result is a table of the form:
-- containers[target_node_name]["extra"][list of extra information]
end
if not already_in_neighbors then
table.insert(hopper.neighbors, neighbor_node)
end
end
end
Expand All @@ -116,10 +69,10 @@ hopper:add_container({
{"bottom", "hopper:hopper", "main"},
{"side", "hopper:hopper", "main"},
{"side", "hopper:hopper_side", "main"},

{"bottom", "hopper:chute", "main"},
{"side", "hopper:chute", "main"},

{"bottom", "hopper:sorter", "main"},
{"side", "hopper:sorter", "main"},
})
Expand All @@ -129,15 +82,15 @@ if minetest.get_modpath("default") then
{"top", "default:chest", "main"},
{"bottom", "default:chest", "main"},
{"side", "default:chest", "main"},

{"top", "default:furnace", "dst"},
{"bottom", "default:furnace", "src"},
{"side", "default:furnace", "fuel"},

{"top", "default:furnace_active", "dst"},
{"bottom", "default:furnace_active", "src"},
{"side", "default:furnace_active", "fuel"},

{"top", "default:chest_locked", "main"},
{"bottom", "default:chest_locked", "main"},
{"side", "default:chest_locked", "main"},
Expand Down
Loading

0 comments on commit 1518524

Please sign in to comment.