Compare commits

...

10 Commits

Author SHA1 Message Date
d38bb9b9dc 上传文件至 / 2025-12-05 02:23:27 +08:00
402f543295 上传文件至 / 2025-12-05 02:02:00 +08:00
HKXluo
7fa0a96b3d 修复 #1 bug后将临时调试日志添加英文版本 2025-10-25 23:55:46 +08:00
HKXluo
95670693d5 修复#1的bug 2025-10-25 23:49:14 +08:00
HKXluo
d5fa6c94fc Merge branch 'main' of https://git.liulikeji.cn/xingluo/CCTweaked-utils 2025-10-24 03:02:19 +08:00
HKXluo
43ca190993 添加项目仓库跳转和程序下载 2025-10-24 03:02:11 +08:00
77db980e4f 删除 Logistics/V5/web/README.md 2025-10-24 02:46:32 +08:00
HKXluo
c38a55330c cc物流系统V5 2025-10-24 02:43:59 +08:00
HKXluo
b6dea93a18 修复流体无法添加一次转移数量的bug 2025-10-22 13:49:49 +08:00
HKXluo
9893c2dada 修复添加数量限制出错问题 2025-10-22 13:10:10 +08:00
16 changed files with 4978 additions and 10 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.history
Logistics/V5/web/node_modules

117
LICENSE Normal file
View File

@@ -0,0 +1,117 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice

148
Logistics/V5/README.md Normal file
View File

@@ -0,0 +1,148 @@
# 物流系统 v5.0 文档
## 概述
物流系统 v5.0 是一个高级物品和流体传输系统,支持多容器操作和缓存模式。系统通过配置文件管理任务,支持复杂的过滤规则和优先级设置。
## 核心功能
### 1. 传输类型
- **物品传输**:在容器间移动物品
- **流体传输**:在容器间移动流体
### 2. 缓存模式
- 适用于及作为输出也作为输入的容器
- 物品被送如时添加临时黑名单 这些物品不会被再次传输到别的容器
### 3. 过滤规则
- **白名单**:只允许特定物品传输
- **黑名单**:禁止特定物品传输
- **数量限制**:设置一次传输物品数量
### 4. 优先级系统
- 输出容器按优先级分组
- 高优先级容器优先接收物品
- 同优先级采用轮询机制
## 配置文件结构
配置文件存储在 `config.cfg` 中,包含以下部分:
```lua
{
settings = {
logEnabled = false, -- 日志开关
zhlog = false, -- 中文日志开关
delay = 0.01 -- 轮询延迟(秒)
},
tasks = { -- 任务列表
-- 任务示例
{
type = "item", -- 或 "fluid"
input = {
["容器名"] = {
cache = true/false,
whitelist = {["物品名"] = {}},
blacklist = {["物品名"] = {}}
}
},
output = {
["容器名"] = {
priority = 数字,
cache = true/false,
whitelist = {["物品名"] = {count = 数量}},
blacklist = {["物品名"] = {}}
}
}
}
},
Tlist = {} -- 缓存传输列表
}
```
## 使用命令
### 基本命令
| 命令 | 描述 | 示例 |
|------|------|------|
| `add <JSON>` | 添加新任务 | `add {"type":"item","input":{"left":{...}},"output":{"right":{...}}}` |
| `list` | 列出所有任务 | `list` |
| `remove <索引>` | 移除任务 | `remove 1` |
| `set <key> <value>` | 修改设置 | `set delay 0.05` |
| `help` | 显示帮助信息 | `help` |
| `exit` | 退出程序 | `exit` |
### 设置命令
| 键 | 值 | 描述 |
|----|----|------|
| `log` | true/false | 启用/禁用日志 |
| `zh` | true/false | 启用/禁用中文日志 |
| `delay` | 数字 | 设置轮询延迟(秒) |
## 任务配置示例
### 物品传输任务
```json
{
"type": "item",
"input": {
"left": {
"cache": true,
"whitelist": {
"minecraft:stone": {}
}
}
},
"output": {
"right": {
"priority": 1,
"whitelist": {
"minecraft:stone": {"count": 64}
},
"blacklist": {
"minecraft:dirt": {}
}
}
}
}
```
### 流体传输任务
```json
{
"type": "fluid",
"input": {
"tank_left": {
"whitelist": {
"water": {}
}
}
},
"output": {
"tank_right": {
"priority": 2
}
}
}
```
## 日志系统
- **日志级别**
- SUCCESS (绿色):成功操作
- SKIPPED (橙色):跳过操作
- INFO (白色):一般信息
- WARNING (黄色):警告信息
- ERROR (红色):错误信息
- DEBUG (浅灰色):调试信息
- **日志格式**
```
[时间] 级别: 消息
```
## 注意事项
1. 确保所有容器名称正确且可访问
2. 确保所有容器链接到电脑的同一个调制解调器
3. 流体容器必须支持流体接口
4. 复杂的过滤规则可能影响性能
5. 轮询延迟设置过低可能导致CPU占用过高
系统通过高效的缓存机制和优先级处理,实现了复杂的多容器物流管理,适用于各种自动化场景。

947
Logistics/V5/startup.lua Normal file
View File

