mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2025-09-22 12:26:46 +00:00
新增安装教程
This commit is contained in:
26
Musicreater/__init__.py
Normal file
26
Musicreater/__init__.py
Normal file
@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""一个简单的我的世界音频转换库
|
||||
音·创 库版 (Musicreater)
|
||||
是一款免费开源的针对《我的世界》的midi音乐转换库
|
||||
Musicreater(音·创)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft**.
|
||||
|
||||
版权所有 © 2023 音·创 开发者
|
||||
Copyright © 2023 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ../License.md
|
||||
Terms & Conditions: ../License.md
|
||||
"""
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
from .main import *
|
||||
|
||||
__version__ = "0.2.3"
|
||||
__all__ = []
|
||||
__author__ = (("金羿", "Eilles Wan"), ("诸葛亮与八卦阵", "bgArray"))
|
||||
|
||||
|
73
Musicreater/exceptions.py
Normal file
73
Musicreater/exceptions.py
Normal file
@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# Email EillesWan2006@163.com W-YI_DoctorYI@outlook.com EillesWan@outlook.com
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 (Musicreater Package Version)
|
||||
是一款免费开源的针对《我的世界:基岩版》的midi音乐转换库
|
||||
Musicreater pkgver (Package Version 音·创 库版)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft: Bedrock Edition**.
|
||||
|
||||
版权所有 © 2023 音·创 开发者
|
||||
Copyright © 2023 all the developers of Musicreater
|
||||
|
||||
开源相关声明请见 ../License.md
|
||||
Terms & Conditions: ../License.md
|
||||
"""
|
||||
|
||||
|
||||
class MSCTBaseException(Exception):
|
||||
"""音·创库版本的所有错误均继承于此"""
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
|
||||
def miao(
|
||||
self,
|
||||
):
|
||||
for i in self.args:
|
||||
print(i + "喵!")
|
||||
|
||||
def crash_it(self):
|
||||
raise self
|
||||
|
||||
|
||||
class CrossNoteError(MSCTBaseException):
|
||||
"""同通道下同音符交叉出现所产生的错误"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class NotDefineTempoError(MSCTBaseException):
|
||||
"""没有Tempo设定导致时间无法计算的错误"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MidiDestroyedError(MSCTBaseException):
|
||||
"""Midi文件损坏"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ChannelOverFlowError(MSCTBaseException):
|
||||
"""一个midi中含有过多的通道(数量应≤16)"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class NotDefineProgramError(MSCTBaseException):
|
||||
"""没有Program设定导致没有乐器可以选择的错误"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ZeroSpeedError(MSCTBaseException):
|
||||
"""以0作为播放速度的错误"""
|
||||
|
||||
pass
|
179
Musicreater/instConstants.py
Normal file
179
Musicreater/instConstants.py
Normal file
@ -0,0 +1,179 @@
|
||||
pitched_instrument_list = {
|
||||
0: ("note.harp", 6),
|
||||
1: ("note.harp", 6),
|
||||
2: ("note.pling", 6),
|
||||
3: ("note.harp", 6),
|
||||
4: ("note.pling", 6),
|
||||
5: ("note.pling", 6),
|
||||
6: ("note.harp", 6),
|
||||
7: ("note.harp", 6),
|
||||
8: ("note.share", 7), # 打击乐器无音域
|
||||
9: ("note.harp", 6),
|
||||
10: ("note.didgeridoo", 8),
|
||||
11: ("note.harp", 6),
|
||||
12: ("note.xylophone", 4),
|
||||
13: ("note.chime", 4),
|
||||
14: ("note.harp", 6),
|
||||
15: ("note.harp", 6),
|
||||
16: ("note.bass", 8),
|
||||
17: ("note.harp", 6),
|
||||
18: ("note.harp", 6),
|
||||
19: ("note.harp", 6),
|
||||
20: ("note.harp", 6),
|
||||
21: ("note.harp", 6),
|
||||
22: ("note.harp", 6),
|
||||
23: ("note.guitar", 7),
|
||||
24: ("note.guitar", 7),
|
||||
25: ("note.guitar", 7),
|
||||
26: ("note.guitar", 7),
|
||||
27: ("note.guitar", 7),
|
||||
28: ("note.guitar", 7),
|
||||
29: ("note.guitar", 7),
|
||||
30: ("note.guitar", 7),
|
||||
31: ("note.bass", 8),
|
||||
32: ("note.bass", 8),
|
||||
33: ("note.bass", 8),
|
||||
34: ("note.bass", 8),
|
||||
35: ("note.bass", 8),
|
||||
36: ("note.bass", 8),
|
||||
37: ("note.bass", 8),
|
||||
38: ("note.bass", 8),
|
||||
39: ("note.bass", 8),
|
||||
40: ("note.harp", 6),
|
||||
41: ("note.harp", 6),
|
||||
42: ("note.harp", 6),
|
||||
43: ("note.harp", 6),
|
||||
44: ("note.iron_xylophone", 6),
|
||||
45: ("note.guitar", 7),
|
||||
46: ("note.harp", 6),
|
||||
47: ("note.harp", 6),
|
||||
48: ("note.guitar", 7),
|
||||
49: ("note.guitar", 7),
|
||||
50: ("note.bit", 6),
|
||||
51: ("note.bit", 6),
|
||||
52: ("note.harp", 6),
|
||||
53: ("note.harp", 6),
|
||||
54: ("note.bit", 6),
|
||||
55: ("note.flute", 5),
|
||||
56: ("note.flute", 5),
|
||||
57: ("note.flute", 5),
|
||||
58: ("note.flute", 5),
|
||||
59: ("note.flute", 5),
|
||||
60: ("note.flute", 5),
|
||||
61: ("note.flute", 5),
|
||||
62: ("note.flute", 5),
|
||||
63: ("note.flute", 5),
|
||||
64: ("note.bit", 6),
|
||||
65: ("note.bit", 6),
|
||||
66: ("note.bit", 6),
|
||||
67: ("note.bit", 6),
|
||||
68: ("note.flute", 5),
|
||||
69: ("note.harp", 6),
|
||||
70: ("note.harp", 6),
|
||||
71: ("note.flute", 5),
|
||||
72: ("note.flute", 5),
|
||||
73: ("note.flute", 5),
|
||||
74: ("note.harp", 6),
|
||||
75: ("note.flute", 5),
|
||||
76: ("note.harp", 6),
|
||||
77: ("note.harp", 6),
|
||||
78: ("note.harp", 6),
|
||||
79: ("note.harp", 6),
|
||||
80: ("note.bit", 6),
|
||||
81: ("note.bit", 6),
|
||||
82: ("note.bit", 6),
|
||||
83: ("note.bit", 6),
|
||||
84: ("note.bit", 6),
|
||||
85: ("note.bit", 6),
|
||||
86: ("note.bit", 6),
|
||||
87: ("note.bit", 6),
|
||||
88: ("note.bit", 6),
|
||||
89: ("note.bit", 6),
|
||||
90: ("note.bit", 6),
|
||||
91: ("note.bit", 6),
|
||||
92: ("note.bit", 6),
|
||||
93: ("note.bit", 6),
|
||||
94: ("note.bit", 6),
|
||||
95: ("note.bit", 6),
|
||||
96: ("note.bit", 6),
|
||||
97: ("note.bit", 6),
|
||||
98: ("note.bit", 6),
|
||||
99: ("note.bit", 6),
|
||||
100: ("note.bit", 6),
|
||||
101: ("note.bit", 6),
|
||||
102: ("note.bit", 6),
|
||||
103: ("note.bit", 6),
|
||||
104: ("note.harp", 6),
|
||||
105: ("note.banjo", 6),
|
||||
106: ("note.harp", 6),
|
||||
107: ("note.harp", 6),
|
||||
108: ("note.harp", 6),
|
||||
109: ("note.harp", 6),
|
||||
110: ("note.harp", 6),
|
||||
111: ("note.guitar", 7),
|
||||
112: ("note.harp", 6),
|
||||
113: ("note.bell", 4),
|
||||
114: ("note.harp", 6),
|
||||
115: ("note.cow_bell", 5),
|
||||
116: ("note.bd", 7), # 打击乐器无音域
|
||||
117: ("note.bass", 8),
|
||||
118: ("note.bit", 6),
|
||||
119: ("note.bd", 7), # 打击乐器无音域
|
||||
120: ("note.guitar", 7),
|
||||
121: ("note.harp", 6),
|
||||
122: ("note.harp", 6),
|
||||
123: ("note.harp", 6),
|
||||
124: ("note.harp", 6),
|
||||
125: ("note.hat", 7), # 打击乐器无音域
|
||||
126: ("note.bd", 7), # 打击乐器无音域
|
||||
127: ("note.snare", 7), # 打击乐器无音域
|
||||
}
|
||||
|
||||
percussion_instrument_list = {
|
||||
34: ("note.bd", 7),
|
||||
35: ("note.bd", 7),
|
||||
36: ("note.hat", 7),
|
||||
37: ("note.snare", 7),
|
||||
38: ("note.snare", 7),
|
||||
39: ("note.snare", 7),
|
||||
40: ("note.hat", 7),
|
||||
41: ("note.snare", 7),
|
||||
42: ("note.hat", 7),
|
||||
43: ("note.snare", 7),
|
||||
44: ("note.snare", 7),
|
||||
45: ("note.bell", 4),
|
||||
46: ("note.snare", 7),
|
||||
47: ("note.snare", 7),
|
||||
48: ("note.bell", 4),
|
||||
49: ("note.hat", 7),
|
||||
50: ("note.bell", 4),
|
||||
51: ("note.bell", 4),
|
||||
52: ("note.bell", 4),
|
||||
53: ("note.bell", 4),
|
||||
54: ("note.bell", 4),
|
||||
55: ("note.bell", 4),
|
||||
56: ("note.snare", 7),
|
||||
57: ("note.hat", 7),
|
||||
58: ("note.chime", 4),
|
||||
59: ("note.iron_xylophone", 6),
|
||||
60: ("note.bd", 7),
|
||||
61: ("note.bd", 7),
|
||||
62: ("note.xylophone", 4),
|
||||
63: ("note.xylophone", 4),
|
||||
64: ("note.xylophone", 4),
|
||||
65: ("note.hat", 7),
|
||||
66: ("note.bell", 4),
|
||||
67: ("note.bell", 4),
|
||||
68: ("note.hat", 7),
|
||||
69: ("note.hat", 7),
|
||||
70: ("note.flute", 5),
|
||||
71: ("note.flute", 5),
|
||||
72: ("note.hat", 7),
|
||||
73: ("note.hat", 7),
|
||||
74: ("note.xylophone", 4),
|
||||
75: ("note.hat", 7),
|
||||
76: ("note.hat", 7),
|
||||
77: ("note.xylophone", 4),
|
||||
78: ("note.xylophone", 4),
|
||||
79: ("note.bell", 4),
|
||||
80: ("note.bell", 4), }
|
217
Musicreater/magicmain.py
Normal file
217
Musicreater/magicmain.py
Normal file
@ -0,0 +1,217 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# 音·创 开发交流群 861684859
|
||||
# 版权所有 金羿("Eilles Wan") & 诸葛亮与八卦阵("bgArray") & 鸣凤鸽子("MingFengPigeon")
|
||||
# 若需使用或借鉴 请依照 Apache 2.0 许可证进行许可
|
||||
|
||||
|
||||
"""
|
||||
音·创 库版 (Musicreater Package Version)
|
||||
是一款免费开源的针对《我的世界:基岩版》的midi音乐转换库
|
||||
注意!除了此源文件以外,任何属于此仓库以及此项目的文件均依照Apache许可证进行许可
|
||||
Musicreater pkgver (Package Version 音·创 库版)
|
||||
A free open source library used for convert midi file into formats that is suitable for **Minecraft: Bedrock Edition**.
|
||||
Note! Except for this source file, all the files in this repository and this project are licensed under Apache License 2.0
|
||||
|
||||
Copyright 2022 all the developers of Musicreater
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
|
||||
def _toCmdList_m1(
|
||||
self,
|
||||
scoreboardname: str = "mscplay",
|
||||
volume: float = 1.0,
|
||||
speed: float = 1.0) -> list:
|
||||
"""
|
||||
使用Dislink Sforza的转换思路,将midi转换为我的世界命令列表
|
||||
:param scoreboardname: 我的世界的计分板名称
|
||||
:param volume: 音量,注意:这里的音量范围为(0,1],如果超出将被处理为正确值,其原理为在距离玩家 (1 / volume -1) 的地方播放音频
|
||||
:param speed: 速度,注意:这里的速度指的是播放倍率,其原理为在播放音频的时候,每个音符的播放时间除以 speed
|
||||
:return: tuple(命令列表, 命令个数, 计分板最大值)
|
||||
"""
|
||||
tracks = []
|
||||
if volume > 1:
|
||||
volume = 1
|
||||
if volume <= 0:
|
||||
volume = 0.001
|
||||
|
||||
commands = 0
|
||||
maxscore = 0
|
||||
|
||||
for i, track in enumerate(self.midi.tracks):
|
||||
|
||||
ticks = 0
|
||||
instrumentID = 0
|
||||
singleTrack = []
|
||||
|
||||
for msg in track:
|
||||
ticks += msg.time
|
||||
# print(msg)
|
||||
if msg.is_meta:
|
||||
if msg.type == "set_tempo":
|
||||
tempo = msg.tempo
|
||||
else:
|
||||
if msg.type == "program_change":
|
||||
# print("TT")
|
||||
instrumentID = msg.program
|
||||
if msg.type == "note_on" and msg.velocity != 0:
|
||||
nowscore = round(
|
||||
(ticks * tempo)
|
||||
/ ((self.midi.ticks_per_beat * float(speed)) * 50000)
|
||||
)
|
||||
maxscore = max(maxscore, nowscore)
|
||||
soundID, _X = self.__Inst2soundID_withX(instrumentID)
|
||||
singleTrack.append(
|
||||
"execute @a[scores={" +
|
||||
str(scoreboardname) +
|
||||
"=" +
|
||||
str(nowscore) +
|
||||
"}" +
|
||||
f"] ~ ~ ~ playsound {soundID} @s ~ ~{1 / volume - 1} ~ {msg.velocity * (0.7 if msg.channel == 0 else 0.9)} {2 ** ((msg.note - 60 - _X) / 12)}")
|
||||
commands += 1
|
||||
if len(singleTrack) != 0:
|
||||
tracks.append(singleTrack)
|
||||
|
||||
return [tracks, commands, maxscore]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ============================
|
||||
|
||||
|
||||
|
||||
|
||||
import mido
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class NoteMessage:
|
||||
def __init__(self, channel, pitch, velocity, startT, lastT, midi, now_bpm, change_bpm=None):
|
||||
self.channel = channel
|
||||
self.note = pitch
|
||||
self.velocity = velocity
|
||||
self.startTime = startT
|
||||
self.lastTime = lastT
|
||||
self.tempo = now_bpm # 这里要程序实现获取bpm可以参考我的程序
|
||||
|
||||
def mt2gt(mt, tpb_a, bpm_a):
|
||||
return mt / tpb_a / bpm_a * 60
|
||||
self.startTrueTime = mt2gt(self.startTime, midi.ticks_per_beat, self.tempo) # / 20
|
||||
# delete_extra_zero(round_up())
|
||||
if change_bpm is not None:
|
||||
self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, change_bpm) # / 20
|
||||
else:
|
||||
self.lastTrueTime = mt2gt(self.lastTime, midi.ticks_per_beat, self.tempo) # / 20
|
||||
# delete_extra_zero(round_up())
|
||||
print((self.startTime * self.tempo) / (midi.ticks_per_beat * 50000))
|
||||
|
||||
def __str__(self):
|
||||
return "noteMessage channel=" + str(self.channel) + " note=" + str(self.note) + " velocity=" + \
|
||||
str(self.velocity) + " startTime=" + str(self.startTime) + " lastTime=" + str(self.lastTime) + \
|
||||
" startTrueTime=" + str(self.startTrueTime) + " lastTrueTime=" + str(self.lastTrueTime)
|
||||
|
||||
|
||||
def load(mid: mido.MidiFile):
|
||||
|
||||
type_ = [False, False, False] # note_off / note_on+0 / mixed
|
||||
|
||||
is_tempo = False
|
||||
|
||||
# 预检
|
||||
for i, track in enumerate(mid.tracks):
|
||||
for msg in track:
|
||||
# print(msg)
|
||||
if msg.is_meta is not True:
|
||||
if msg.type == 'note_on' and msg.velocity == 0:
|
||||
type_[1] = True
|
||||
elif msg.type == "note_off":
|
||||
type_[0] = True
|
||||
if msg.is_meta is True and msg.type == "set_tempo":
|
||||
is_tempo = True
|
||||
|
||||
if is_tempo is not True:
|
||||
raise Exception("这个mid没有可供计算时间的tempo事件")
|
||||
|
||||
if type_[0] is True and type_[1] is True:
|
||||
type_[2] = True
|
||||
type_[1] = False
|
||||
type_[0] = False
|
||||
print(type_)
|
||||
|
||||
bpm = 0
|
||||
recent_change_bpm = 0
|
||||
is_change_bpm = False
|
||||
# 实检
|
||||
for i, track in enumerate(mid.tracks):
|
||||
noteOn = []
|
||||
trackS = []
|
||||
ticks = 0
|
||||
for msg in track:
|
||||
print(msg)
|
||||
ticks += msg.time
|
||||
print(ticks)
|
||||
if msg.is_meta is True and msg.type == "set_tempo":
|
||||
recent_change_bpm = bpm
|
||||
bpm = 60000000 / msg.tempo
|
||||
is_change_bpm = True
|
||||
|
||||
if msg.type == 'note_on' and msg.velocity != 0:
|
||||
noteOn.append([msg, msg.note, ticks])
|
||||
if type_[1] is True:
|
||||
if msg.type == 'note_on' and msg.velocity == 0:
|
||||
for u in noteOn:
|
||||
index = 0
|
||||
if u[1] == msg.note:
|
||||
lastMessage = u[0]
|
||||
lastTick = u[2]
|
||||
break
|
||||
index += 1
|
||||
print(lastTick)
|
||||
if is_change_bpm and recent_change_bpm != 0:
|
||||
trackS.append(NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick,
|
||||
mid, recent_change_bpm, bpm))
|
||||
is_change_bpm = False
|
||||
else:
|
||||
trackS.append(
|
||||
NoteMessage(msg.channel, msg.note, lastMessage.velocity, lastTick, ticks - lastTick,
|
||||
mid, bpm))
|
||||
# print(noteOn)
|
||||
# print(index)
|
||||
try:
|
||||
noteOn.pop(index)
|
||||
except IndexError:
|
||||
noteOn.pop(index - 1)
|
||||
print(trackS)
|
||||
for j in trackS:
|
||||
print(j)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
load(mido.MidiFile("test.mid"))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
1434
Musicreater/main.py
Normal file
1434
Musicreater/main.py
Normal file
File diff suppressed because it is too large
Load Diff
222
Musicreater/utils.py
Normal file
222
Musicreater/utils.py
Normal file
@ -0,0 +1,222 @@
|
||||
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 compress_zipfile(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)
|
||||
arc_name = pathfile[pre_len:].strip(os.path.sep) # 相对路径
|
||||
zipf.write(pathfile, arc_name)
|
||||
zipf.close()
|
||||
|
||||
|
||||
def form_command_block_in_BDX_bytes(
|
||||
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 bottem_side_length_of_smallest_square_bottom_box(total: int, maxHeight: int):
|
||||
"""给定总方块数量和最大高度,返回所构成的图形外切正方形的边长
|
||||
:param total: 总方块数量
|
||||
:param maxHeight: 最大高度
|
||||
:return: 外切正方形的边长 int"""
|
||||
return math.ceil(math.sqrt(math.ceil(total / maxHeight)))
|
||||
|
||||
|
||||
def to_BDX_bytes(
|
||||
commands: list,
|
||||
max_height: int = 64,
|
||||
):
|
||||
"""
|
||||
:param commands: 指令列表(指令, 延迟)
|
||||
:param max_height: 生成结构最大高度
|
||||
:return 成功与否,成功返回(True,未经过压缩的源,结构占用大小),失败返回(False,str失败原因)
|
||||
"""
|
||||
|
||||
_sideLength = bottem_side_length_of_smallest_square_bottom_box(len(commands), max_height)
|
||||
_bytes = b""
|
||||
|
||||
y_forward = True
|
||||
z_forward = True
|
||||
|
||||
now_y = 0
|
||||
now_z = 0
|
||||
now_x = 0
|
||||
|
||||
for cmd, delay in commands:
|
||||
impluse = 2
|
||||
condition = False
|
||||
needRedstone = False
|
||||
tickDelay = delay
|
||||
customName = ""
|
||||
executeOnFirstTick = False
|
||||
trackOutput = True
|
||||
_bytes += form_command_block_in_BDX_bytes(
|
||||
cmd,
|
||||
(1 if y_forward else 0)
|
||||
if (
|
||||
((now_y != 0) and (not y_forward))
|
||||
or (y_forward and (now_y != (max_height - 1)))
|
||||
)
|
||||
else (3 if z_forward else 2)
|
||||
if (
|
||||
((now_z != 0) and (not z_forward))
|
||||
or (z_forward and (now_z != _sideLength))
|
||||
)
|
||||
else 5,
|
||||
impluse=impluse,
|
||||
condition=condition,
|
||||
needRedstone=needRedstone,
|
||||
tickDelay=tickDelay,
|
||||
customName=customName,
|
||||
executeOnFirstTick=executeOnFirstTick,
|
||||
trackOutput=trackOutput,
|
||||
)
|
||||
|
||||
now_y += 1 if y_forward else -1
|
||||
|
||||
if ((now_y >= max_height) and y_forward) or ((now_y < 0) and (not y_forward)):
|
||||
now_y -= 1 if y_forward else -1
|
||||
|
||||
y_forward = not y_forward
|
||||
|
||||
now_z += 1 if z_forward else -1
|
||||
|
||||
if ((now_z > _sideLength) and z_forward) or (
|
||||
(now_z < 0) and (not z_forward)
|
||||
):
|
||||
now_z -= 1 if z_forward else -1
|
||||
z_forward = not z_forward
|
||||
_bytes += key[x][1]
|
||||
now_x += 1
|
||||
else:
|
||||
|
||||
_bytes += key[z][int(z_forward)]
|
||||
|
||||
else:
|
||||
|
||||
_bytes += key[y][int(y_forward)]
|
||||
|
||||
return (
|
||||
_bytes,
|
||||
[
|
||||
now_x + 1,
|
||||
max_height if now_x or now_z else now_y,
|
||||
_sideLength if now_x else now_z,
|
||||
],
|
||||
[now_x, now_y, now_z],
|
||||
)
|
Reference in New Issue
Block a user