diff --git a/demo_convert_bdx_byDelay.py b/demo_convert_bdx_byDelay.py
index 13f47dc..ae0028f 100644
--- a/demo_convert_bdx_byDelay.py
+++ b/demo_convert_bdx_byDelay.py
@@ -71,14 +71,16 @@ def operation(
):
print(f'正在操作{i}')
convertion.convert(midipath + '/' + i, outpath)
- convertion.toBDXfile_withDelay(
- 1,
- authorname if authorname != '' else input('请输入作者:'),
- isProgress,
- heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')),
- volume if volume != '' else float(input('请输入音量(0-1]:')),
- speed if speed != '' else float(input('请输入速度倍率:')),
- player if player != '' else input('请输入玩家选择器:'),
+ print(
+ convertion.toBDXfile_withDelay(
+ 1,
+ authorname if authorname != '' else input('请输入作者:'),
+ isProgress,
+ heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')),
+ volume if volume != '' else float(input('请输入音量(0-1]:')),
+ speed if speed != '' else float(input('请输入速度倍率:')),
+ player if player != '' else input('请输入玩家选择器:'),
+ )
)
@@ -90,12 +92,14 @@ if os.path.isdir(midipath):
threading.Thread(target=operation, args=(i,)).start()
else:
convertion.convert(midipath, outpath)
- convertion.toBDXfile_withDelay(
- 1,
- authorname if authorname != '' else input('请输入作者:'),
- isProgress,
- heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')),
- volume if volume != '' else float(input('请输入音量(0-1]:')),
- speed if speed != '' else float(input('请输入速度倍率:')),
- player if player != '' else input('请输入玩家选择器:'),
+ print(
+ convertion.toBDXfile_withDelay(
+ 1,
+ authorname if authorname != '' else input('请输入作者:'),
+ isProgress,
+ heightmax if heightmax != '' else int(input('请输入指令结构最大生成高度:')),
+ volume if volume != '' else float(input('请输入音量(0-1]:')),
+ speed if speed != '' else float(input('请输入速度倍率:')),
+ player if player != '' else input('请输入玩家选择器:'),
+ )
)
diff --git a/docs/新手答疑指南.md b/docs/新手答疑指南.md
index c7b6ac2..5bb609b 100644
--- a/docs/新手答疑指南.md
+++ b/docs/新手答疑指南.md
@@ -143,9 +143,10 @@
## 第三部分 关于其他问题
-如果你在仔细阅读以上内容之后,仍然感觉有所困惑,可以加我们的[QQ群](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)来提问,但是请注意,**由于开发者们都很忙,群友们的耐心也都有限,所以恳请大家提出有意义的问题。**如果您在使用过程中遇到报错,希望你能够把完整的报错内容复制给我们,而不是截一半的屏幕或者在群内大呼小叫。以下两张图给了你很好的解释说明。
+如果你在仔细阅读以上内容之后,仍然感觉有所困惑,可以加我们的[QQ群](https://jq.qq.com/?_wv=1027&k=hpeRxrYr)来提问,但是请注意,**由于开发者们都很忙,群友们的耐心也都有限,所以恳请大家提出有意义的问题。**
+如果您在使用过程中遇到报错,希望你能够把完整的报错内容复制给我们,而不是截一半的屏幕或者在群内大呼小叫。以下两张图给了你很好的解释说明。
-*图片暂无。*
+
| |
这里附一个关于[智慧地提问](https://github.com/tvvocold/How-To-Ask-Questions-The-Smart-Way)的文章,大家可以看看。
diff --git a/magicDemo.py b/magicDemo.py
index 6a7c239..53ecc85 100644
--- a/magicDemo.py
+++ b/magicDemo.py
@@ -35,12 +35,14 @@ languages = {
"WhetherArgEntering": "是否为文件夹内文件的转换统一参数[是(1) 或 否(0)]",
"EnterArgs": "请输入转换参数",
"noteofArgs": "注:文件夹内的全部midi将统一以此参数转换",
- "ChooseSbReset": "是否自动重置计分板[是(1) 或 否(0)]",
+ "EnterVolume": "请输入音量大小(0~1)",
+ "EnterSpeed": "请输入速度倍率",
+ "WhetherPgb": "是否自动生成进度条[是(1) 或 否(0)]",
"WhetherCstmProgressBar": "是否自定义进度条[是(1) 或 否(0)]",
"EnterProgressBarStyle": "请输入进度条样式",
"EnterSbName": "请输入计分板名称",
- "EnterVolume": "请输入音量大小(0~1)",
- "EnterSpeed": "请输入速度倍率",
+ "EnterSelecter": "请输入播放者选择器",
+ "WhetherSbReset": "是否自动重置计分板[是(1) 或 否(0)]",
"EnterAuthor": "请输入作者",
"EnterMaxHeight": "请输入指令结构最大生成高度",
"ErrEnter": "输入错误",
@@ -48,8 +50,13 @@ languages = {
"Dealing": "正在处理",
"FileNotFound": "文件(夹)不存在",
"ChooseOutPath": "请输入结果输出路径",
- "EnterSelecter": "请输入播放者选择器",
"Saying": "言·论",
+ "Failed": "失败",
+ "CmdLength": "指令数量",
+ "MaxDelay": "曲目时间(游戏刻)",
+ "PlaceSize": "结构占用大小",
+ "LastPos": "最末方块坐标",
+ "PressEnterExit": "请按下回车键退出。",
}
}
@@ -121,20 +128,50 @@ MainConsole.rule(
title="[bold #AB70FF]Welcome to Independent Musicreater Convernter", characters="-"
)
+nowYang = datetime.datetime.now()
+nowYin = zhdate.ZhDate.from_datetime(nowYang)
-# 显示箴言部分
-MainConsole.print(
- "[#121110 on #F0F2F4]"
- + random.choice(
- requests.get(
- "https://gitee.com/EillesWan/Musicreater/raw/master/resources/myWords.txt"
- )
- .text.strip("\r\n")
- .split("\r\n")
- ),
- style="#121110 on #F0F2F4",
- justify="center",
-)
+if nowYang.month == 8 and nowYang.day == 6:
+ # 诸葛八卦生日
+ MainConsole.print(
+ "[#7DB5F0 on #121110]今天可不是催更的日子!\n诸葛亮与八卦阵{}岁生日快乐!".format(nowYang.year - 2009),
+ style="#7DB5F0 on #121110",
+ justify="center",
+ )
+elif nowYang.month == 4 and nowYang.day == 3:
+ # 金羿生日快乐
+ MainConsole.print(
+ "[#0089F2 on #F0F2F4]今天就不要催更啦!\n金羿{}岁生日快乐!".format(nowYang.year - 2006),
+ style="#0089F2 on #F0F2F4",
+ justify="center",
+ )
+elif nowYin.lunar_month == 12 and nowYin.lunar_day == 30:
+ MainConsole.print(
+ "[#FF3432 on #121110]除夕到了,你是否与家人共处,融融其乐?",
+ style="#FF3432 on #121110",
+ justify="center",
+ )
+elif nowYin.leap_month == 1 and nowYin.lunar_day in range(1, 9):
+ MainConsole.print(
+ "[#FFF642 on #FF3432]春节快乐!\n在你使用音·创的时候,是不是也要去感受一下喜庆的氛围呢?",
+ style="#FFF642 on #FF3432",
+ justify="center",
+ )
+else:
+ # 显示箴言部分
+ MainConsole.print(
+ "[#121110 on #F0F2F4]{}".format(
+ random.choice(
+ requests.get(
+ 'https://gitee.com/EillesWan/Musicreater/raw/master/resources/myWords.txt'
+ )
+ .text.strip('\r\n')
+ .split('\r\n')
+ )
+ ),
+ style="#121110 on #F0F2F4",
+ justify="center",
+ )
from typing import Any, Literal, Optional, TextIO
@@ -262,7 +299,12 @@ def ipt(
return MainConsole.input("", password=password, stream=stream)
-def formatipt(notice: str, fun, errnote: str = "", *extraArg):
+def formatipt(
+ notice: str,
+ fun,
+ errnote: str = f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}",
+ *extraArg,
+):
'''循环输入,以某种格式
notice: 输入时的提示
fun: 格式函数
@@ -306,7 +348,7 @@ outpath = formatipt(
f"{_('ChooseOutPath')}{_(':')}",
os.path.exists,
f"{_('FileNotFound')}{_(',')}{_('Re-Enter')}{_('.')}",
-).lower()
+)[0].lower()
# 选择输出格式
@@ -314,17 +356,16 @@ while True:
fileFormat = ipt(f"{_('ChooseFileFormat')}{_(':')}").lower()
if fileFormat in ('0', 'mcpack'):
fileFormat = 0
- prt(_("EnterArgs"))
- if len(midis) > 1:
- prt(_("noteofArgs"))
+ playerFormat = 1
+ break
elif fileFormat in ('1', 'bdx'):
fileFormat = 1
while True:
playerFormat = ipt(f"{_('ChoosePlayer')}{_(':')}").lower()
- if playerFormat in ('0', '延迟'):
+ if playerFormat in ('0', '延迟', 'delay'):
playerFormat = 0
- elif playerFormat in ('1', '计分板'):
+ elif playerFormat in ('1', '计分板', 'scoreboard'):
playerFormat = 1
else:
prt(f"{_('ErrEnter')}{_(',')}{_('Re-Enter')}{_('.')}")
@@ -336,8 +377,84 @@ while True:
break
-if fileFormat == 0:
- pass
+# 真假字符串判断
+def boolstr(sth: str) -> bool:
+ try:
+ return bool(int(sth))
+ except:
+ if str(sth).lower() == 'true':
+ return True
+ elif str(sth).lower() == 'false':
+ return False
+ else:
+ raise "布尔字符串啊?"
-MainConsole.input()
+prompts = []
+# 提示语 检测函数 错误提示语
+for args in [
+ (
+ f'{_("EnterVolume")}{_(":")}',
+ float,
+ ),
+ (
+ f'{_("EnterSpeed")}{_(":")}',
+ float,
+ ),
+ (
+ f'{_("WhetherPgb")}{_(":")}',
+ boolstr,
+ ),
+ (
+ f'{_("EnterSbName")}{_(":")}',
+ str,
+ )
+ if playerFormat == 1
+ else (
+ f'{_("EnterSelecter")}{_(":")}',
+ str,
+ ),
+ (
+ f'{_("WhetherSbReset")}{_(":")}',
+ boolstr,
+ )
+ if playerFormat == 1
+ else (),
+ (
+ f'{_("EnterAuthor")}{_(":")}',
+ str,
+ )
+ if fileFormat == 1
+ else (),
+ (
+ f'{_("EnterMaxHeight")}{_(":")}',
+ int,
+ )
+ if fileFormat == 1
+ else (),
+]:
+ if args:
+ prompts.append(formatipt(*args)[1])
+
+
+newLine = '\n'
+conversion = midiConvert()
+for singleMidi in midis:
+ conversion.convert(singleMidi, outpath)
+ conversion_result = (
+ conversion.tomcpack(2, *prompts)
+ if fileFormat == 0
+ else (
+ conversion.toBDXfile(2, *prompts)
+ if playerFormat == 1
+ else conversion.toBDXfile_withDelay(1, *prompts)
+ )
+ )
+ if conversion_result[0]:
+ prt(
+ f"{newLine}{_('Dealing')} {singleMidi} {_(':')}{newLine} {_('CmdLength')}{_(':')}{conversion_result[1]}{_(',')}{_('MaxDelay')}{_(':')}{conversion_result[2]}{f'''{_(',')}{_('PlaceSize')}{_(':')}{conversion_result[3]}{_(',')}{_('LastPos')}{_(':')}{conversion_result[4]}''' if fileFormat == 1 else ''}"
+ )
+ else:
+ prt(f"{_('Failed')}")
+
+ipt(_("PressEnterExit"))
diff --git a/msctPkgver/__init__.py b/msctPkgver/__init__.py
index 0e8ae79..710cb38 100644
--- a/msctPkgver/__init__.py
+++ b/msctPkgver/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-"""一个简单的基于音·创的我的世界音频操作工具"""
+"""一个简单的我的世界音频转换库"""
# 音·创 开发交流群 861684859
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
diff --git a/msctPkgver/main.py b/msctPkgver/main.py
index e170eb9..fe50f22 100644
--- a/msctPkgver/main.py
+++ b/msctPkgver/main.py
@@ -19,39 +19,16 @@ Copyright 2023 all the developers of Musicreater
Terms & Conditions: ../Lisence.md
"""
-import os
import mido
import brotli
import json
import uuid
import shutil
-import math
+from .utils import *
from .exceptions import *
-def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
- """使用compression指定的算法打包目录为zip文件\n
- 默认算法为DEFLATED(8),可用算法如下:\n
- STORED = 0\n
- DEFLATED = 8\n
- BZIP2 = 12\n
- LZMA = 14\n
- """
- import zipfile
-
- zipf = zipfile.ZipFile(outFilename, "w", compression)
- pre_len = len(os.path.dirname(sourceDir))
- for parent, dirnames, filenames in os.walk(sourceDir):
- for filename in filenames:
- if filename == exceptFile:
- continue
- pathfile = os.path.join(parent, filename)
- arcname = pathfile[pre_len:].strip(os.path.sep) # 相对路径
- zipf.write(pathfile, arcname)
- zipf.close()
-
-
class SingleNote:
def __init__(
self, instrument: int, pitch: int, velocity, startTime: int, lastTime: int
@@ -147,7 +124,7 @@ class midiConvert:
self._toCmdList_withDelay_m1,
]
- def convert(self, midiFile: str, outputPath: str):
+ def convert(self, midiFile: str, outputPath: str, oldExeFormat: bool = True):
"""转换前需要先运行此函数来获取基本信息"""
self.midiFile = midiFile
@@ -165,6 +142,13 @@ class midiConvert:
self.midFileName = os.path.splitext(os.path.basename(self.midiFile))[0]
"""文件名,不含路径且不含后缀"""
+ self.exeHead = (
+ "execute {} ~ ~ ~ "
+ if oldExeFormat
+ else "execute as {} at @s positioned ~ ~ ~ run "
+ )
+ """execute指令的应用,两个版本提前决定。"""
+
def __Inst2soundIDwithX(self, instrumentID):
"""返回midi的乐器ID对应的我的世界乐器名,对于音域转换算法,如下:
2**( ( msg.note - 60 - X ) / 12 ) 即为MC的音高,其中
@@ -368,7 +352,7 @@ class midiConvert:
except:
return ("note.bd", 7)
except:
- print("WARN", "无法导入打击乐器列表动态链接库,可能是不支持当前及其环境,打击乐器使用Dislink算法代替。")
+ print("WARN", "无法使用打击乐器列表库,可能是不支持当前环境,打击乐器使用Dislink算法代替。")
if instrumentID == 55:
return ("note.cow_bell", 5)
elif instrumentID in [41, 43, 45]:
@@ -381,6 +365,106 @@ class midiConvert:
def __score2time(self, score: int):
return str(int(int(score / 20) / 60)) + ":" + str(int(int(score / 20) % 60))
+ # def __formProgressBar(
+ # self,
+ # maxscore: int,
+ # scoreboardname: str,
+ # progressbar: tuple = (
+ # r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]",
+ # ("§e=§r", "§7=§r"),
+ # ),
+ # ) -> list:
+
+ # pgsstyle = progressbar[0]
+ # """用于被替换的进度条原始样式"""
+
+ # """
+ # | 标识符 | 指定的可变量 |
+ # |---------|----------------|
+ # | `%%N` | 乐曲名(即传入的文件名)|
+ # | `%%s` | 当前计分板值 |
+ # | `%^s` | 计分板最大值 |
+ # | `%%t` | 当前播放时间 |
+ # | `%^t` | 曲目总时长 |
+ # | `%%%` | 当前进度比率 |
+ # | `_` | 用以表示进度条占位|
+ # """
+
+ # def __replace(
+ # s: str, tobeReplaced: str, replaceWith: str, times: int, other: str
+ # ):
+ # if times == 0:
+ # return s.replace(tobeReplaced, other)
+ # if times == s.count(tobeReplaced):
+ # return s.replace(tobeReplaced, replaceWith)
+ # result = ""
+ # t = 0
+ # for i in s:
+ # if i == tobeReplaced:
+ # if t < times:
+ # result += replaceWith
+ # t += 1
+ # else:
+ # result += other
+ # else:
+ # result += i
+
+ # return result
+
+ # idlist = {
+ # r"%%N": self.midFileName,
+ # r"%%s": r"%%s",
+ # r"%^s": str(maxscore),
+ # r"%%t": r"%%t",
+ # r"%^t": self.__score2time(maxscore),
+ # r"%%%": r"%%%",
+ # }
+
+ # ids = {}
+
+ # for i, j in idlist.items():
+ # if i != j:
+ # if i in pgsstyle:
+ # pgsstyle = pgsstyle.replace(i, j)
+ # else:
+ # if i in pgsstyle:
+ # ids[i] = True
+ # else:
+ # ids[i] = False
+
+ # del idlist
+
+ # pgblength = pgsstyle.count("_")
+ # """进度条的“条”长度"""
+
+ # finalprgsbar = []
+
+ # for i in range(maxscore):
+ # nowstr = pgsstyle
+ # if ids[r"%%s"]:
+ # nowstr = nowstr.replace(r"%%s", str(i + 1))
+ # if ids[r"%%t"]:
+ # nowstr = nowstr.replace(r"%%t", self.__score2time(i + 1))
+ # if ids[r"%%%"]:
+ # nowstr = nowstr.replace(
+ # r"%%%", str(int((i + 1) / maxscore * 10000) / 100) + "%"
+ # )
+
+ # countof_s = int((i + 1) / maxscore * pgblength)
+
+ # finalprgsbar.append(
+ # "title @a[scores={"
+ # + scoreboardname
+ # + "="
+ # + str(i + 1)
+ # + "}] actionbar "
+ # + __replace(
+ # nowstr, "_", progressbar[1][0], countof_s, progressbar[1][1]
+ # )
+ # )
+
+ # return finalprgsbar
+
def __formProgressBar(
self,
maxscore: int,
@@ -405,153 +489,158 @@ class midiConvert:
| `%%%` | 当前进度比率 |
| `_` | 用以表示进度条占位|
"""
+ perEach = maxscore / pgsstyle.count('_')
- def __replace(
- s: str, tobeReplaced: str, replaceWith: str, times: int, other: str
- ):
- if times == 0:
- return s.replace(tobeReplaced, other)
- if times == s.count(tobeReplaced):
- return s.replace(tobeReplaced, replaceWith)
- result = ""
- t = 0
- for i in s:
- if i == tobeReplaced:
- if t < times:
- result += replaceWith
- t += 1
- else:
- result += other
- else:
- result += i
+ result = []
- return result
+ if r"%^s" in pgsstyle:
+ pgsstyle = pgsstyle.replace(r"%^s", str(maxscore))
- idlist = {
- r"%%N": self.midFileName,
- r"%%s": r"%%s",
- r"%^s": str(maxscore),
- r"%%t": r"%%t",
- r"%^t": self.__score2time(maxscore),
- r"%%%": r"%%%",
- }
+ if r"%^t" in pgsstyle:
+ pgsstyle = pgsstyle.replace(r"%^t", self.__score2time(maxscore))
- ids = {}
-
- for i, j in idlist.items():
- if i != j:
- if i in pgsstyle:
- pgsstyle = pgsstyle.replace(i, j)
- else:
- if i in pgsstyle:
- ids[i] = True
- else:
- ids[i] = False
-
- del idlist
-
- pgblength = pgsstyle.count("_")
- """进度条的“条”长度"""
-
- finalprgsbar = []
-
- for i in range(maxscore):
- nowstr = pgsstyle
- if ids[r"%%s"]:
- nowstr = nowstr.replace(r"%%s", str(i + 1))
- if ids[r"%%t"]:
- nowstr = nowstr.replace(r"%%t", self.__score2time(i + 1))
- if ids[r"%%%"]:
- nowstr = nowstr.replace(
- r"%%%", str(int((i + 1) / maxscore * 10000) / 100) + "%"
+ def replaceBar(i):
+ try:
+ return pgsstyle.replace('_', progressbar[1][0], i + 1).replace(
+ '_', progressbar[1][1]
)
+ except:
+ return pgsstyle.replace('_', progressbar[1][0], i + 1)
- countof_s = int((i + 1) / maxscore * pgblength)
-
- finalprgsbar.append(
- "title @a[scores={"
- + scoreboardname
- + "="
- + str(i + 1)
- + "}] actionbar "
- + __replace(
- nowstr, "_", progressbar[1][0], countof_s, progressbar[1][1]
+ sbnpc = scoreboardname[:2]
+ if r"%%%" in pgsstyle:
+ result.append(
+ "scoreboard objectives add {}PercT dummy \"百分比计算\"".format(sbnpc)
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players set MaxScore {} {}".format(
+ scoreboardname, maxscore
+ )
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players set MaxScore {} 100".format(scoreboardname)
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} = @s {}".format(
+ sbnpc + "PercT", scoreboardname
+ )
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} *= n100 {}".format(
+ sbnpc + "PercT", scoreboardname
+ )
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} /= MaxScore {}".format(
+ sbnpc + "PercT", scoreboardname
)
)
- return finalprgsbar
+ if r"%%t" in pgsstyle:
+ result.append(
+ "scoreboard objectives add {}TMinT dummy \"时间计算:分\"".format(sbnpc)
+ )
+ result.append(
+ "scoreboard objectives add {}TSecT dummy \"时间计算:秒\"".format(sbnpc)
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players set n20 {} 20".format(scoreboardname)
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players set n60 {} 60".format(scoreboardname)
+ )
- def __formCMDblk(
- self,
- command: str,
- particularValue: int,
- impluse: int = 0,
- condition: bool = False,
- needRedstone: bool = True,
- tickDelay: int = 0,
- customName: str = "",
- executeOnFirstTick: bool = False,
- trackOutput: bool = True,
- ):
- """
- 使用指定项目返回指定的指令方块放置指令项
- :param command: `str`
- 指令
- :param particularValue:
- 方块特殊值,即朝向
- :0 下 无条件
- :1 上 无条件
- :2 z轴负方向 无条件
- :3 z轴正方向 无条件
- :4 x轴负方向 无条件
- :5 x轴正方向 无条件
- :6 下 无条件
- :7 下 无条件
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} = @s {}".format(
+ sbnpc + "TMinT", scoreboardname
+ )
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} /= n20 {}".format(
+ sbnpc + "TMinT", scoreboardname
+ )
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} /= n60 {}".format(
+ sbnpc + "TMinT", scoreboardname
+ )
+ )
- :8 下 有条件
- :9 上 有条件
- :10 z轴负方向 有条件
- :11 z轴正方向 有条件
- :12 x轴负方向 有条件
- :13 x轴正方向 有条件
- :14 下 有条件
- :14 下 有条件
- 注意!此处特殊值中的条件会被下面condition参数覆写
- :param impluse: `int 0|1|2`
- 方块类型
- 0脉冲 1循环 2连锁
- :param condition: `bool`
- 是否有条件
- :param needRedstone: `bool`
- 是否需要红石
- :param tickDelay: `int`
- 执行延时
- :param customName: `str`
- 悬浮字
- lastOutput: `str`
- 上次输出字符串,注意此处需要留空
- :param executeOnFirstTick: `bool`
- 执行第一个已选项(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
- :param trackOutput: `bool`
- 是否输出
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} = @s {}".format(
+ sbnpc + "TSecT", scoreboardname
+ )
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} /= n20 {}".format(
+ sbnpc + "TSecT", scoreboardname
+ )
+ )
+ result.append(
+ self.exeHead.format("@a[scores={" + scoreboardname + "=1..}]")
+ + "scoreboard players operation @s {} %= n60 {}".format(
+ sbnpc + "TSecT", scoreboardname
+ )
+ )
- :return:str
- """
- block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
+ for i in range(pgsstyle.count('_')):
+ npgstl = (
+ replaceBar(i).replace(r"%%N", self.midFileName)
+ if r"%%N" in pgsstyle
+ else replaceBar(i)
+ )
+ if r"%%s" in npgstl:
+ npgstl = npgstl.replace(
+ r"%%s",
+ '"},{"score":{"name":"*","objective":"'
+ + scoreboardname
+ + '"}},{"text":"',
+ )
+ if r"%%%" in npgstl:
+ npgstl = npgstl.replace(
+ r"%%%",
+ '"},{"score":{"name":"*","objective":"'
+ + sbnpc
+ + 'PercT"}},{"text":"%',
+ )
+ if r"%%t" in npgstl:
+ npgstl = npgstl.replace(
+ r"%%t",
+ r'"},{"score":{"name":"*","objective":"{-}TMinT"}},{"text":":"},{"score":{"name":"*","objective":"{-}TSecT"}},{"text":"'.replace(
+ r"{-}", sbnpc
+ ),
+ )
+ result.append(
+ self.exeHead.format(
+ "@a[scores={"
+ + scoreboardname
+ + f"={int(i * perEach)}..{math.ceil((i + 1) * perEach)}"
+ + "}]"
+ )
+ + r'titleraw @s actionbar {"rawtext":[{"text":"'
+ + npgstl
+ + r'"}]}'
+ )
- for i in [
- impluse.to_bytes(4, byteorder="big", signed=False),
- bytes(command, encoding="utf-8") + b"\x00",
- bytes(customName, encoding="utf-8") + b"\x00",
- bytes("", encoding="utf-8") + b"\x00",
- tickDelay.to_bytes(4, byteorder="big", signed=True),
- executeOnFirstTick.to_bytes(1, byteorder="big"),
- trackOutput.to_bytes(1, byteorder="big"),
- condition.to_bytes(1, byteorder="big"),
- needRedstone.to_bytes(1, byteorder="big"),
- ]:
- block += i
- return block
+ if r"%%%" in pgsstyle:
+ result.append("scoreboard objectives remove {}PercT".format(sbnpc))
+ if r"%%t" in pgsstyle:
+ result.append("scoreboard objectives remove {}TMinT".format(sbnpc))
+ result.append("scoreboard objectives remove {}TSecT".format(sbnpc))
+
+ return result
def _toCmdList_m1(
self, scoreboardname: str = "mscplay", volume: float = 1.0, speed: float = 1.0
@@ -601,17 +690,6 @@ class midiConvert:
else:
soundID, _X = self.__Inst2soundIDwithX(instrumentID)
- # /playsound [player: target] [position: x y z]
- # [volume: float] [pitch: float] [minimumVolume: float]
-
- # volume_d = 1 / volume - 1
- # if volume_d == 0.0:
- # volume_d = ""
- # command_now = "playsound {0} @a[scores={{{1}}}] ~ ~{2} ~ {3} {4}" \
- # .format(soundID, "{}={}".format(scoreboardname,
- # nowscore), volume_d,
- # msg.velocity, 2 ** ((msg.note - 60 - _X) / 12))
- # singleTrack.append(command_now)
singleTrack.append(
"execute @a[scores={"
+ str(scoreboardname)
@@ -650,86 +728,74 @@ class midiConvert:
# 一个midi中仅有16通道 我们通过通道来识别而不是音轨
channels = [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
+ microseconds = 0
+
# 我们来用通道统计音乐信息
- for i, track in enumerate(self.midi.tracks):
+ for msg in self.midi:
- microseconds = 0
+ if msg.time != 0:
+ try:
+ microseconds += msg.time * tempo / self.midi.ticks_per_beat
+ except NameError:
+ raise NotDefineTempoError("计算当前分数时出错 未定义参量 Tempo")
- for msg in track:
+ if msg.is_meta:
+ if msg.type == "set_tempo":
+ tempo = msg.tempo
+ else:
- if msg.time != 0:
- try:
- microseconds += msg.time * tempo / self.midi.ticks_per_beat
- except NameError:
- raise NotDefineTempoError("计算当前分数时出错 未定义参量 Tempo")
+ try:
+ msg.channel
+ channelMsg = True
+ except:
+ channelMsg = False
+ if channelMsg:
+ if msg.channel > 15:
+ raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
- if msg.is_meta:
- if msg.type == "set_tempo":
- tempo = msg.tempo
- else:
+ if msg.type == "program_change":
+ channels[msg.channel].append(("PgmC", msg.program, microseconds))
- try:
- msg.channel
- channelMsg = True
- except:
- channelMsg = False
- if channelMsg:
- if msg.channel > 15:
- raise ChannelOverFlowError(f"当前消息 {msg} 的通道超限(≤15)")
+ elif msg.type == "note_on" and msg.velocity != 0:
+ channels[msg.channel].append(
+ ("NoteS", msg.note, msg.velocity, microseconds)
+ )
- if msg.type == "program_change":
- channels[msg.channel].append(
- ("PgmC", msg.program, microseconds)
- )
-
- elif msg.type == "note_on" and msg.velocity != 0:
- channels[msg.channel].append(
- ("NoteS", msg.note, msg.velocity, microseconds)
- )
-
- elif (msg.type == "note_on" and msg.velocity == 0) or (
- msg.type == "note_off"
- ):
- channels[msg.channel].append(("NoteE", msg.note, microseconds))
+ elif (msg.type == "note_on" and msg.velocity == 0) or (
+ msg.type == "note_off"
+ ):
+ channels[msg.channel].append(("NoteE", msg.note, microseconds))
"""整合后的音乐通道格式
每个通道包括若干消息元素其中逃不过这三种:
1 切换乐器消息
-
("PgmC", 切换后的乐器ID: int, 距离演奏开始的毫秒)
2 音符开始消息
-
("NoteS", 开始的音符ID, 力度(响度), 距离演奏开始的毫秒)
3 音符结束消息
-
("NoteS", 结束的音符ID, 距离演奏开始的毫秒)"""
tracks = []
cmdAmount = 0
maxScore = 0
- CheckFirstChannel = False
# 此处 我们把通道视为音轨
- for track in channels:
+ for i in range(len(channels)):
# 如果当前通道为空 则跳过
- if not track:
+ if not channels[i]:
continue
- if channels.index(track) == 0:
- CheckFirstChannel = True
- SpecialBits = False
- elif channels.index(track) == 9:
+ if i == 9:
SpecialBits = True
else:
- CheckFirstChannel = False
SpecialBits = False
nowTrack = []
- for msg in track:
+ for msg in channels[i]:
if msg[0] == "PgmC":
InstID = msg[1]
@@ -744,12 +810,12 @@ class midiConvert:
maxScore = max(maxScore, score_now)
nowTrack.append(
- "execute @a[scores={"
- + str(scoreboardname)
- + "="
- + str(score_now)
- + "}"
- + f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / MaxVolume - 1} ~ {msg[2] * (0.7 if CheckFirstChannel else 0.9)} {2 ** ((msg[1] - 60 - _X) / 12)}"
+ self.exeHead.format(
+ "@a[scores=({}={})]".format(scoreboardname, score_now)
+ .replace('(', r"{")
+ .replace(")", r"}")
+ )
+ + f"playsound {soundID} @s ^ ^ ^{1 / MaxVolume - 1} {msg[2]/128} {2 ** ((msg[1] - 60 - _X) / 12)}"
)
cmdAmount += 1
@@ -949,7 +1015,7 @@ class midiConvert:
volume: float = 1.0,
speed: float = 1.0,
player: str = "@a",
- isMixedWithPrograssBar=False,
+ # isMixedWithPrograssBar=False,
) -> list:
"""
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表,并输出每个音符之后的延迟
@@ -967,11 +1033,11 @@ class midiConvert:
volume = 0.001
# 此处是对于仅有 True 的参数和自定义参数的判断
- if isMixedWithPrograssBar == True:
- isMixedWithPrograssBar = (
- r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]",
- ("§e=§r", "§7=§r"),
- )
+ # if isMixedWithPrograssBar == True:
+ # isMixedWithPrograssBar = (
+ # r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]",
+ # ("§e=§r", "§7=§r"),
+ # )
for i, track in enumerate(self.midi.tracks):
@@ -1001,74 +1067,12 @@ class midiConvert:
f"execute {player} ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}",
]
- allticks = list(tracks.keys())
-
- if isMixedWithPrograssBar:
-
- pgsstyle = isMixedWithPrograssBar[0]
- """用于被替换的进度条原始样式"""
-
- """
- | 标识符 | 指定的可变量 |
- |---------|----------------|
- | `%%N` | 乐曲名(即传入的文件名)|
- | `%%s` | 当前计分板值 |
- | `%^s` | 计分板最大值 |
- | `%%t` | 当前播放时间 |
- | `%^t` | 曲目总时长 |
- | `%%%` | 当前进度比率 |
- | `_` | 用以表示进度条占位|
- """
-
- def __replace(
- s: str, tobeReplaced: str, replaceWith: str, times: int, other: str
- ):
- if times == 0:
- return s.replace(tobeReplaced, other)
- if times == s.count(tobeReplaced):
- return s.replace(tobeReplaced, replaceWith)
- result = ""
- t = 0
- for i in s:
- if i == tobeReplaced:
- if t < times:
- result += replaceWith
- t += 1
- else:
- result += other
- else:
- result += i
-
- return result
-
- idlist = {
- r"%%N": self.midFileName,
- r"%%s": r"%%s",
- r"%^s": str(allticks[-1]),
- r"%%t": r"%%t",
- r"%^t": self.__score2time(allticks[-1]),
- r"%%%": r"%%%",
- }
-
- ids = {}
-
- for i, j in idlist.items():
- if i != j:
- if i in pgsstyle:
- pgsstyle = pgsstyle.replace(i, j)
- else:
- if i in pgsstyle:
- ids[i] = True
- else:
- ids[i] = False
-
- del idlist
-
- pgblength = pgsstyle.count("_")
- """进度条的“条”长度"""
-
results = []
+ allticks = list(tracks.keys())
+ # if isMixedWithPrograssBar:
+ # results.append("scoreboard objectives add {}")
+
for i in range(len(allticks)):
if i != 0:
for j in range(len(tracks[allticks[i]])):
@@ -1082,54 +1086,17 @@ class midiConvert:
for j in range(len(tracks[allticks[i]])):
results.append((tracks[allticks[i]][j], allticks[i]))
- if isMixedWithPrograssBar:
-
- nowstr = pgsstyle
- if ids[r"%%s"]:
- nowstr = nowstr.replace(r"%%s", str(allticks[i] + 1))
- if ids[r"%%t"]:
- nowstr = nowstr.replace(r"%%t", self.__score2time(allticks[i] + 1))
- if ids[r"%%%"]:
- nowstr = nowstr.replace(
- r"%%%",
- str(int((allticks[i] + 1) / allticks[-1] * 10000) / 100) + "%",
- )
-
- countof_s = int((allticks[i] + 1) / allticks[-1] * pgblength)
-
- titlenow = __replace(
- nowstr,
- "_",
- isMixedWithPrograssBar[1][0],
- countof_s,
- isMixedWithPrograssBar[1][1],
- )
-
- results.append(
- (
- f"title {player} actionbar {titlenow}",
- 0,
- )
- )
-
- return results
-
- def __fillSquareSideLength(self, total: int, maxHeight: int):
- """给定总方块数量和最大高度,返回所构成的图形外切正方形的边长
- :param total: 总方块数量
- :param maxHeight: 最大高度
- :return: 外切正方形的边长 int"""
- return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
+ return results, max(allticks)
def tomcpack(
self,
method: int = 1,
- isAutoReset: bool = False,
- progressbar=None,
- scoreboardname: str = "mscplay",
volume: float = 1.0,
speed: float = 1.0,
- ) -> bool or tuple:
+ progressbar=None,
+ scoreboardname: str = "mscplay",
+ isAutoReset: bool = False,
+ ) -> tuple:
"""
使用method指定的转换算法,将midi转换为我的世界mcpack格式的包
:param method: 转换算法
@@ -1255,18 +1222,18 @@ class midiConvert:
shutil.rmtree(f"{self.outputPath}/temp/")
- return (True, f"转换完成,总长度{maxlen}")
+ return (True, maxlen, maxscore)
def toBDXfile(
self,
method: int = 1,
- author: str = "Eilles",
- progressbar=False,
- maxheight: int = 64,
- scoreboardname: str = "mscplay",
volume: float = 1.0,
speed: float = 1.0,
+ progressbar=False,
+ scoreboardname: str = "mscplay",
isAutoReset: bool = False,
+ author: str = "Eilles",
+ maxheight: int = 64,
):
"""
使用method指定的转换算法,将midi转换为BDX结构文件
@@ -1302,25 +1269,6 @@ class midiConvert:
+ b" & Musicreater\x00\x01command_block\x00"
)
- key = {
- "x": (b"\x0f", b"\x0e"),
- "y": (b"\x11", b"\x10"),
- "z": (b"\x13", b"\x12"),
- }
- """key存储了方块移动指令的数据,其中可以用key[x|y|z][0|1]来表示xyz的减或增"""
- x = "x"
- y = "y"
- z = "z"
-
- _sideLength = self.__fillSquareSideLength(totalcount, maxheight)
-
- yforward = True
- zforward = True
-
- nowy = 0
- nowz = 0
- nowx = 0
-
commands = []
for track in cmdlist:
@@ -1336,61 +1284,32 @@ class midiConvert:
+ scoreboardname,
)
+ cmdBytes, size, finalPos = toBDXbytes([(i,0)for i in commands], maxheight - 1)
# 此处是对于仅有 True 的参数和自定义参数的判断
if progressbar:
- if progressbar == True:
- commands += self.__formProgressBar(maxScore, scoreboardname)
- else:
- commands += self.__formProgressBar(
- maxScore, scoreboardname, progressbar
- )
-
- for cmd in commands:
- _bytes += self.__formCMDblk(
- cmd,
- (1 if yforward else 0)
- if (
- ((nowy != 0) and (not yforward))
- or ((yforward) and (nowy != (maxheight - 1)))
- )
- else (3 if zforward else 2)
- if (
- ((nowz != 0) and (not zforward))
- or ((zforward) and (nowz != _sideLength))
- )
- else 5,
- impluse=2,
- condition=False,
- needRedstone=False,
- tickDelay=0,
- customName="",
- executeOnFirstTick=False,
- trackOutput=True,
+ pgbBytes, pgbSize, pgbNowPos = toBDXbytes(
+ [
+ (i, 0)
+ for i in (
+ self.__formProgressBar(maxScore, scoreboardname)
+ if progressbar == True
+ else self.__formProgressBar(
+ maxScore, scoreboardname, progressbar
+ )
+ )
+ ],
+ maxheight - 1,
)
+ _bytes += pgbBytes
+ _bytes += move(y, -pgbNowPos[1])
+ _bytes += move(z, -pgbNowPos[2])
+ _bytes += move(x, 2)
- nowy += 1 if yforward else -1
+ size[0] += 2 + pgbSize[0]
+ size[1] = max(size[1], pgbSize[1])
+ size[2] = max(size[2], pgbSize[2])
- if ((nowy >= maxheight) and (yforward)) or ((nowy < 0) and (not yforward)):
- nowy -= 1 if yforward else -1
-
- yforward = not yforward
-
- nowz += 1 if zforward else -1
-
- if ((nowz > _sideLength) and (zforward)) or (
- (nowz < 0) and (not zforward)
- ):
- nowz -= 1 if zforward else -1
- zforward = not zforward
- _bytes += key[x][1]
- nowx += 1
- else:
-
- _bytes += key[z][int(zforward)]
-
- else:
-
- _bytes += key[y][int(yforward)]
+ _bytes += cmdBytes
with open(
os.path.abspath(os.path.join(self.outputPath, f"{self.midFileName}.bdx")),
@@ -1398,17 +1317,17 @@ class midiConvert:
) as f:
f.write(brotli.compress(_bytes + b"XE"))
- return (True, totalcount, maxScore, (nowx, maxheight, _sideLength))
+ return (True, totalcount, maxScore, size, finalPos)
def toBDXfile_withDelay(
self,
method: int = 1,
- author: str = "Eilles",
- progressbar=False,
- maxheight: int = 64,
volume: float = 1.0,
speed: float = 1.0,
+ progressbar=False,
player: str = "@a",
+ author: str = "Eilles",
+ maxheight: int = 64,
):
"""
使用method指定的转换算法,将midi转换为BDX结构文件
@@ -1422,12 +1341,14 @@ class midiConvert:
:return 成功与否,成功返回(True,未经过压缩的源,结构占用大小),失败返回(False,str失败原因)
"""
- try:
- cmdlist = self.methods_byDelay[method - 1](
- volume, speed, player, progressbar
- )
- except:
- return (False, f"无法找到算法ID{method}对应的转换算法")
+ # try:
+ cmdlist, maxdelay = self.methods_byDelay[method - 1](
+ volume,
+ speed,
+ player,
+ )
+ # except Exception as E:
+ # return (False, f"无法找到算法ID{method}对应的转换算法\n{E}")
if not os.path.exists(self.outputPath):
os.makedirs(self.outputPath)
@@ -1444,71 +1365,53 @@ class midiConvert:
+ b" & Musicreater\x00\x01command_block\x00"
)
- key = {
- "x": (b"\x0f", b"\x0e"),
- "y": (b"\x11", b"\x10"),
- "z": (b"\x13", b"\x12"),
- }
- """key存储了方块移动指令的数据,其中可以用key[x|y|z][0|1]来表示xyz的减或增"""
- x = "x"
- y = "y"
- z = "z"
-
- _sideLength = self.__fillSquareSideLength(len(cmdlist), maxheight)
-
- yforward = True
- zforward = True
-
- nowy = 0
- nowz = 0
- nowx = 0
-
- for cmd, delay in cmdlist:
- _bytes += self.__formCMDblk(
- cmd,
- (1 if yforward else 0)
- if (
- ((nowy != 0) and (not yforward))
- or ((yforward) and (nowy != (maxheight - 1)))
- )
- else (3 if zforward else 2)
- if (
- ((nowz != 0) and (not zforward))
- or ((zforward) and (nowz != _sideLength))
- )
- else 5,
- impluse=2,
- condition=False,
- needRedstone=False,
- tickDelay=delay,
- customName="",
- executeOnFirstTick=False,
- trackOutput=True,
+ # 此处是对于仅有 True 的参数和自定义参数的判断
+ if progressbar == True:
+ progressbar = (
+ r"▶ %%N [ %%s/%^s %%% __________ %%t|%^t ]",
+ ("§e=§r", "§7=§r"),
)
- nowy += 1 if yforward else -1
+ cmdBytes, size, finalPos = toBDXbytes(cmdlist, maxheight - 1)
- if ((nowy >= maxheight) and (yforward)) or ((nowy < 0) and (not yforward)):
- nowy -= 1 if yforward else -1
+ if progressbar:
+ scbname = self.midFileName[:5] + "Pgb"
+ _bytes += formCMDblk(
+ r"scoreboard objectives add {} dummy {}播放用".replace(r"{}", scbname),
+ 1,
+ customName="初始化进度条",
+ )
+ _bytes += move(z, 2)
+ _bytes += formCMDblk(
+ r"scoreboard players add {} {} 1".format(player, scbname),
+ 1,
+ 1,
+ customName="显示进度条并加分",
+ )
+ _bytes += move(y, 1)
+ pgbBytes, pgbSize, pgbNowPos = toBDXbytes(
+ [
+ (i, 0)
+ for i in self.__formProgressBar(maxdelay, scbname, progressbar)
+ ],
+ maxheight - 1,
+ )
+ _bytes += pgbBytes
+ _bytes += move(y, -1 - pgbNowPos[1])
+ _bytes += move(z, -2 - pgbNowPos[2])
+ _bytes += move(x, 2)
+ _bytes += formCMDblk(
+ r"scoreboard players reset {} {}".format(player, scbname),
+ 1,
+ customName="置零进度条",
+ )
+ _bytes += move(y, 1)
+ size[0] += 2 + pgbSize[0]
+ size[1] = max(size[1], pgbSize[1])
+ size[2] = max(size[2], pgbSize[2])
- yforward = not yforward
-
- nowz += 1 if zforward else -1
-
- if ((nowz > _sideLength) and (zforward)) or (
- (nowz < 0) and (not zforward)
- ):
- nowz -= 1 if zforward else -1
- zforward = not zforward
- _bytes += key[x][1]
- nowx += 1
- else:
-
- _bytes += key[z][int(zforward)]
-
- else:
-
- _bytes += key[y][int(yforward)]
+ size[1] += 1
+ _bytes += cmdBytes
with open(
os.path.abspath(os.path.join(self.outputPath, f"{self.midFileName}.bdx")),
@@ -1516,7 +1419,7 @@ class midiConvert:
) as f:
f.write(brotli.compress(_bytes + b"XE"))
- return (True, _bytes, (nowx, maxheight, _sideLength))
+ return (True, len(cmdlist), maxdelay, size, finalPos)
# def isProgressBar(pgbarLike:str):
diff --git a/msctPkgver/utils.py b/msctPkgver/utils.py
new file mode 100644
index 0000000..54708e2
--- /dev/null
+++ b/msctPkgver/utils.py
@@ -0,0 +1,209 @@
+import math
+import os
+
+key = {
+ "x": [b"\x0f", b"\x0e", b"\x1c", b"\x14", b"\x15"],
+ "y": [b"\x11", b"\x10", b"\x1d", b"\x16", b"\x17"],
+ "z": [b"\x13", b"\x12", b"\x1e", b"\x18", b"\x19"],
+}
+"""key存储了方块移动指令的数据,其中可以用key[x|y|z][0|1]来表示xyz的减或增
+而key[][2+]是用来增加指定数目的"""
+
+x = "x"
+y = "y"
+z = "z"
+
+
+def move(axis: str, value: int):
+ if value == 0:
+ return b''
+ if abs(value) == 1:
+ return key[axis][0 if value == -1 else 1]
+
+ pointer = sum(
+ [
+ 1 if i else 0
+ for i in (
+ value != -1,
+ value < -1 or value > 1,
+ value < -128 or value > 127,
+ value < -32768 or value > 32767,
+ )
+ ]
+ )
+
+ return key[axis][pointer] + value.to_bytes(2 ** (pointer - 2), 'big', signed=True)
+
+
+def makeZip(sourceDir, outFilename, compression=8, exceptFile=None):
+ """使用compression指定的算法打包目录为zip文件\n
+ 默认算法为DEFLATED(8),可用算法如下:\n
+ STORED = 0\n
+ DEFLATED = 8\n
+ BZIP2 = 12\n
+ LZMA = 14\n
+ """
+ import zipfile
+
+ zipf = zipfile.ZipFile(outFilename, "w", compression)
+ pre_len = len(os.path.dirname(sourceDir))
+ for parent, dirnames, filenames in os.walk(sourceDir):
+ for filename in filenames:
+ if filename == exceptFile:
+ continue
+ pathfile = os.path.join(parent, filename)
+ arcname = pathfile[pre_len:].strip(os.path.sep) # 相对路径
+ zipf.write(pathfile, arcname)
+ zipf.close()
+
+
+def formCMDblk(
+ command: str,
+ particularValue: int,
+ impluse: int = 0,
+ condition: bool = False,
+ needRedstone: bool = True,
+ tickDelay: int = 0,
+ customName: str = "",
+ executeOnFirstTick: bool = False,
+ trackOutput: bool = True,
+):
+ """
+ 使用指定项目返回指定的指令方块放置指令项
+ :param command: `str`
+ 指令
+ :param particularValue:
+ 方块特殊值,即朝向
+ :0 下 无条件
+ :1 上 无条件
+ :2 z轴负方向 无条件
+ :3 z轴正方向 无条件
+ :4 x轴负方向 无条件
+ :5 x轴正方向 无条件
+ :6 下 无条件
+ :7 下 无条件
+
+ :8 下 有条件
+ :9 上 有条件
+ :10 z轴负方向 有条件
+ :11 z轴正方向 有条件
+ :12 x轴负方向 有条件
+ :13 x轴正方向 有条件
+ :14 下 有条件
+ :14 下 有条件
+ 注意!此处特殊值中的条件会被下面condition参数覆写
+ :param impluse: `int 0|1|2`
+ 方块类型
+ 0脉冲 1循环 2连锁
+ :param condition: `bool`
+ 是否有条件
+ :param needRedstone: `bool`
+ 是否需要红石
+ :param tickDelay: `int`
+ 执行延时
+ :param customName: `str`
+ 悬浮字
+ lastOutput: `str`
+ 上次输出字符串,注意此处需要留空
+ :param executeOnFirstTick: `bool`
+ 执行第一个已选项(循环指令方块是否激活后立即执行,若为False,则从激活时起延迟后第一次执行)
+ :param trackOutput: `bool`
+ 是否输出
+
+ :return:str
+ """
+ block = b"\x24" + particularValue.to_bytes(2, byteorder="big", signed=False)
+
+ for i in [
+ impluse.to_bytes(4, byteorder="big", signed=False),
+ bytes(command, encoding="utf-8") + b"\x00",
+ bytes(customName, encoding="utf-8") + b"\x00",
+ bytes("", encoding="utf-8") + b"\x00",
+ tickDelay.to_bytes(4, byteorder="big", signed=True),
+ executeOnFirstTick.to_bytes(1, byteorder="big"),
+ trackOutput.to_bytes(1, byteorder="big"),
+ condition.to_bytes(1, byteorder="big"),
+ needRedstone.to_bytes(1, byteorder="big"),
+ ]:
+ block += i
+ return block
+
+
+def __fillSquareSideLength(total: int, maxHeight: int):
+ """给定总方块数量和最大高度,返回所构成的图形外切正方形的边长
+ :param total: 总方块数量
+ :param maxHeight: 最大高度
+ :return: 外切正方形的边长 int"""
+ return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
+
+
+def toBDXbytes(
+ commands: list,
+ maxheight: int = 64,
+):
+ """
+ :param commands: 指令列表(指令, 延迟)
+ :param maxheight: 生成结构最大高度
+ :return 成功与否,成功返回(True,未经过压缩的源,结构占用大小),失败返回(False,str失败原因)
+ """
+
+ _sideLength = __fillSquareSideLength(len(commands), maxheight)
+ _bytes = b''
+
+ yforward = True
+ zforward = True
+
+ nowy = 0
+ nowz = 0
+ nowx = 0
+
+ for cmd, delay in commands:
+ _bytes += formCMDblk(
+ cmd,
+ (1 if yforward else 0)
+ if (
+ ((nowy != 0) and (not yforward))
+ or ((yforward) and (nowy != (maxheight - 1)))
+ )
+ else (3 if zforward else 2)
+ if (
+ ((nowz != 0) and (not zforward))
+ or ((zforward) and (nowz != _sideLength))
+ )
+ else 5,
+ impluse=2,
+ condition=False,
+ needRedstone=False,
+ tickDelay=delay,
+ customName="",
+ executeOnFirstTick=False,
+ trackOutput=True,
+ )
+
+ nowy += 1 if yforward else -1
+
+ if ((nowy >= maxheight) and (yforward)) or ((nowy < 0) and (not yforward)):
+ nowy -= 1 if yforward else -1
+
+ yforward = not yforward
+
+ nowz += 1 if zforward else -1
+
+ if ((nowz > _sideLength) and (zforward)) or ((nowz < 0) and (not zforward)):
+ nowz -= 1 if zforward else -1
+ zforward = not zforward
+ _bytes += key[x][1]
+ nowx += 1
+ else:
+
+ _bytes += key[z][int(zforward)]
+
+ else:
+
+ _bytes += key[y][int(yforward)]
+
+ return (
+ _bytes,
+ [nowx + 1, maxheight if nowx or nowz else nowy, _sideLength if nowx else nowz],
+ [nowx, nowy, nowz],
+ )