@@ -0,0 +1,947 @@
-- Logistics System v5.0 (Multi-Container & Cache Mode)
-- 支持带缓存的复杂容器间传输
-- 配置保存在 config.cfg
local configFile = "config.cfg"
local tasks = {}
local settings = {
logEnabled = false, -- 日志开关,默认关闭
zhlog = false, -- 中文日志开关,默认关闭
delay = 0.01 -- 轮询延迟默认0.01秒
}
local Tlist = {} -- 缓存传输列表
local lastOutput = {} -- 记录每个任务每个优先级组上次使用的输出容器
-- 加载配置
local function loadConfig()
if fs.exists(configFile) then
local file = fs.open(configFile, "r")
local data = file.readAll()
file.close()
local config = textutils.unserialize(data) or {}
-- 加载设置
if config.settings then
settings.logEnabled = config.settings.logEnabled or settings.logEnabled
settings.zhlog = config.settings.zhlog or settings.zhlog
settings.delay = config.settings.delay or settings.delay
end
-- 加载任务
tasks = config.tasks or {}
-- 加载传输列表
Tlist = config.Tlist or {}
end
end
loadConfig()
-- 保存配置
local function saveConfig()
local config = {
settings = {
logEnabled = settings.logEnabled,
zhlog = settings.zhlog,
delay = settings.delay
},
tasks = tasks,
Tlist = Tlist
}
local file = fs.open(configFile, "w")
file.write(textutils.serialize(config))
file.close()
end
if settings.zhlog then
printUtf8 = load(http.get("https://alist.liulikeji.cn/d/HFS/utf8ptrint.lua").readAll())()
term.clear()
term.setCursorPos(1,1)
else
function printUtf8(logMessage,color,bcolor)
term.setTextColour(color)
term.setBackgroundColour(bcolor)
print(logMessage)
term.setTextColour(colors.white)
term.setBackgroundColour(colors.black)
end
end
-- 日志级别定义
local LOG_LEVEL = {
SUCCESS = {name = "SUCCESS", color = colors.green},
SKIPPED = {name = "SKIPPED", color = colors.orange},
INFO = {name = "INFO", color = colors.white},
WARNING = {name = "WARNING", color = colors.yellow},
ERROR = {name = "ERROR", color = colors.red},
DEBUG = {name = "DEBUG", color = colors.lightGray}
}
-- 日志记录函数
local function log(level, zhMessage, enMessage)
if level ~= LOG_LEVEL.ERROR then
if not settings.logEnabled then return end
end
local message = settings.zhlog and zhMessage or enMessage
local timeStr = os.date("%H:%M:%S")
local levelName = level.name
local color = level.color
-- 格式化日志消息
local logMessage = string.format("[%s] %s: %s", timeStr, levelName, message)
-- 打印日志
printUtf8(logMessage, color, colors.black)
end
-- 命令输出函数(双语支持)
local function echo(zhMessage, enMessage)
local message = settings.zhlog and zhMessage or enMessage
printUtf8(message, colors.lightBlue, colors.black)
end
-- 初始化日志
log(LOG_LEVEL.INFO, "程序开始 - 物流系统 v5.0", "Program started - Turtle Logistics System v5.0")
-- 获取容器物品列表
local function getContainerItems(containerName)
local container = peripheral.wrap(containerName)
if not container then
log(LOG_LEVEL.ERROR, "错误: 容器不存在: "..containerName, "Error: Container not found: "..containerName)
return nil
end
local items = {}
local containerList = container.list()
for slot, item in pairs(containerList) do
items[tostring(slot)] = {
count = item.count,
name = item.name
}
end
return items
end
-- 比较两个物品列表是否相同
local function compareItems(items1, items2)
if not items1 or not items2 then return false end
-- 检查槽位数量
local count1 = 0
for _ in pairs(items1) do count1 = count1 + 1 end
local count2 = 0
for _ in pairs(items2) do count2 = count2 + 1 end
if count1 ~= count2 then return false end
-- 检查每个槽位
for slot, item1 in pairs(items1) do
local item2 = items2[slot]
if not item2 then return false end
if item1.name ~= item2.name or item1.count ~= item2.count then
return false
end
end
return true
end
-- 获取容器中不在Tlist中的物品
local function getNewItems(containerName)
local currentItems = getContainerItems(containerName)
if not currentItems then return nil end
local tlistItems = Tlist[containerName] or {}
local newItems = {}
for slot, item in pairs(currentItems) do
local tlistItem = tlistItems[slot]
if not tlistItem or tlistItem.name ~= item.name or tlistItem.count ~= item.count then
newItems[slot] = item
end
end
return newItems
end
-- 更新Tlist中的容器记录
local function updateTlist(containerName, items)
if not Tlist[containerName] then
Tlist[containerName] = {}
end
-- 只更新传入的物品槽位
for slot, item in pairs(items) do
Tlist[containerName][slot] = {
name = item.name,
count = item.count
}
end
end
-- 清理Tlist中缺失的物品
local function cleanTlist(containerName)
if not Tlist[containerName] then return end
local currentItems = getContainerItems(containerName)
if not currentItems then return end
local toRemove = {}
-- 找出Tlist中存在但容器中不存在的物品
for slot, tlistItem in pairs(Tlist[containerName]) do
local currentItem = currentItems[slot]
if not currentItem or currentItem.name ~= tlistItem.name or currentItem.count ~= tlistItem.count then
table.insert(toRemove, slot)
end
end
-- 移除这些物品
for _, slot in ipairs(toRemove) do
Tlist[containerName][slot] = nil
end
end
-- 辅助函数:检查物品是否通过过滤规则
local function isItemAllowed(itemName, containerConfig)
-- 如果有白名单,只允许白名单中的物品
if containerConfig.whitelist then
return containerConfig.whitelist[itemName] ~= nil
end
-- 如果有黑名单,排除黑名单中的物品
if containerConfig.blacklist then
return containerConfig.blacklist[itemName] == nil
end
-- 没有过滤规则时允许所有物品
return true
end
-- 获取物品的传输数量限制
local function getTransferLimit(itemName, containerConfig)
-- 检查白名单中是否有该物品的数量限制
if containerConfig.whitelist and containerConfig.whitelist[itemName] then
local countSetting = containerConfig.whitelist[itemName].count
if countSetting and countSetting > 0 then
return countSetting
end
end
-- 如果没有限制或限制为0则返回nil表示传输全部
return nil
end
-- 在 executeItemTask 函数中添加更多调试日志
local function executeItemTask(task, taskIndex)
log(LOG_LEVEL.INFO, "开始执行物品任务 #"..taskIndex, "Starting item transfer task #"..taskIndex)
-- 收集所有输入容器的物品
local allItemsToTransfer = {}
-- 处理输入容器
for inputName, inputConfig in pairs(task.input) do
log(LOG_LEVEL.DEBUG, "开始处理输入容器: "..inputName, "Starting processing input container: "..inputName)
local container = peripheral.wrap(inputName)
if not container then
log(LOG_LEVEL.ERROR, "错误: 输入容器不存在: "..inputName, "Error: Input container not found: "..inputName)
goto continue
end
-- 缓存容器处理
if inputConfig.cache then
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 是缓存容器", "Container "..inputName.." is a cache container")
-- 清理Tlist中缺失的物品
cleanTlist(inputName)
-- 获取当前物品状态
local currentItems = getContainerItems(inputName)
if not currentItems then
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
goto continue
end
-- 检查是否与Tlist完全相同
if compareItems(currentItems, Tlist[inputName] or {}) then
log(LOG_LEVEL.DEBUG, "容器内容与Tlist相同跳过: "..inputName, "Container contents match Tlist, skipping: "..inputName)
goto continue
end
-- 获取新物品不在Tlist中的
local newItems = getNewItems(inputName)
if not newItems or not next(newItems) then
log(LOG_LEVEL.DEBUG, "没有新物品,跳过: "..inputName, "No new items, skipping: "..inputName)
goto continue
end
-- 添加到传输列表(应用输入容器过滤)
for slot, item in pairs(newItems) do
if isItemAllowed(item.name, inputConfig) then
-- 不再在输入容器限制流量
-- -- 获取传输数量限制
-- local transferLimit = getTransferLimit(item.name, inputConfig)
-- -- 检查是否满足传输条件
-- if transferLimit and item.count < transferLimit then
-- log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
-- "Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
-- goto continue_item
-- end
-- local transferCount = transferLimit or item.count
local transferCount = item.count
-- 如果数量大于0才添加
if transferCount > 0 then
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
table.insert(allItemsToTransfer, {
container = inputName,
slot = tonumber(slot),
name = item.name,
count = transferCount
})
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
"Item "..item.name.." insufficient quantity, skipping transfer")
end
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
"Item "..item.name.." filtered by input container "..inputName)
end
::continue_item::
end
else
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 不是缓存容器", "Container "..inputName.." is not a cache container")
local items = getContainerItems(inputName)
if not items then
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
goto continue
end
for slot, item in pairs(items) do
-- 应用输入容器过滤
if isItemAllowed(item.name, inputConfig) then
-- 不再在输入容器限制流量
-- 获取传输数量限制
-- local transferLimit = getTransferLimit(item.name, inputConfig)
-- -- 检查是否满足传输条件
-- if transferLimit and item.count < transferLimit then
-- log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
-- "Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
-- goto continue_item_non_cache
-- end
-- local transferCount = transferLimit or item.count
local transferCount = item.count
-- 如果数量大于0才添加
if transferCount > 0 then
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
table.insert(allItemsToTransfer, {
container = inputName,
slot = tonumber(slot),
name = item.name,
count = transferCount
})
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
"Item "..item.name.." insufficient quantity, skipping transfer")
end
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
"Item "..item.name.." filtered by input container "..inputName)
end
::continue_item_non_cache::
end
end
::continue::
end
if #allItemsToTransfer == 0 then
log(LOG_LEVEL.SKIPPED, "没有物品需要传输", "No items to transfer")
return false
end
log(LOG_LEVEL.INFO, "找到 "..#allItemsToTransfer.." 个物品需要传输", "Found "..#allItemsToTransfer.." items to transfer")
-- 按优先级分组输出容器
local outputGroups = {}
for outputName, outputConfig in pairs(task.output) do
local priority = outputConfig.priority or 1
if not outputGroups[priority] then
outputGroups[priority] = {}
end
table.insert(outputGroups[priority], {
name = outputName,
config = outputConfig
})
end
-- 按优先级排序(从高到低)
local sortedPriorities = {}
for priority in pairs(outputGroups) do
table.insert(sortedPriorities, priority)
end
table.sort(sortedPriorities, function(a, b) return a > b end)
-- 传输物品
local transferred = false
for _, itemData in ipairs(allItemsToTransfer) do
log(LOG_LEVEL.DEBUG, "尝试传输物品: "..itemData.name.." x"..itemData.count.." (源容器: "..itemData.container..", 槽位: "..itemData.slot..")",
"Attempting to transfer item: "..itemData.name.." x"..itemData.count.." (source: "..itemData.container..", slot: "..itemData.slot..")")
local sourceContainer = peripheral.wrap(itemData.container)
if not sourceContainer then
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..itemData.container, "Error: Source container not found: "..itemData.container)
goto next_item
end
-- 尝试推送到输出容器
local pushed = false
-- 按优先级尝试输出
for _, priority in ipairs(sortedPriorities) do
local group = outputGroups[priority]
-- 轮询输出
local key = taskIndex.."_"..priority -- 使用任务索引和优先级作为唯一键
log(LOG_LEVEL.DEBUG, "输出容器键: "..key, "Output container key: "..key)
local startIdx = lastOutput[key] or 1
local idx = startIdx
local attempts = 0
repeat
log(LOG_LEVEL.DEBUG, "输出容器索引: "..tostring(idx).." 容器组大小: "..tostring(#group),"Output container index: "..tostring(idx).." group size: "..tostring(#group))
local output = group[idx]
log(LOG_LEVEL.DEBUG, "尝试输出容器: "..output.name.." (优先级 "..priority..", 尝试 "..attempts.."/"..#group..")",
"Attempting output container: "..output.name.." (priority "..priority..", attempt "..attempts.."/"..#group..")")
local outputContainer = peripheral.wrap(output.name)
if outputContainer then
-- 应用输出容器过滤
if not isItemAllowed(itemData.name, output.config) then
log(LOG_LEVEL.DEBUG, "物品 "..itemData.name.." 被输出容器 "..output.name.." 过滤",
"Item "..itemData.name.." filtered by output container "..output.name)
goto next_output
end
local transferLimit = getTransferLimit(itemData.name, output.config)
-- 检查是否满足传输条件
if transferLimit and itemData.count < transferLimit then
log(LOG_LEVEL.DEBUG, "物品 "..itemData.name.." 数量不足("..itemData.count.."<"..transferLimit.."),跳过传输",
"Item "..itemData.name.." insufficient quantity ("..itemData.count.."<"..transferLimit.."), skipping transfer")
goto next_output
end
local transferCount = transferLimit or itemData.count
log(LOG_LEVEL.DEBUG, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
-- 推送物品
local pushedCount = sourceContainer.pushItems(
output.name,
itemData.slot,
transferCount
)
if pushedCount > 0 then
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedCount..""..itemData.name..""..output.name, "Successfully pushed "..pushedCount.." "..itemData.name.." to "..output.name)
pushed = true
transferred = true
-- 更新输出容器的Tlist如果是缓存容器
if output.config.cache then
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
if not Tlist[output.name] then
Tlist[output.name] = {}
end
-- 获取输出容器当前物品
local outputItems = getContainerItems(output.name)
if outputItems then
-- 只更新传输的物品槽位
for slot, item in pairs(outputItems) do
if item.name == itemData.name then
Tlist[output.name][slot] = {
name = item.name,
count = item.count
}
end
end
end
end
-- 更新输入容器的Tlist如果是缓存容器
if task.input[itemData.container] and task.input[itemData.container].cache then
log(LOG_LEVEL.DEBUG, "更新输入容器Tlist: "..itemData.container, "Updating input container Tlist: "..itemData.container)
updateTlist(itemData.container, {
[tostring(itemData.slot)] = {
name = itemData.name,
count = itemData.count - pushedCount
}
})
end
-- 更新轮询索引
log(LOG_LEVEL.DEBUG, "轮询: idx:" .. tostring(idx) .. " #group:" .. tostring(#group))
lastOutput[key] = idx % #group + 1
break
else
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name.." (可能容器已满或无法接收)",
"Push failed to "..output.name.." (container may be full or unable to receive)")
end
else
log(LOG_LEVEL.ERROR, "输出容器不存在或无法访问: "..output.name, "Output container not found or inaccessible: "..output.name)
end
::next_output::
idx = idx % #group + 1
attempts = attempts + 1
until attempts >= #group
if pushed then break end
end
if not pushed then
log(LOG_LEVEL.WARNING, "警告: 无法传输物品 "..itemData.name.." x"..itemData.count.." (所有输出容器尝试失败)",
"Warning: Failed to transfer item "..itemData.name.." x"..itemData.count.." (all output containers failed)")
end
::next_item::
end
if not transferred then
log(LOG_LEVEL.DEBUG, "所有物品传输尝试失败", "All item transfer attempts failed")
end
return transferred
end
-- 执行流体传输任务
local function executeFluidTask(task, taskIndex)
log(LOG_LEVEL.INFO, "开始执行流体任务 #"..taskIndex, "Starting fluid transfer task #"..taskIndex)
local transferred = false
-- 处理输入容器
for inputName, inputConfig in pairs(task.input) do
log(LOG_LEVEL.INFO, "处理输入容器: "..inputName, "Processing input container: "..inputName)
local source = peripheral.wrap(inputName)
if not source then
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..inputName, "Error: Source container not found: "..inputName)
goto continue
end
if not source.tanks then
log(LOG_LEVEL.WARNING, "警告: 容器不支持流体: "..inputName.." - 跳过", "Warning: Container does not support fluids: "..inputName.." - skipping")
goto continue
end
-- 获取流体槽 - 使用 pairs 而不是 # 来计算数量
local tanks = source.tanks()
if not tanks then
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
goto continue
end
-- 正确计算流体槽数量
local tankCount = 0
for _ in pairs(tanks) do tankCount = tankCount + 1 end
if tankCount == 0 then
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
goto continue
end
-- 使用 pairs 遍历所有流体槽
for tankIndex, tank in pairs(tanks) do
-- 确保 tank 是有效的表
if type(tank) == "table" and tank.name and tank.amount then
-- 应用输入容器过滤
if tank.amount > 0 and isItemAllowed(tank.name, inputConfig) then
log(LOG_LEVEL.INFO, "找到可传输流体: "..tank.name.." ("..tank.amount.."mB)", "Found transferable fluid: "..tank.name.." ("..tank.amount.."mB)")
-- 按优先级分组输出容器
local outputGroups = {}
for outputName, outputConfig in pairs(task.output) do
local priority = outputConfig.priority or 1
if not outputGroups[priority] then
outputGroups[priority] = {}
end
table.insert(outputGroups[priority], {
name = outputName,
config = outputConfig
})
end
-- 按优先级排序(从高到低)
local sortedPriorities = {}
for priority in pairs(outputGroups) do
table.insert(sortedPriorities, priority)
end
table.sort(sortedPriorities, function(a, b) return a > b end)
-- 尝试传输流体
local pushed = false
local amountToTransfer = tank.amount
for _, priority in ipairs(sortedPriorities) do
local group = outputGroups[priority]
-- 轮询输出
local key = taskIndex.."_"..priority -- 使用任务索引和优先级作为唯一键
local startIdx = lastOutput[key] or 1
local idx = startIdx
local attempts = 0
repeat
local output = group[idx]
local destination = peripheral.wrap(output.name)
if destination and destination.tanks then
-- 应用输出容器过滤
if not isItemAllowed(tank.name, output.config) then
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输出容器 "..output.name.." 过滤",
"Fluid "..tank.name.." filtered by output container "..output.name)
goto next_output
end
log(LOG_LEVEL.INFO, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
-- 推送流体
local pushedAmount = source.pushFluid(
output.name,
amountToTransfer,
tank.name,
tankIndex
)
if pushedAmount > 0 then
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedAmount.."mB "..tank.name..""..output.name, "Successfully pushed "..pushedAmount.."mB "..tank.name.." to "..output.name)
pushed = true
transferred = true
amountToTransfer = amountToTransfer - pushedAmount
-- 更新输出容器的Tlist如果是缓存容器
if output.config.cache then
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
if not Tlist[output.name] then
Tlist[output.name] = {}
end
-- 获取输出容器当前流体
local destTanks = destination.tanks()
if destTanks then
for _, destTank in pairs(destTanks) do
if destTank.name == tank.name then
Tlist[output.name][tank.name] = {
amount = destTank.amount
}
end
end
end
end
-- 更新轮询索引
lastOutput[key] = idx % #group + 1
else
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name, "Push failed to "..output.name)
end
else
if not destination then
log(LOG_LEVEL.ERROR, "错误: 输出容器不存在: "..output.name, "Error: Output container not found: "..output.name)
else
log(LOG_LEVEL.WARNING, "错误: 输出容器不支持流体: "..output.name, "Error: Output container does not support fluids: "..output.name)
end
end
::next_output::
idx = idx % #group + 1
attempts = attempts + 1
until attempts >= #group or amountToTransfer <= 0
if amountToTransfer <= 0 then break end
end
if amountToTransfer > 0 then
log(LOG_LEVEL.WARNING, string.format("警告: 仍有 %dmB %s 未传输", amountToTransfer, tank.name),
string.format("Warning: Still %dmB %s not transferred", amountToTransfer, tank.name))
end
else
if tank.amount == 0 then
log(LOG_LEVEL.INFO, "槽位 "..tankIndex.." 无流体", "Tank "..tankIndex.." has no fluid")
else
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输入容器 "..inputName.." 过滤",
"Fluid "..tank.name.." filtered by input container "..inputName)
end
end
else
log(LOG_LEVEL.WARNING, "警告: 槽位 "..tankIndex.." 的数据无效", "Warning: Invalid data for tank "..tankIndex)
end
end
::continue::
end
return transferred
end
-- 执行任务
local function executeTask(task, taskIndex)
if task.type == "item" then
return executeItemTask(task, taskIndex)
elseif task.type == "fluid" then
return executeFluidTask(task, taskIndex)
else
log(LOG_LEVEL.ERROR, "错误: 未知任务类型: "..(task.type or "nil"), "Error: Unknown task type: "..(task.type or "nil"))
return false
end
end
-- 主任务循环
local function taskLoop()
log(LOG_LEVEL.INFO, "任务循环开始", "Task loop started")
while true do
for i, task in ipairs(tasks) do
log(LOG_LEVEL.INFO, "开始任务 #"..i, "Starting task #"..i)
local success, result = pcall(executeTask, task, i)
if not success then
log(LOG_LEVEL.ERROR, "任务 #"..i.." 错误: "..tostring(result), "Task #"..i.." error: "..tostring(result))
elseif result then
log(LOG_LEVEL.SUCCESS, "任务 #"..i.." 成功执行", "Task #"..i.." executed successfully")
else
log(LOG_LEVEL.SKIPPED, "任务 #"..i.." 未执行传输", "Task #"..i.." no transfer performed")
end
end
os.sleep(settings.delay)
end
end
-- 命令处理
local function processCommand(cmd)
local args = {}
for word in cmd:gmatch("%S+") do
table.insert(args, word)
end
if #args == 0 then return end
local command = args[1]:lower()
if command == "add" and #args >= 2 then
-- 合并参数为JSON字符串
local jsonStr = table.concat(args, " ", 2)
local success, task = pcall(textutils.unserializeJSON, jsonStr)
if success and type(task) == "table" then
-- 验证任务类型
if not task.type then
echo("错误: 任务缺少类型字段", "Error: Task missing type field")
return
end
if task.type ~= "item" and task.type ~= "fluid" then
echo("错误: 无效的任务类型: "..task.type, "Error: Invalid task type: "..task.type)
return
end
-- 验证输入输出
if not task.input or not task.output then
echo("错误: 任务缺少输入或输出字段", "Error: Task missing input or output fields")
return
end
table.insert(tasks, task)
saveConfig()
echo("任务添加成功", "Task added successfully")
else
echo("错误: 无效的JSON格式", "Error: Invalid JSON format")
end
elseif command == "list" then
if #tasks == 0 then
echo("没有配置任务", "No tasks configured")
else
for i, task in ipairs(tasks) do
echo("任务 #"..i.." ("..task.type..")", "Task #"..i.." ("..task.type..")")
echo(" 输入容器:", " Input containers:")
for name, config in pairs(task.input) do
local cacheStr = config.cache and "缓存" or "非缓存"
local cacheStrEn = config.cache and "cache" or "non-cache"
-- 显示过滤规则
if config.whitelist then
local items = {}
for itemName, itemConfig in pairs(config.whitelist) do
if itemConfig.count and itemConfig.count > 0 then
table.insert(items, itemName.." (count="..itemConfig.count..")")
else
table.insert(items, itemName)
end
end
echo(" "..name.." ("..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
" "..name.." ("..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
elseif config.blacklist then
local items = {}
for itemName in pairs(config.blacklist) do
table.insert(items, itemName)
end
echo(" "..name.." ("..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
" "..name.." ("..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
else
echo(" "..name.." ("..cacheStr..")", " "..name.." ("..cacheStrEn..")")
end
end
echo(" 输出容器:", " Output containers:")
for name, config in pairs(task.output) do
local cacheStr = config.cache and "缓存" or "非缓存"
local cacheStrEn = config.cache and "cache" or "non-cache"
local priority = config.priority or 1
-- 显示过滤规则
if config.whitelist then
local items = {}
for itemName, itemConfig in pairs(config.whitelist) do
if itemConfig.count and itemConfig.count > 0 then
table.insert(items, itemName.." (count="..itemConfig.count..")")
else
table.insert(items, itemName)
end
end
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
" "..name.." (priority:"..priority..", "..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
elseif config.blacklist then
local items = {}
for itemName in pairs(config.blacklist) do
table.insert(items, itemName)
end
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
" "..name.." (priority:"..priority..", "..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
else
echo(" "..name.." (优先级:"..priority..", "..cacheStr..")",
" "..name.." (priority:"..priority..", "..cacheStrEn..")")
end
end
end
end
elseif command == "remove" and #args >= 2 then
local index = tonumber(args[2])
if index and tasks[index] then
table.remove(tasks, index)
saveConfig()
echo("任务 "..index.." 已移除", "Task "..index.." removed")
else
echo("无效的任务索引", "Invalid task index")
end
elseif command == "log" then
if #args >= 2 then
if args[2]:lower() == "on" then
settings.logEnabled = true
saveConfig()
echo("日志已启用", "Logging enabled")
elseif args[2]:lower() == "off" then
settings.logEnabled = false
saveConfig()
echo("日志已禁用", "Logging disabled")
else
echo("无效参数,使用 'log on' 或 'log off'", "Invalid argument, use 'log on' or 'log off'")
end
else
echo("当前日志状态: "..(settings.logEnabled and "启用" or "禁用"),
"Current log status: "..(settings.logEnabled and "enabled" or "disabled"))
end
-- 添加 set 命令
elseif command == "set" and #args >= 3 then
local key = args[2]:lower()
local value = args[3]
if key == "log" then
if value == "true" then
settings.logEnabled = true
saveConfig()
echo("日志已启用", "Logging enabled")
elseif value == "false" then
settings.logEnabled = false
saveConfig()
echo("日志已禁用", "Logging disabled")
else
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
end
elseif key == "zh" then
if value == "true" then
settings.zhlog = true
saveConfig()
echo("中文日志已启用", "Chinese log enabled")
elseif value == "false" then
settings.zhlog = false
saveConfig()
echo("中文日志已禁用", "Chinese log disabled")
else
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
end
elseif key == "delay" then
local numValue = tonumber(value)
if numValue and numValue > 0 then
settings.delay = numValue
saveConfig()
echo("轮询延迟已设置为: "..numValue.."", "Polling delay set to: "..numValue.." seconds")
else
echo("无效值请输入大于0的数字", "Invalid value, please enter a number greater than 0")
end
else
echo("无效设置项,可用选项: logEnabled, zhlog, delay", "Invalid setting, available options: logEnabled, zhlog, delay")
end
elseif command == "help" then
echo("可用命令:", "Available commands:")
echo("add <JSON任务> - 添加新任务", "add <JSON task> - Add new task")
echo(" 示例: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}",
" Example: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}")
echo("list - 列出所有任务", "list - List all tasks")
echo("remove <索引> - 移除任务", "remove <index> - Remove task")
echo("log [on|off] - 启用/禁用日志", "log [on|off] - Enable/disable logging")
echo("set <key> <value> - 修改设置", "set <key> <value> - Modify settings")
echo(" 可用键: logEnabled (true/false), zhlog (true/false), delay (数字)", " Available keys: logEnabled (true/false), zhlog (true/false), delay (number)")
echo("exit - 退出程序", "exit - Exit program")
elseif command == "exit" then
echo("正在退出程序", "Exiting program")
os.sleep(0.5)
error("程序终止", 0)
end
end
-- 初始化
echo("物流系统 v5.0 已启动。输入 'help' 查看命令。",
"Turtle Logistics System v5.0 started. Type 'help' for commands.")
-- 主循环
parallel.waitForAll(
function()
while true do
write("> ")
local command = read()
processCommand(command)
end
end,
taskLoop
)

935
Logistics/V5/web/1.txt Normal file
View File

@@ -0,0 +1,935 @@
-- Turtle Logistics System v5.0 (Multi-Container & Cache Mode)
-- 支持带缓存的复杂容器间传输
-- 配置保存在 config.cfg
local configFile = "config.cfg"
local tasks = {}
local settings = {
logEnabled = false, -- 日志开关,默认关闭
zhlog = false, -- 中文日志开关,默认关闭
delay = 0.01 -- 轮询延迟默认0.01秒
}
local directions = {
up = "up", down = "down", forward = "forward"
}
local Tlist = {} -- 缓存传输列表
local lastOutput = {} -- 记录每个优先级组上次使用的输出容器
-- 加载配置
local function loadConfig()
if fs.exists(configFile) then
local file = fs.open(configFile, "r")
local data = file.readAll()
file.close()
local config = textutils.unserialize(data) or {}
-- 加载设置
if config.settings then
settings.logEnabled = config.settings.logEnabled or settings.logEnabled
settings.zhlog = config.settings.zhlog or settings.zhlog
settings.delay = config.settings.delay or settings.delay
end
-- 加载任务
tasks = config.tasks or {}
-- 加载传输列表
Tlist = config.Tlist or {}
end
end
loadConfig()
-- 保存配置
local function saveConfig()
local config = {
settings = {
logEnabled = settings.logEnabled,
zhlog = settings.zhlog,
delay = settings.delay
},
tasks = tasks,
Tlist = Tlist
}
local file = fs.open(configFile, "w")
file.write(textutils.serialize(config))
file.close()
end
if settings.zhlog then
printUtf8 = load(http.get("https://alist.liulikeji.cn/d/HFS/utf8ptrint.lua").readAll())()
term.clear()
term.setCursorPos(1,1)
else
function printUtf8(logMessage,color,bcolor)
term.setTextColour(color)
term.setBackgroundColour(bcolor)
print(logMessage)
term.setTextColour(colors.white)
term.setBackgroundColour(colors.black)
end
end
-- 日志级别定义
local LOG_LEVEL = {
SUCCESS = {name = "SUCCESS", color = colors.green},
SKIPPED = {name = "SKIPPED", color = colors.orange},
INFO = {name = "INFO", color = colors.white},
WARNING = {name = "WARNING", color = colors.yellow},
ERROR = {name = "ERROR", color = colors.red},
DEBUG = {name = "DEBUG", color = colors.lightGray}
}
-- 日志记录函数
local function log(level, zhMessage, enMessage)
if level ~= LOG_LEVEL.ERROR then
if not settings.logEnabled then return end
end
local message = settings.zhlog and zhMessage or enMessage
local timeStr = os.date("%H:%M:%S")
local levelName = level.name
local color = level.color
-- 格式化日志消息
local logMessage = string.format("[%s] %s: %s", timeStr, levelName, message)
-- 打印日志
printUtf8(logMessage, color, colors.black)
end
-- 命令输出函数(双语支持)
local function echo(zhMessage, enMessage)
local message = settings.zhlog and zhMessage or enMessage
printUtf8(message, colors.lightBlue, colors.black)
end
-- 初始化日志
log(LOG_LEVEL.INFO, "程序开始 - 海龟物流系统 v5.0", "Program started - Turtle Logistics System v5.0")
-- 获取容器物品列表
local function getContainerItems(containerName)
local container = peripheral.wrap(containerName)
if not container then
log(LOG_LEVEL.ERROR, "错误: 容器不存在: "..containerName, "Error: Container not found: "..containerName)
return nil
end
local items = {}
local containerList = container.list()
for slot, item in pairs(containerList) do
items[tostring(slot)] = {
count = item.count,
name = item.name
}
end
return items
end
-- 比较两个物品列表是否相同
local function compareItems(items1, items2)
if not items1 or not items2 then return false end
-- 检查槽位数量
local count1 = 0
for _ in pairs(items1) do count1 = count1 + 1 end
local count2 = 0
for _ in pairs(items2) do count2 = count2 + 1 end
if count1 ~= count2 then return false end
-- 检查每个槽位
for slot, item1 in pairs(items1) do
local item2 = items2[slot]
if not item2 then return false end
if item1.name ~= item2.name or item1.count ~= item2.count then
return false
end
end
return true
end
-- 获取容器中不在Tlist中的物品
local function getNewItems(containerName)
local currentItems = getContainerItems(containerName)
if not currentItems then return nil end
local tlistItems = Tlist[containerName] or {}
local newItems = {}
for slot, item in pairs(currentItems) do
local tlistItem = tlistItems[slot]
if not tlistItem or tlistItem.name ~= item.name or tlistItem.count ~= item.count then
newItems[slot] = item
end
end
return newItems
end
-- 更新Tlist中的容器记录
local function updateTlist(containerName, items)
if not Tlist[containerName] then
Tlist[containerName] = {}
end
-- 只更新传入的物品槽位
for slot, item in pairs(items) do
Tlist[containerName][slot] = {
name = item.name,
count = item.count
}
end
end
-- 清理Tlist中缺失的物品
local function cleanTlist(containerName)
if not Tlist[containerName] then return end
local currentItems = getContainerItems(containerName)
if not currentItems then return end
local toRemove = {}
-- 找出Tlist中存在但容器中不存在的物品
for slot, tlistItem in pairs(Tlist[containerName]) do
local currentItem = currentItems[slot]
if not currentItem or currentItem.name ~= tlistItem.name or currentItem.count ~= tlistItem.count then
table.insert(toRemove, slot)
end
end
-- 移除这些物品
for _, slot in ipairs(toRemove) do
Tlist[containerName][slot] = nil
end
end
-- 辅助函数:检查物品是否通过过滤规则
local function isItemAllowed(itemName, containerConfig)
-- 如果有白名单,只允许白名单中的物品
if containerConfig.whitelist then
return containerConfig.whitelist[itemName] ~= nil
end
-- 如果有黑名单,排除黑名单中的物品
if containerConfig.blacklist then
return containerConfig.blacklist[itemName] == nil
end
-- 没有过滤规则时允许所有物品
return true
end
-- 获取物品的传输数量限制
local function getTransferLimit(itemName, containerConfig)
-- 检查白名单中是否有该物品的数量限制
if containerConfig.whitelist and containerConfig.whitelist[itemName] then
local countSetting = containerConfig.whitelist[itemName].count
if countSetting and countSetting > 0 then
return countSetting
end
end
-- 如果没有限制或限制为0则返回nil表示传输全部
return nil
end
-- 在 executeItemTask 函数中添加更多调试日志
local function executeItemTask(task)
log(LOG_LEVEL.INFO, "开始执行物品任务", "Starting item transfer task")
-- 收集所有输入容器的物品
local allItemsToTransfer = {}
-- 处理输入容器
for inputName, inputConfig in pairs(task.input) do
log(LOG_LEVEL.DEBUG, "开始处理输入容器: "..inputName, "Starting processing input container: "..inputName)
local container = peripheral.wrap(inputName)
if not container then
log(LOG_LEVEL.ERROR, "错误: 输入容器不存在: "..inputName, "Error: Input container not found: "..inputName)
goto continue
end
-- 缓存容器处理
if inputConfig.cache then
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 是缓存容器", "Container "..inputName.." is a cache container")
-- 清理Tlist中缺失的物品
cleanTlist(inputName)
-- 获取当前物品状态
local currentItems = getContainerItems(inputName)
if not currentItems then
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
goto continue
end
-- 检查是否与Tlist完全相同
if compareItems(currentItems, Tlist[inputName] or {}) then
log(LOG_LEVEL.DEBUG, "容器内容与Tlist相同跳过: "..inputName, "Container contents match Tlist, skipping: "..inputName)
goto continue
end
-- 获取新物品不在Tlist中的
local newItems = getNewItems(inputName)
if not newItems or not next(newItems) then
log(LOG_LEVEL.DEBUG, "没有新物品,跳过: "..inputName, "No new items, skipping: "..inputName)
goto continue
end
-- 添加到传输列表(应用输入容器过滤)
for slot, item in pairs(newItems) do
if isItemAllowed(item.name, inputConfig) then
-- 获取传输数量限制
local transferLimit = getTransferLimit(item.name, inputConfig)
-- 检查是否满足传输条件
if transferLimit and item.count < transferLimit then
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
"Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
goto continue_item
end
local transferCount = transferLimit or item.count
-- 如果数量大于0才添加
if transferCount > 0 then
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
table.insert(allItemsToTransfer, {
container = inputName,
slot = tonumber(slot),
name = item.name,
count = transferCount
})
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
"Item "..item.name.." insufficient quantity, skipping transfer")
end
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
"Item "..item.name.." filtered by input container "..inputName)
end
::continue_item::
end
else
log(LOG_LEVEL.DEBUG, "容器 "..inputName.." 不是缓存容器", "Container "..inputName.." is not a cache container")
local items = getContainerItems(inputName)
if not items then
log(LOG_LEVEL.DEBUG, "无法获取容器物品: "..inputName, "Failed to get container items: "..inputName)
goto continue
end
for slot, item in pairs(items) do
-- 应用输入容器过滤
if isItemAllowed(item.name, inputConfig) then
-- 获取传输数量限制
local transferLimit = getTransferLimit(item.name, inputConfig)
-- 检查是否满足传输条件
if transferLimit and item.count < transferLimit then
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足("..item.count.."<"..transferLimit.."),跳过传输",
"Item "..item.name.." insufficient quantity ("..item.count.."<"..transferLimit.."), skipping transfer")
goto continue_item_non_cache
end
local transferCount = transferLimit or item.count
-- 如果数量大于0才添加
if transferCount > 0 then
log(LOG_LEVEL.DEBUG, "添加物品到传输列表: "..item.name.." x"..transferCount.." (槽位 "..slot..")",
"Adding item to transfer list: "..item.name.." x"..transferCount.." (slot "..slot..")")
table.insert(allItemsToTransfer, {
container = inputName,
slot = tonumber(slot),
name = item.name,
count = transferCount
})
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 数量不足,跳过传输",
"Item "..item.name.." insufficient quantity, skipping transfer")
end
else
log(LOG_LEVEL.DEBUG, "物品 "..item.name.." 被输入容器 "..inputName.." 过滤",
"Item "..item.name.." filtered by input container "..inputName)
end
::continue_item_non_cache::
end
end
::continue::
end
if #allItemsToTransfer == 0 then
log(LOG_LEVEL.SKIPPED, "没有物品需要传输", "No items to transfer")
return false
end
log(LOG_LEVEL.INFO, "找到 "..#allItemsToTransfer.." 个物品需要传输", "Found "..#allItemsToTransfer.." items to transfer")
-- 按优先级分组输出容器
local outputGroups = {}
for outputName, outputConfig in pairs(task.output) do
local priority = outputConfig.priority or 1
if not outputGroups[priority] then
outputGroups[priority] = {}
end
table.insert(outputGroups[priority], {
name = outputName,
config = outputConfig
})
end
-- 按优先级排序(从高到低)
local sortedPriorities = {}
for priority in pairs(outputGroups) do
table.insert(sortedPriorities, priority)
end
table.sort(sortedPriorities, function(a, b) return a > b end)
-- 传输物品
local transferred = false
for _, itemData in ipairs(allItemsToTransfer) do
log(LOG_LEVEL.DEBUG, "尝试传输物品: "..itemData.name.." x"..itemData.count.." (源容器: "..itemData.container..", 槽位: "..itemData.slot..")",
"Attempting to transfer item: "..itemData.name.." x"..itemData.count.." (source: "..itemData.container..", slot: "..itemData.slot..")")
local sourceContainer = peripheral.wrap(itemData.container)
if not sourceContainer then
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..itemData.container, "Error: Source container not found: "..itemData.container)
goto next_item
end
-- 尝试推送到输出容器
local pushed = false
-- 按优先级尝试输出
for _, priority in ipairs(sortedPriorities) do
local group = outputGroups[priority]
-- 轮询输出
local startIdx = lastOutput[priority] or 1
local idx = startIdx
local attempts = 0
repeat
local output = group[idx]
log(LOG_LEVEL.DEBUG, "尝试输出容器: "..output.name.." (优先级 "..priority..", 尝试 "..attempts.."/"..#group..")",
"Attempting output container: "..output.name.." (priority "..priority..", attempt "..attempts.."/"..#group..")")
local outputContainer = peripheral.wrap(output.name)
if outputContainer then
-- 应用输出容器过滤
if not isItemAllowed(itemData.name, output.config) then
log(LOG_LEVEL.DEBUG, "物品 "..itemData.name.." 被输出容器 "..output.name.." 过滤",
"Item "..itemData.name.." filtered by output container "..output.name)
goto next_output
end
log(LOG_LEVEL.DEBUG, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
-- 推送物品
local pushedCount = sourceContainer.pushItems(
output.name,
itemData.slot,
itemData.count
)
if pushedCount > 0 then
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedCount.." 个 "..itemData.name.." 到 "..output.name, "Successfully pushed "..pushedCount.." "..itemData.name.." to "..output.name)
pushed = true
transferred = true
-- 更新输出容器的Tlist如果是缓存容器
if output.config.cache then
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
if not Tlist[output.name] then
Tlist[output.name] = {}
end
-- 获取输出容器当前物品
local outputItems = getContainerItems(output.name)
if outputItems then
-- 只更新传输的物品槽位
for slot, item in pairs(outputItems) do
if item.name == itemData.name then
Tlist[output.name][slot] = {
name = item.name,
count = item.count
}
end
end
end
end
-- 更新输入容器的Tlist如果是缓存容器
if task.input[itemData.container] and task.input[itemData.container].cache then
log(LOG_LEVEL.DEBUG, "更新输入容器Tlist: "..itemData.container, "Updating input container Tlist: "..itemData.container)
updateTlist(itemData.container, {
[tostring(itemData.slot)] = {
name = itemData.name,
count = itemData.count - pushedCount
}
})
end
-- 更新轮询索引
lastOutput[priority] = idx % #group + 1
break
else
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name.." (可能容器已满或无法接收)",
"Push failed to "..output.name.." (container may be full or unable to receive)")
end
else
log(LOG_LEVEL.ERROR, "输出容器不存在或无法访问: "..output.name, "Output container not found or inaccessible: "..output.name)
end
::next_output::
idx = idx % #group + 1
attempts = attempts + 1
until attempts >= #group
if pushed then break end
end
if not pushed then
log(LOG_LEVEL.WARNING, "警告: 无法传输物品 "..itemData.name.." x"..itemData.count.." (所有输出容器尝试失败)",
"Warning: Failed to transfer item "..itemData.name.." x"..itemData.count.." (all output containers failed)")
end
::next_item::
end
if not transferred then
log(LOG_LEVEL.DEBUG, "所有物品传输尝试失败", "All item transfer attempts failed")
end
return transferred
end
-- 执行流体传输任务
local function executeFluidTask(task)
log(LOG_LEVEL.INFO, "开始执行流体任务", "Starting fluid transfer task")
local transferred = false
-- 处理输入容器
for inputName, inputConfig in pairs(task.input) do
log(LOG_LEVEL.INFO, "处理输入容器: "..inputName, "Processing input container: "..inputName)
local source = peripheral.wrap(inputName)
if not source then
log(LOG_LEVEL.ERROR, "错误: 源容器不存在: "..inputName, "Error: Source container not found: "..inputName)
goto continue
end
if not source.tanks then
log(LOG_LEVEL.WARNING, "警告: 容器不支持流体: "..inputName.." - 跳过", "Warning: Container does not support fluids: "..inputName.." - skipping")
goto continue
end
-- 获取流体槽 - 使用 pairs 而不是 # 来计算数量
local tanks = source.tanks()
if not tanks then
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
goto continue
end
-- 正确计算流体槽数量
local tankCount = 0
for _ in pairs(tanks) do tankCount = tankCount + 1 end
if tankCount == 0 then
log(LOG_LEVEL.INFO, "容器中没有流体槽: "..inputName, "No fluid tanks in container: "..inputName)
goto continue
end
-- 使用 pairs 遍历所有流体槽
for tankIndex, tank in pairs(tanks) do
-- 确保 tank 是有效的表
if type(tank) == "table" and tank.name and tank.amount then
-- 应用输入容器过滤
if tank.amount > 0 and isItemAllowed(tank.name, inputConfig) then
log(LOG_LEVEL.INFO, "找到可传输流体: "..tank.name.." ("..tank.amount.."mB)", "Found transferable fluid: "..tank.name.." ("..tank.amount.."mB)")
-- 按优先级分组输出容器
local outputGroups = {}
for outputName, outputConfig in pairs(task.output) do
local priority = outputConfig.priority or 1
if not outputGroups[priority] then
outputGroups[priority] = {}
end
table.insert(outputGroups[priority], {
name = outputName,
config = outputConfig
})
end
-- 按优先级排序(从高到低)
local sortedPriorities = {}
for priority in pairs(outputGroups) do
table.insert(sortedPriorities, priority)
end
table.sort(sortedPriorities, function(a, b) return a > b end)
-- 尝试传输流体
local pushed = false
local amountToTransfer = tank.amount
for _, priority in ipairs(sortedPriorities) do
local group = outputGroups[priority]
-- 轮询输出
local startIdx = lastOutput[priority] or 1
local idx = startIdx
local attempts = 0
repeat
local output = group[idx]
local destination = peripheral.wrap(output.name)
if destination and destination.tanks then
-- 应用输出容器过滤
if not isItemAllowed(tank.name, output.config) then
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输出容器 "..output.name.." 过滤",
"Fluid "..tank.name.." filtered by output container "..output.name)
goto next_output
end
log(LOG_LEVEL.INFO, "尝试推送到 "..output.name.." (优先级 "..priority..")", "Attempting push to "..output.name.." (priority "..priority..")")
-- 推送流体
local pushedAmount = source.pushFluid(
output.name,
amountToTransfer,
tank.name,
tankIndex
)
if pushedAmount > 0 then
log(LOG_LEVEL.SUCCESS, "成功推送 "..pushedAmount.."mB "..tank.name.." 到 "..output.name, "Successfully pushed "..pushedAmount.."mB "..tank.name.." to "..output.name)
pushed = true
transferred = true
amountToTransfer = amountToTransfer - pushedAmount
-- 更新输出容器的Tlist如果是缓存容器
if output.config.cache then
log(LOG_LEVEL.DEBUG, "更新输出容器Tlist: "..output.name, "Updating output container Tlist: "..output.name)
if not Tlist[output.name] then
Tlist[output.name] = {}
end
-- 获取输出容器当前流体
local destTanks = destination.tanks()
if destTanks then
for _, destTank in pairs(destTanks) do
if destTank.name == tank.name then
Tlist[output.name][tank.name] = {
amount = destTank.amount
}
end
end
end
end
-- 更新轮询索引
lastOutput[priority] = idx % #group + 1
else
log(LOG_LEVEL.WARNING, "推送失败到 "..output.name, "Push failed to "..output.name)
end
else
if not destination then
log(LOG_LEVEL.ERROR, "错误: 输出容器不存在: "..output.name, "Error: Output container not found: "..output.name)
else
log(LOG_LEVEL.WARNING, "错误: 输出容器不支持流体: "..output.name, "Error: Output container does not support fluids: "..output.name)
end
end
::next_output::
idx = idx % #group + 1
attempts = attempts + 1
until attempts >= #group or amountToTransfer <= 0
if amountToTransfer <= 0 then break end
end
if amountToTransfer > 0 then
log(LOG_LEVEL.WARNING, string.format("警告: 仍有 %dmB %s 未传输", amountToTransfer, tank.name),
string.format("Warning: Still %dmB %s not transferred", amountToTransfer, tank.name))
end
else
if tank.amount == 0 then
log(LOG_LEVEL.INFO, "槽位 "..tankIndex.." 无流体", "Tank "..tankIndex.." has no fluid")
else
log(LOG_LEVEL.INFO, "流体 "..tank.name.." 被输入容器 "..inputName.." 过滤",
"Fluid "..tank.name.." filtered by input container "..inputName)
end
end
else
log(LOG_LEVEL.WARNING, "警告: 槽位 "..tankIndex.." 的数据无效", "Warning: Invalid data for tank "..tankIndex)
end
end
::continue::
end
return transferred
end
-- 执行任务
local function executeTask(task)
if task.type == "item" then
return executeItemTask(task)
elseif task.type == "fluid" then
return executeFluidTask(task)
else
log(LOG_LEVEL.ERROR, "错误: 未知任务类型: "..(task.type or "nil"), "Error: Unknown task type: "..(task.type or "nil"))
return false
end
end
-- 主任务循环
local function taskLoop()
log(LOG_LEVEL.INFO, "任务循环开始", "Task loop started")
while true do
for i, task in ipairs(tasks) do
log(LOG_LEVEL.INFO, "开始任务 #"..i, "Starting task #"..i)
local success, result = pcall(executeTask, task)
if not success then
log(LOG_LEVEL.ERROR, "任务 #"..i.." 错误: "..tostring(result), "Task #"..i.." error: "..tostring(result))
elseif result then
log(LOG_LEVEL.SUCCESS, "任务 #"..i.." 成功执行", "Task #"..i.." executed successfully")
else
log(LOG_LEVEL.SKIPPED, "任务 #"..i.." 未执行传输", "Task #"..i.." no transfer performed")
end
end
os.sleep(settings.delay)
end
end
-- 命令处理
local function processCommand(cmd)
local args = {}
for word in cmd:gmatch("%S+") do
table.insert(args, word)
end
if #args == 0 then return end
local command = args[1]:lower()
if command == "add" and #args >= 2 then
-- 合并参数为JSON字符串
local jsonStr = table.concat(args, " ", 2)
local success, task = pcall(textutils.unserializeJSON, jsonStr)
if success and type(task) == "table" then
-- 验证任务类型
if not task.type then
echo("错误: 任务缺少类型字段", "Error: Task missing type field")
return
end
if task.type ~= "item" and task.type ~= "fluid" then
echo("错误: 无效的任务类型: "..task.type, "Error: Invalid task type: "..task.type)
return
end
-- 验证输入输出
if not task.input or not task.output then
echo("错误: 任务缺少输入或输出字段", "Error: Task missing input or output fields")
return
end
table.insert(tasks, task)
saveConfig()
echo("任务添加成功", "Task added successfully")
else
echo("错误: 无效的JSON格式", "Error: Invalid JSON format")
end
elseif command == "list" then
if #tasks == 0 then
echo("没有配置任务", "No tasks configured")
else
for i, task in ipairs(tasks) do
echo("任务 #"..i.." ("..task.type..")", "Task #"..i.." ("..task.type..")")
echo(" 输入容器:", " Input containers:")
for name, config in pairs(task.input) do
local cacheStr = config.cache and "缓存" or "非缓存"
local cacheStrEn = config.cache and "cache" or "non-cache"
-- 显示过滤规则
if config.whitelist then
local items = {}
for itemName, itemConfig in pairs(config.whitelist) do
if itemConfig.count and itemConfig.count > 0 then
table.insert(items, itemName.." (count="..itemConfig.count..")")
else
table.insert(items, itemName)
end
end
echo(" "..name.." ("..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
" "..name.." ("..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
elseif config.blacklist then
local items = {}
for itemName in pairs(config.blacklist) do
table.insert(items, itemName)
end
echo(" 极"..name.." ("..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
" "..name.." ("..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
else
echo(" "..name.." ("..cacheStr..")", " "..name.." ("..cacheStrEn..")")
end
end
echo(" 输出容器:", " Output containers:")
for name, config in pairs(task.output) do
local cacheStr = config.cache and "缓存" or "非缓存"
local cacheStrEn = config.cache and "cache" or "non-cache"
local priority = config.priority or 1
-- 显示过滤规则
if config.whitelist then
local items = {}
for itemName, itemConfig in pairs(config.whitelist) do
if itemConfig.count and itemConfig.count > 0 then
table.insert(items, itemName.." (count="..itemConfig.count..")")
else
table.insert(items, itemName)
end
end
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 白名单: "..table.concat(items, ", ")..")",
" "..name.." (priority:"..priority..", "..cacheStrEn..", whitelist: "..table.concat(items, ", ")..")")
elseif config.blacklist then
local items = {}
for itemName in pairs(config.blacklist) do
table.insert(items, itemName)
end
echo(" "..name.." (优先级:"..priority..", "..cacheStr..", 黑名单: "..table.concat(items, ", ")..")",
" "..name.." (priority:"..priority..", "..cacheStrEn..", blacklist: "..table.concat(items, ", ")..")")
else
echo(" "..name.." (优先级:"..priority..", "..cacheStr..")",
" "..name.." (priority:"..priority..", "..cacheStrEn..")")
end
end
end
end
elseif command == "remove" and #args >= 2 then
local index = tonumber(args[2])
if index and tasks[index] then
table.remove(tasks, index)
saveConfig()
echo("任务 "..index.." 已移除", "Task "..index.." removed")
else
echo("无效的任务索引", "Invalid task index")
end
elseif command == "log" then
if #args >= 2 then
if args[2]:lower() == "on" then
settings.logEnabled = true
saveConfig()
echo("日志已启用", "Logging enabled")
elseif args[2]:lower() == "off" then
settings.logEnabled = false
saveConfig()
echo("日志已禁用", "Logging disabled")
else
echo("无效参数,使用 'log on' 或 'log off'", "Invalid argument, use 'log on' or 'log off'")
end
else
echo("当前日志状态: "..(settings.logEnabled and "启用" or "禁用"),
"Current log status: "..(settings.logEnabled and "enabled" or "disabled"))
end
-- 添加 set 命令
elseif command == "set" and #args >= 3 then
local key = args[2]:lower()
local value = args[3]
if key == "log" then
if value == "true" then
settings.logEnabled = true
saveConfig()
echo("日志已启用", "Logging enabled")
elseif value == "false" then
settings.logEnabled = false
saveConfig()
echo("日志已禁用", "Logging disabled")
else
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
end
elseif key == "zh" then
if value == "true" then
settings.zhlog = true
saveConfig()
echo("中文日志已启用", "Chinese log enabled")
elseif value == "false" then
settings.zhlog = false
saveConfig()
echo("中文日志已禁用", "Chinese log disabled")
else
echo("无效值,使用 'true' 或 'false'", "Invalid value, use 'true' or 'false'")
end
elseif key == "delay" then
local numValue = tonumber(value)
if numValue and numValue > 0 then
settings.delay = numValue
saveConfig()
echo("轮询延迟已设置为: "..numValue.." 秒", "Polling delay set to: "..numValue.." seconds")
else
echo("无效值请输入大于0的数字", "Invalid value, please enter a number greater than 0")
end
else
echo("无效设置项,可用选项: logEnabled, zhlog, delay", "Invalid setting, available options: logEnabled, zhlog, delay")
end
elseif command == "help" then
echo("可用命令:", "Available commands:")
echo("add <JSON任务> - 添加新任务", "add <JSON task> - Add new task")
echo(" 示例: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}",
" Example: add {\"type\":\"item\",\"input\":{\"left\":{\"cache\":true,\"whitelist\":{\"minecraft:stone\":{\"count\":64}}}},\"output\":{\"right\":{\"priority\":1,\"blacklist\":{\"minecraft:dirt\":{}}}}")
echo("list - 列出所有任务", "list - List all tasks")
echo("remove <索引> - 移除任务", "remove <index> - Remove task")
echo("log [on|off] - 启用/禁用日志", "log [on|off] - Enable/disable logging")
echo("set <key> <value> - 修改设置", "set <key> <value> - Modify settings")
echo(" 可用键: logEnabled (true/false), zhlog (true/false), delay (数字)", " Available keys: logEnabled (true/false), zhlog (true/false), delay (number)")
echo("exit - 退出程序", "exit - Exit program")
elseif command == "exit" then
echo("正在退出程序", "Exiting program")
os.sleep(0.5)
error("程序终止", 0)
end
end
-- 初始化
echo("海龟物流系统 v5.0 已启动。输入 'help' 查看命令。",
"Turtle Logistics System v5.0 started. Type 'help' for commands.")
-- 主循环
parallel.waitForAll(
function()
while true do
write("> ")
local command = read()
processCommand(command)
end
end,
taskLoop
)
{"type":"item","input":{"create:basin_6":{"cache":true,"whitelist":{"minecraft:iron_ingot":{}}}},"output":{"create:basin_7":{"priority":1,"whitelist":{"minecraft:iron_ingot":{"count":10}}}}}
我在输出容器的白名单定理了数量,但程序不遵循数量参数而是直接传输了所有

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html data-theme="emerald">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>web</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

2193
Logistics/V5/web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
{
"name": "web",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.14",
"axios": "^1.12.2",
"daisyui": "^5.3.7",
"tailwindcss": "^4.1.14",
"vue": "^3.5.22"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"vite": "^7.1.7"
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,548 @@
<template>
<div id="app" class="container mx-auto px-4 py-8">
<div class="text-center mb-8">
<h1 class="text-3xl font-bold text-gray-800">物流系统 v5.0</h1>
<p class="text-gray-600">任务配置创建工具</p>
<!-- 新增项目信息和下载区域 -->
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mt-4 max-w-2xl mx-auto">
<div class="flex flex-col sm:flex-row justify-between items-center gap-4">
<div class="text-left w-30">
<h3 class="font-semibold text-blue-800 mb-1">项目信息</h3>
<a href="http://git.liulikeji.cn/xingluo/CCTweaked-utils/src/branch/main/Logistics/V5"
target="_blank"
class="text-blue-600 hover:text-blue-800 underline text-sm">
📁项目仓库
</a>
</div>
<div class="flex items-center gap-2">
<code class="bg-gray-800 text-green-200 px-3 py-1 rounded text-sm">wget http://git.liulikeji.cn/xingluo/CCTweaked-utils/raw/branch/main/Logistics/V5/startup.lua</code>
<button @click="copyDownloadCommand" class="btn btn-sm btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
</svg>
复制
</button>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
<div class="flex flex-col md:flex-row gap-6">
<!-- 左侧配置区域 -->
<div class="w-full md:w-2/3">
<div class="mb-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">创建新任务</h2>
<!-- 传输类型选择 -->
<div class="mb-6">
<label class="block text-gray-700 font-medium mb-2">传输类型</label>
<div class="flex gap-4">
<label class="flex items-center">
<input type="radio" v-model="task.type" value="item" class="radio radio-primary mr-2">
<span>物品传输</span>
</label>
<label class="flex items-center">
<input type="radio" v-model="task.type" value="fluid" class="radio radio-primary mr-2">
<span>流体传输</span>
</label>
</div>
</div>
<!-- 输入容器配置 -->
<div class="container-box">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-800">输入容器</h3>
<button @click="addInputContainer" class="btn btn-sm btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
添加容器
</button>
</div>
<div v-for="(input, index) in task.input" :key="'input-'+index" class="mb-6 p-4 bg-gray-50 rounded-lg">
<div class="flex justify-between items-center mb-3">
<h4 class="font-medium text-gray-700">容器 #{{ index + 1 }}</h4>
<button @click="removeInputContainer(index)" class="btn btn-xs btn-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="mb-4">
<label class="block text-gray-700 mb-1">容器名称</label>
<input v-model="input.containerName" type="text" placeholder="例如: chest_left" class="input input-bordered w-full">
</div>
<div class="mb-4">
<label class="flex items-center">
<input type="checkbox" v-model="input.cache" class="checkbox checkbox-primary mr-2">
<span>启用缓存模式</span>
</label>
</div>
<div class="mb-4">
<label class="block text-gray-700 mb-2">过滤规则</label>
<div class="tabs mb-3">
<a class="tab tab-bordered" :class="{'tab-active': input.filterType === 'whitelist'}" @click="input.filterType = 'whitelist'">白名单</a>
<a class="tab tab-bordered" :class="{'tab-active': input.filterType === 'blacklist'}" @click="input.filterType = 'blacklist'">黑名单</a>
</div>
<div v-if="input.filterType === 'whitelist'">
<div class="mb-2">
<div class="item-entry" v-for="(item, itemIndex) in input.whitelist" :key="'input-wl-'+itemIndex">
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:stone)" class="input input-bordered flex-grow">
<button @click="removeWhitelistItem(input.whitelist, itemIndex)" class="btn btn-xs btn-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<button @click="addWhitelistItem(input.whitelist)" class="btn btn-sm btn-outline">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
添加白名单项目
</button>
</div>
<div v-else>
<div class="mb-2">
<div class="item-entry" v-for="(item, itemIndex) in input.blacklist" :key="'input-bl-'+itemIndex">
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:dirt)" class="input input-bordered flex-grow">
<button @click="removeBlacklistItem(input.blacklist, itemIndex)" class="btn btn-xs btn-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<button @click="addBlacklistItem(input.blacklist)" class="btn btn-sm btn-outline">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
添加黑名单项目
</button>
</div>
</div>
</div>
</div>
<!-- 输出容器配置 -->
<div class="container-box">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-800">输出容器</h3>
<button @click="addOutputContainer" class="btn btn-sm btn-primary">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
添加容器
</button>
</div>
<div v-for="(output, index) in task.output" :key="'output-'+index" class="mb-6 p-4 bg-gray-50 rounded-lg">
<div class="flex justify-between items-center mb-3">
<h4 class="font-medium text-gray-700">容器 #{{ index + 1 }}</h4>
<button @click="removeOutputContainer(index)" class="btn btn-xs btn-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="mb-4">
<label class="block text-gray-700 mb-1">容器名称</label>
<input v-model="output.containerName" type="text" placeholder="例如: chest_right" class="input input-bordered w-full">
</div>
<div class="mb-4">
<label class="block text-gray-700 mb-1">优先级</label>
<input v-model="output.priority" type="number" min="1" class="input input-bordered w-full">
</div>
<div class="mb-4">
<label class="flex items-center">
<input type="checkbox" v-model="output.cache" class="checkbox checkbox-primary mr-2">
<span>启用缓存模式</span>
</label>
</div>
<div class="mb-4">
<label class="block text-gray-700 mb-2">过滤规则</label>
<div class="tabs mb-3">
<a class="tab tab-bordered" :class="{'tab-active': output.filterType === 'whitelist'}" @click="output.filterType = 'whitelist'">白名单</a>
<a class="tab tab-bordered" :class="{'tab-active': output.filterType === 'blacklist'}" @click="output.filterType = 'blacklist'">黑名单</a>
</div>
<div v-if="output.filterType === 'whitelist'">
<div class="mb-2">
<div class="item-entry" v-for="(item, itemIndex) in output.whitelist" :key="'output-wl-'+itemIndex">
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:stone)" class="input input-bordered flex-grow">
<input v-model="item.count" type="number" min="1" placeholder="数量" class="input input-bordered w-24">
<button @click="removeWhitelistItem(output.whitelist, itemIndex)" class="btn btn-xs btn-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<button @click="addWhitelistItem(output.whitelist)" class="btn btn-sm btn-outline">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
添加白名单项目
</button>
</div>
<div v-else>
<div class="mb-2">
<div class="item-entry" v-for="(item, itemIndex) in output.blacklist" :key="'output-bl-'+itemIndex">
<input v-model="item.name" type="text" placeholder="物品ID (例如: minecraft:dirt)" class="input input-bordered flex-grow">
<button @click="removeBlacklistItem(output.blacklist, itemIndex)" class="btn btn-xs btn-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
<button @click="addBlacklistItem(output.blacklist)" class="btn btn-sm btn-outline">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
添加黑名单项目
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 右侧预览区域 -->
<div class="w-full md:w-1/3">
<div class="sticky top-4">
<div class="bg-white rounded-xl shadow-lg p-6">
<h3 class="text-lg font-semibold text-gray-800 mb-4">配置预览</h3>
<div class="mb-4">
<label class="block text-gray-700 mb-2">生成的任务配置</label>
<pre class="bg-gray-800 text-green-200 p-4 rounded-lg overflow-auto max-h-96">{{ formattedJson }}</pre>
</div>
<!-- 新增命令文本区域 -->
<div class="mb-4">
<label class="block text-gray-700 mb-2">命令文本</label>
<pre class="bg-gray-800 text-blue-200 p-4 rounded-lg overflow-auto">{{ commandString }}</pre>
</div>
<div class="flex gap-2">
<button @click="copyToClipboard" class="btn btn-primary flex-grow">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
</svg>
复制命令
</button>
<button @click="resetForm" class="btn btn-outline">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
重置
</button>
</div>
<div v-if="copySuccess" class="mt-3 text-center text-green-500">
命令已复制到剪贴板
</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 mt-4">
<h3 class="text-lg font-semibold text-gray-800 mb-4">使用说明</h3>
<ul class="text-gray-600 space-y-2">
<li class="flex items-start">
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">1</div>
<span>配置输入和输出容器信息</span>
</li>
<li class="flex items-start">
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">2</div>
<span>设置过滤规则白名单或黑名单</span>
</li>
<li class="flex items-start">
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">3</div>
<span>为输出容器设置优先级</span>
</li>
<li class="flex items-start">
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">4</div>
<span>复制生成的命令文本</span>
</li>
<li class="flex items-start">
<div class="bg-blue-100 text-blue-800 rounded-full w-6 h-6 flex items-center justify-center mr-2 flex-shrink-0">5</div>
<span>在物流系统中粘贴并执行命令</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
task: {
type: 'item',
input: [
{
containerName: '',
cache: false,
filterType: 'whitelist',
whitelist: [{ name: '' }],
blacklist: [{ name: '' }]
}
],
output: [
{
containerName: '',
priority: 1,
cache: false,
filterType: 'whitelist',
whitelist: [{ name: '', count: null }],
blacklist: [{ name: '' }]
}
]
},
copySuccess: false,
copyDownloadSuccess: false
}
},
computed: {
taskConfig() {
const task = JSON.parse(JSON.stringify(this.task));
// 转换输入容器
const inputObj = {};
task.input.forEach(container => {
const containerName = container.containerName || 'unnamed_container';
inputObj[containerName] = {
cache: container.cache
};
// 处理白名单 - 只有当有实际内容时才添加
const whitelistItems = container.whitelist.filter(item => item.name && item.name.trim() !== '');
if (whitelistItems.length > 0) {
inputObj[containerName].whitelist = {};
whitelistItems.forEach(item => {
inputObj[containerName].whitelist[item.name] = {};
});
}
// 处理黑名单 - 只有当有实际内容时才添加
const blacklistItems = container.blacklist.filter(item => item.name && item.name.trim() !== '');
if (blacklistItems.length > 0) {
inputObj[containerName].blacklist = {};
blacklistItems.forEach(item => {
inputObj[containerName].blacklist[item.name] = {};
});
}
});
// 转换输出容器
const outputObj = {};
task.output.forEach(container => {
const containerName = container.containerName || 'unnamed_container';
outputObj[containerName] = {
priority: parseInt(container.priority) || 1,
cache: container.cache
};
// 处理白名单 - 只有当有实际内容时才添加
const whitelistItems = container.whitelist.filter(item => item.name && item.name.trim() !== '');
if (whitelistItems.length > 0) {
outputObj[containerName].whitelist = {};
whitelistItems.forEach(item => {
if (item.count) {
outputObj[containerName].whitelist[item.name] = { count: parseInt(item.count) };
} else {
outputObj[containerName].whitelist[item.name] = {};
}
});
}
// 处理黑名单 - 只有当有实际内容时才添加
const blacklistItems = container.blacklist.filter(item => item.name && item.name.trim() !== '');
if (blacklistItems.length > 0) {
outputObj[containerName].blacklist = {};
blacklistItems.forEach(item => {
outputObj[containerName].blacklist[item.name] = {};
});
}
});
return {
type: task.type,
input: inputObj,
output: outputObj
};
},
formattedJson() {
return JSON.stringify(this.taskConfig, null, 2);
},
compressedJson() {
return JSON.stringify(this.taskConfig);
},
commandString() {
return `add ${this.compressedJson}`;
},
downloadCommand() {
return 'wget http://git.liulikeji.cn/xingluo/CCTweaked-utils/raw/branch/main/Logistics/V5/startup.lua';
}
},
methods: {
addInputContainer() {
this.task.input.push({
containerName: '',
cache: false,
filterType: 'whitelist',
whitelist: [{ name: '' }],
blacklist: [{ name: '' }]
});
},
removeInputContainer(index) {
if (this.task.input.length > 1) {
this.task.input.splice(index, 1);
}
},
addOutputContainer() {
this.task.output.push({
containerName: '',
priority: 1,
cache: false,
filterType: 'whitelist',
whitelist: [{ name: '', count: null }],
blacklist: [{ name: '' }]
});
},
removeOutputContainer(index) {
if (this.task.output.length > 1) {
this.task.output.splice(index, 1);
}
},
addWhitelistItem(list) {
list.push(this.task.type === 'item' ? { name: '', count: null } : { name: '' });
},
removeWhitelistItem(list, index) {
if (list.length > 1) {
list.splice(index, 1);
}
},
addBlacklistItem(list) {
list.push({ name: '' });
},
removeBlacklistItem(list, index) {
if (list.length > 1) {
list.splice(index, 1);
}
},
copyToClipboard() {
const textarea = document.createElement('textarea');
textarea.value = this.commandString;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
this.copySuccess = true;
setTimeout(() => {
this.copySuccess = false;
}, 3000);
},
copyDownloadCommand() {
const textarea = document.createElement('textarea');
textarea.value = this.downloadCommand;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
this.copyDownloadSuccess = true;
setTimeout(() => {
this.copyDownloadSuccess = false;
}, 3000);
},
resetForm() {
this.task = {
type: 'item',
input: [
{
containerName: '',
cache: false,
filterType: 'whitelist',
whitelist: [{ name: '' }],
blacklist: [{ name: '' }]
}
],
output: [
{
containerName: '',
priority: 1,
cache: false,
filterType: 'whitelist',
whitelist: [{ name: '', count: null }],
blacklist: [{ name: '' }]
}
]
};
}
}
}
</script>
<style>
.container-box {
background-color: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.item-entry {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.drag-handle {
cursor: move;
opacity: 0.6;
}
.drag-handle:hover {
opacity: 1;
}
.transition-all {
transition: all 0.3s ease;
}
</style>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,5 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
createApp(App).mount('#app')

View File

@@ -0,0 +1,18 @@
import { createRouter, createWebHistory } from 'vue-router';
import BlueprintEditorView from '@/views/BlueprintEditorView.vue';
const routes = [
{
path: '/blueprint-editor',
name: 'BlueprintEditor',
component: BlueprintEditorView
},
// 其他路由...
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;

View File

@@ -0,0 +1,2 @@
@import "tailwindcss";
@plugin "daisyui";

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import tailwindcss from '@tailwindcss/vite';
// https://vite.dev/config/
export default defineConfig({
plugins: [vue(),tailwindcss()],
})

View File

@@ -56,18 +56,23 @@ local function parseItemRequirements(input)
-- Split key-value pairs -- Split key-value pairs
for pair in content:gmatch("[^,]+") do for pair in content:gmatch("[^,]+") do
-- 支持JSON格式: "key":value
local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)") local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)")
if key and value then if key and value then
requirements[key] = tonumber(value) requirements[key] = tonumber(value)
end end
end end
else else
-- Simple format: item:minCount -- 只支持等号格式: item=count
for item in input:gmatch("%S+") do for item in input:gmatch("%S+") do
local name, minCount = item:match("([^:]+):(%d+)") -- 匹配等号格式: modid:itemid=count
local name, minCount = item:match("([^=]+)=(%d+)")
-- 如果匹配到等号格式
if name and minCount then if name and minCount then
requirements[name] = tonumber(minCount) requirements[name] = tonumber(minCount)
else else
-- 没有等号,视为只有物品名
requirements[item] = 0 -- No minimum requirement requirements[item] = 0 -- No minimum requirement
end end
end end
@@ -87,18 +92,23 @@ local function parseFluidRequirements(input)
-- Split key-value pairs -- Split key-value pairs
for pair in content:gmatch("[^,]+") do for pair in content:gmatch("[^,]+") do
-- 支持JSON格式: "key":value
local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)") local key, value = pair:match("[\"']?(.-)[\"']?%s*:%s*(%d+)")
if key and value then if key and value then
requirements[key] = tonumber(value) requirements[key] = tonumber(value)
end end
end end
else else
-- Simple format: fluid:minAmount -- 只支持等号格式: fluid=minAmount
for fluid in input:gmatch("%S+") do for fluid in input:gmatch("%S+") do
local name, minAmount = fluid:match("([^:]+):(%d+)") -- 匹配等号格式: fluid_name=minAmount
local name, minAmount = fluid:match("([^=]+)=(%d+)")
-- 如果匹配到等号格式
if name and minAmount then if name and minAmount then
requirements[name] = tonumber(minAmount) requirements[name] = tonumber(minAmount)
else else
-- 没有等号,视为只有流体名
requirements[fluid] = 0 -- No minimum requirement requirements[fluid] = 0 -- No minimum requirement
end end
end end
@@ -488,7 +498,7 @@ local function processCommand(cmd)
local fluidsList = {} local fluidsList = {}
for name, minAmount in pairs(task.fluids) do for name, minAmount in pairs(task.fluids) do
if minAmount > 0 then if minAmount > 0 then
table.insert(fluidsList, name..":"..minAmount.."mB") table.insert(fluidsList, name.."="..minAmount.."mB")
else else
table.insert(fluidsList, name) table.insert(fluidsList, name)
end end
@@ -502,7 +512,7 @@ local function processCommand(cmd)
local itemsList = {} local itemsList = {}
for name, minCount in pairs(task.items) do for name, minCount in pairs(task.items) do
if minCount > 0 then if minCount > 0 then
table.insert(itemsList, name..":"..minCount) table.insert(itemsList, name.."="..minCount)
else else
table.insert(itemsList, name) table.insert(itemsList, name)
end end
@@ -533,13 +543,11 @@ local function processCommand(cmd)
print("Available commands:") print("Available commands:")
print("add <source> <destination> [items] - Add item transfer task") print("add <source> <destination> [items] - Add item transfer task")
print(" Items format:") print(" Items format:")
print(" Simple: item1[:minCount] item2[:minCount] ...") print(" Simple: item1[=minCount] item2[=minCount] ...")
print(" JSON-like: {\"item1\":minCount, \"item2\":minCount}")
print(" Set minCount to 0 to transfer all available items") print(" Set minCount to 0 to transfer all available items")
print("addfluid <source> <destination> [fluids] - Add fluid transfer task") print("addfluid <source> <destination> [fluids] - Add fluid transfer task")
print(" Fluids format:") print(" Fluids format:")
print(" Simple: fluid1[:minAmount] fluid2[:minAmount] ...") print(" Simple: fluid1[=minAmount] fluid2[:minAmount] ...")
print(" JSON-like: {\"fluid1\":minAmount, \"fluid2\":minAmount}")
print(" Set minAmount to 0 to transfer all available fluid") print(" Set minAmount to 0 to transfer all available fluid")
print("list - List all tasks") print("list - List all tasks")
print("remove <index> - Remove task") print("remove <index> - Remove task")