This commit is contained in:
EillesWan 2021-11-21 00:59:15 +08:00
commit c674fbac3b
64 changed files with 3401 additions and 0 deletions

19
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}

1298
Musicreater.py Normal file

File diff suppressed because it is too large Load Diff

141
fcwslib/__init__.py Normal file
View File

@ -0,0 +1,141 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
__version__ = '0.0.1'
__all__ = ['run_server', 'subscribe', 'unsubscribe', 'send_command', 'tellraw']
__author__ = 'Fuckcraft <https://gitee.com/fuckcraft>'
import os
import json
import uuid
import logging
import asyncio
import time
import websockets
# 写这段代码的时候,只有我和上帝知道这段代码是干什么的。
# 现在只有上帝知道。
# 此函数用于向 Minecraft 订阅请求
async def subscribe(websocket, event_name):
'''
参数:
: websocket : websocket 对象 :
: event_name : 需要订阅的请求 :
返回:
None
'''
response = {
'body': {
'eventName': str(event_name) # 示例PlayerMessage
},
'header': {
'requestId': str(uuid.uuid4()),
'messagePurpose': 'subscribe',
'version': 1,
'messageType': 'commandRequest'
}
}
# 增加 json 的可读性
# response = json.dumps(response, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
response = json.dumps(response)
await websocket.send(response)
# 此函数用于向 Minecraft 消除订阅请求
async def unsubscribe(webscket):
'''
参数:
: websocket : websocket 对象 :
: event_name : 需要消除订阅的请求 :
返回:
None
'''
response = {
"body": {
"eventName": str(event_name) # PlayerMessage
},
"header": {
"requestId": str(uuid.uuid4()),
"messagePurpose": "unsubscribe",
"version": 1,
"messageType": "commandRequest"
}
}
# 增加 json 的可读性
# response = json.dumps(response, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
response = json.dumps(response)
await websocket.send(response)
# 此函数用于向 Minecraft 执行命令
async def send_command(websocket, command):
'''
参数:
: websocket : websocket 对象 :
: command : 执行的命令 :
返回:
None
'''
response = {
'body': {
'origin': {
'type': 'player'
},
'commandLine': str(command),
'version': 1
},
'header': {
'requestId': str(uuid.uuid4()),
'messagePurpose': 'commandRequest',
'version': 1,
'messageType': 'commandRequest'
}
}
# 增加 json 的可读性
# response = json.dumps(response, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
response = json.dumps(response)
await websocket.send(response)
# 此函数用于向 Minecraft 发送消息
async def tellraw(websocket, message):
'''
参数:
: websocket : websocket 对象 :
: message : 发送的消息 :
返回:
None
'''
command = {
'rawtext':[
{
'text':'[{}] {}'.format(time.asctime(), message)
}
]
}
# 增加 json 可读性
# command = json.dumps(command, sort_keys=True, indent=4, separators=(', ', ': '), ensure_ascii=False)
command = json.dumps(command)
command = 'tellraw @a {}'.format(command)
await send_command(websocket, command)
def run_server(function):
# 修改 ip 地址和端口
start_server = websockets.serve(function, 'localhost', 8080)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

Binary file not shown.

155
msctspt/MSCT Dev Log.txt Normal file
View File

@ -0,0 +1,155 @@
音·创(Musicreater)是由金羿(W-YI)开发的一款《我的世界》基岩版音乐生成辅助软件
本软件源代码依照Apache软件协议公开如使用未经授权的音乐经过此软件生成的任何物件侵犯了他人权利与本软件及其作者无关
Copyright © W-YI 2021
本软件是金羿前作函数音创和世界音创的集合版本,同时增加了大量功能更新。
To-Do
1.可以导出自定义的结构文件用于存储要导入地图中的结构
2.进度条
3.可以将音乐写入音符盒(红乐)
4.更换tk库为briefcase库
5.支持安卓系统
6.可以由.schematic文件导入地图亦可反向处理
7.支持自定义指令方块区域的长宽高等
8.支持自定义创建websockeet服务器播放音乐感谢由 Fuckcraft <https://gitee.com/fuckcraft> “鸣凤鸽子”等 带来的我的世界websocket服务器功能
9.支持使用红石播放指令音乐
10.支持采用延时的播放器
11.支持使用bdx导出结构
12.支持采用tp的方法播放
13.支持识别曲谱图片解析音乐
14.支持使用瀑布流的方式播放音乐
15.帮助菜单
16.多语言
17.支持自动搜寻地图目录位置(网易&微软)
新更新日志
Beta 0.0.3.1~0.0.3.5
2021 11 1~2021 11 2
1.更新部分提示信息使之更加科学
2.强制性限制不得使用非Win32平台打开此程序
3.支持在Windwos7上使用此程序发现错误并解决DLL缺失MSVCP140.dll
4.开始对结构导出进行部分支持
5.发现红乐写入的错误,正在排查修复
Beta 0.0.3
2021 10 29 ~ 2021 10 31
1.修改部分窗口排版
2.修复指令载入地图的结构的错误
3.修复指令生成出现的指令错误(感谢 昀梦<QQ1515399885> 找出bug并指正
4.支持生成红石音乐(以音符盒存储的音乐),并写入地图
5.修复了生成指令音乐导致的错误
6.修复bdx文件y轴过长导致无法生成完毕的错误现在bdx的y轴为200格
Beta 0.0.2
2021 10 25
1.修复了邮件发送错误报告无法生成压缩包的问题
2.修复了导入音轨时无法获得进程返回值的问题
3.修复了.bdx文件生成时无法选择文件的问题
4.修复了生成指令音乐(计分板)没有起始方块的问题
5.新增了创建Websocket的功能可以在localhost:8080创建websocket服务器播放音乐感谢由 Fuckcraft <https://gitee.com/fuckcraft> “鸣凤鸽子”等 带来的我的世界websocket服务器功能(fcwslib)
6.解决了打包成可执行文件时无法正常退出的问题
Beta 0.0.1
2021 10月
1.支持生成.bdx文件感谢由 Charlie_Ping “查理平” 带来的bdx转换功能
2.逐步增强对安卓系统的支持
3.逐步放弃对Windows的强行要求
4.逐步提升性能,增加多线程
Alpha部分更新日志
Alpha 0.0.0
2021 8 20
1.集合了 函数音创0.1.4.1 与 世界音创Beta0.0.1 的功能于本应用
2.新增了可以生成 .mcpack 包的方法
Alpha 0.0.1
2021 8 25
1.新增两个彩蛋(就是函数音创命令行模式的彩蛋
Alpha 0.0.1.1
2021 8 25
1.修复大量已知问题
2.修复了部分彩蛋bug但是程序仍然不会正常退出
3.菜单界面优化
Alpha 0.0.1.2
2021.8.29
1.修复大量已知问题
2.现在可以操作指令文件了
3.窗口界面优化
Alpha 0.0.2
2021 9 5
1.修复部分已知问题
2.指令链导入之时仅生成链式方块且允许折转
3.[Dev]正在逐步支持结构导出
Alpha 0.0.3
2021 9 7
1.修复指令链转入世界的摆放错误
2.指令存储的音乐(包括函数)支持不同玩家不同的播放
3.支持播放进度条
4.删除彩蛋任务栏图标
4.[Dev]已确定导出结构格式
Alpha 0.0.3.1
2021 9 11
1.取消输入玩家选择器时不会出现bug了
2.删除日志文件修改为删除临时文件
3.可以删除用于确认档案存在的文件了
Alpha 0.0.4
2021 10 4-5
1.可以将大函数导入世界(以一条链执行多个函数的方式)
2.关闭了试听音乐的功能但是保留其函数于funOpera.py中
3.修改部分代码减少更多bug
4.发现指令链转入世界的摆放错误,但是没改正
1.0.3
2021 10 5-6
1.解决一些已知问题
2.解决了文件读取造成的字符编码问题
3.使用PyPinyin库将汉字转化为拼音首字母
Alpha 0.0.4.1
2021 10 9
1.将清除日志功能设置为结束后统一清除,避免了清除过程中文件占用导致的问题
Alpha 0.0.5
2021 10 10
1.支持使用邮件方式发送错误报告(日志)
Alpha 0.0.5.1
1.修复了邮件发送错误报告无法发送的问题
2.修复了打包成.exe文件之后无法正常退出的问题

0
msctspt/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

215
msctspt/bdxOpera_CP.py Normal file
View File

@ -0,0 +1,215 @@
import os
import brotli
'''感谢由 Charlie_Ping “查理平” 带来的bdx转换代码'''
class BdxConverter:
__header = "BD@"
__bin_header = b"BDX"
__generator_author = b"&Charlie_Ping"
keys = {
# x--, x++, addSmallX(-128~127), addX(-32768~32767), addBigX(-2147483648~2147483647)
"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"],
"end": b"\x58",
"isSigned": b"\x5a",
"placeCommandBlockWithData": b"\x1b",
"placeBlock": b"\x07"
}
def __init__(self, file_path: str, author: str, blocks):
self.author = author
self.blocks = blocks
self.file_path = file_path
self.direction = [0, 0, 0]
self.block_type = self.get_block_type
self.__file = self.create_and_upload_file
@property
def get_block_type(self):
"""
blocks
[
{
"direction": [x: int, y: int, z: int],
block_name: str,
particular_value: int,
}
]
:return: list 给出的所有方块种类名称
"""
block_type = set()
for block in self.blocks:
block_type.add(block["block_name"])
block_type = list(block_type)
return block_type
@property
def create_and_upload_file(self):
"""
瞎用property 害怕
创建一个bdx文件
要close
:return: 一个文件对象
"""
_dir = os.path.dirname(self.file_path)
if not os.path.isdir(_dir):
os.makedirs(_dir)
_bytes = self.__bin_header
_bytes += b"\x00"
_bytes += self.author.encode("utf-8") + self.__generator_author
for i in self.block_type:
_bytes += b"\x00\x01"
_bytes += bytes(i, encoding="utf-8")
_bytes += b"\x00"
_bytes += self.upload_blocks()
_bytes += b"X"
with open(self.file_path, "w+") as f:
f.write("BD@")
f.close()
with open(self.file_path, "ab+") as f:
f.write(brotli.compress(_bytes))
f.close()
return
def upload_blocks(self):
"""
计算差值
写入移动过程
写入方块
更新差值
:return:
"""
_types = b""
for block in self.blocks:
# print(f"当前方块:{block['block_name']}, 位置: {block['direction']}]")
diff = self.move_pointer(self.direction, block["direction"])
_types += diff
if block["block_name"] in ["command_block",
"chain_command_block",
"repeating_command_block"]:
_types += self.obtain_command_block(block)
else:
_types += self.obtain_universal_block(block)
self.direction = block["direction"]
return _types
def move_pointer(self, direction: list, new_direction):
"""
给出 两个[x, y, z]坐标返回pointer的移动过程
:param direction: 坐标 1
:param new_direction: 坐标 2
:return: bytes
"""
_bytes = b""
for i, sign in enumerate(["x", "y", "z"]):
# print(f"<{sign}> 新-旧={new_direction[i]-direction[i]}")
distance = new_direction[i] - direction[i]
if distance == 0:
# print("距离是0跳过了")
continue
_bytes += self.obtain_pointer_type(distance, sign)
# print(f"向 {sign} 运动了 {distance} 格子")
return _bytes
@classmethod
def obtain_pointer_type(cls, num: int, coordinate: str):
"""
用于确定辅助玩家以某一数据类型走指定长度
-1 -> 0
1 -> 1
[128, 127] -> 2
[-32768, 32767] -> 3
[-2147483648, 2147483647] -> 4
:param num:
:param coordinate: 坐标轴种类x y z
:return:
"""
if num == 0:
return
pointer = 0
condition = (num != -1, # byte=0, pointer=1
num < -1 or num > 1, # byte=1, pointer=2
num < -128 or num > 127, # byte=2, pointer=3
num < -32768 or num > 32767, # byte=4, pointer=4
)
for i in condition:
if i:
pointer += 1
pointer_type = cls.keys[coordinate][pointer]
byte_len = 2 ** (pointer - 2)
if byte_len >= 1:
num_byte = num.to_bytes(byte_len, byteorder="big", signed=True)
return pointer_type + num_byte
return pointer_type
def obtain_universal_block(self, block):
"""
给定一个方块 返回此方块在这个bdx中的id和方块data
:param block: {block_name: str,particular_value: int}
:return: bytes
"""
block_id = b"\x07" + self.block_type.index(block["block_name"]).to_bytes(2, byteorder="big", signed=False)
particular_value = block["particular_value"].to_bytes(2, byteorder="big", signed=False)
block_header = block_id + particular_value
return block_header
def obtain_command_block(self, block):
"""
给定一个命令方块返回命令方块各种数据
:param block: {
"direction": [x: int, y: int, z: int]
"block_name": str,
"particular_value": int,
"impluse": int, # unsigned_int32
"command": str,
"customName": str,
"lastOutput": str, # 没特殊要求写个\x00就得了
"tickdelay": int, # int32
"executeOnFirstTick": int, # 1 bytes
"trackOutput": int, # 1 bytes
"conditional": int, # 1 bytes
"needRedstone": int # 1 bytes
}
:return: bytes of command_block
"""
block_id = b"\x1b" + self.block_type.index(block["block_name"]).to_bytes(2, byteorder="big", signed=False)
particular_value = block["particular_value"].to_bytes(2, byteorder="big", signed=False)
block_header = block_id + particular_value
for i in [
block["impluse"].to_bytes(4, byteorder="big", signed=False),
bytes(block["command"], encoding="utf-8") + b"\x00",
bytes(block["customName"], encoding="utf-8") + b"\x00",
bytes(block["lastOutput"], encoding="utf-8") + b"\x00",
block["tickdelay"].to_bytes(4, byteorder="big", signed=True),
block["executeOnFirstTick"].to_bytes(1, byteorder="big"),
block["trackOutput"].to_bytes(1, byteorder="big"),
block["conditional"].to_bytes(1, byteorder="big"),
block["needRedstone"].to_bytes(1, byteorder="big")
]:
block_header += i
return block_header
if __name__ == '__main__':
block = [{"direction": [-1, -1, -1], "block_name": "concrete", "particular_value": 5},
{"direction": [1, 5, 1], "block_name": "stained_glass", "particular_value": 7},
{"direction": [2, 4, 1], "block_name": "command_block", "particular_value": 3,
"impluse": 0,
"command": "say A generator test",
"customName": "test",
"lastOutput": "",
"tickdelay": 24,
"executeOnFirstTick": 0,
"trackOutput": 0,
"conditional": 0,
"needRedstone": 1
},
{"direction": [3, 4, 1], "block_name": "concrete", "particular_value": 6},
{"direction": [-123412133, 4, 1], "block_name": "concrete", "particular_value": 7}]
bdx = BdxConverter("./test02.bdx", "Charlie_Ping",block)

95
msctspt/bugReporter.py Normal file
View File

@ -0,0 +1,95 @@
# -*- coding: UTF-8 -*-
'''提供错误报告的基本操作及方法'''
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 os, 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;
print(filename)
pathfile = os.path.join(parent, filename)
arcname = pathfile[pre_len:].strip(os.path.sep) #相对路径
zipf.write(pathfile, arcname)
zipf.close()
del zipf,pre_len
#以上函数节选并修改自 正在攀登的小蜗牛 的博客https://blog.csdn.net/qq_21127151/article/details/107503942
'''
# ============================不会写类就别逞强
class report():
def __init__(self,senderName:str = 'Unknown',senderContact:str = 'None',describetion:str = ''):
self.author = 'Yee King (金羿)'
self.senderName = senderName;
self.senderContact = senderContact;
self.describetion = describetion;
if not self.senderName :
self.senderName = 'Unknown';
if not self.senderContact :
self.senderContact = 'None';
@property
'''
def emailReport(senderName:str = 'Unknown',senderContact:str = 'None',describetion:str = ''):
'''使用E-mail方法发送当前的日志和临时文件等'''
import smtplib
from email.mime.text import MIMEText;
from email.mime.multipart import MIMEMultipart;
from email.header import Header;
from nmcsup.log import log
log("发送错误报告")
import os;
log("添加标题与正文")
msg = MIMEMultipart();
#发送者与接收者显示名称
msg["From"] = Header(senderName,'utf-8');
msg["To"] = Header("W-YI (QQ2647547478)",'utf-8');
#标题
msg["Subject"] = '发送来自 '+senderName+' 的BUG错误报告';
#正文
msg.attach(MIMEText("来自"+senderName+"( "+senderContact+" )的错误描述:\n"+describetion,'plain','utf-8'));
log("添加完毕,正在生成压缩包...")
makeZip("./","Temps&Logs.zip",exceptFile="Temps&Logs.zip");
attafile=MIMEText(open("Temps&Logs.zip",'rb').read(),"base64",'gb2312');
attafile["Content-Type"] = 'application/octet-stream';
attafile["Content-Disposition"] = 'attachment;filename="BugReport_from_'+senderName+'.zip"';
msg.attach(attafile);
log("完毕,准备发送")
try:
smtp = smtplib.SMTP()
smtp.connect("smtp.163.com");
#smtp.login("RyounDevTeam@163.com","RyounDaiYi99");
#SIQQKQQYCZRVIDFJ是授权密码
smtp.login("RyounDevTeam@163.com","SIQQKQQYCZRVIDFJ");
smtp.sendmail("RyounDevTeam@163.com",["RyounDevTeam@163.com",],msg.as_string())
log("错误汇报邮件已发送")
except smtplib.SMTPException as e:
log("错误汇报邮件发送失败:\n"+str(e));
log("清空内存和临时文件")
del msg,attafile
os.remove("./Temps&Logs.zip")

214
msctspt/funcOpera.py Normal file
View File

@ -0,0 +1,214 @@
# -*- coding: utf-8 -*-
"""音·创 的函数操作和一些其他功能"""
from nmcsup.log import log
def delPart(Data,starter,ender,includeStart :bool= True,includend :bool= True):
'''删除序列从starter物件到ender物件之间的部分\n
includeStart与inclodend分别控制此函数是否包括starter和ender物件所在部分默认为真\n
starter与ender若为None则默认从首或尾开始'''
try:
if starter == None:
includeStart = True;
starter = Data[0];
if ender == None:
includend = True;
ender = Data[len(Data)-1];
if includend:
if includeStart:
return Data[Data.index(starter):len(Data)-Data[len(Data)::-1].index(ender)];
else:
return Data[Data.index(starter)+1:len(Data)-Data[len(Data)::-1].index(ender)];
else:
if includeStart:
return Data[Data.index(starter):len(Data)-Data[len(Data)::-1].index(ender)-1];
else:
return Data[Data.index(starter)+1:len(Data)-Data[len(Data)::-1].index(ender)-1];
except:
return 0
def keepart(Data,starter,ender,includeStart :bool= True,includend :bool= True):
'''保留序列从starter物件到ender物件之间的部分\n
includeStart与inclodend分别控制此函数是否包括starter和ender物件所在部分默认为真\n
starter与ender若为None则默认从首或尾开始'''
try:
if starter == None:
includeStart = True;
starter = Data[0];
if ender == None:
includend = True;
ender = Data[len(Data)-1];
if includend:
if includeStart:
return Data[Data.index(starter):Data.index(ender)+1];
else:
return Data[Data.index(starter)+1:Data.index(ender)+1];
else:
if includeStart:
return Data[Data.index(starter):Data.index(ender)];
else:
return Data[Data.index(starter)+1:Data.index(ender)];
except:
return 0
def lenFunction(fun) -> int:
'''取得函数指令部分长度,即忽略#开头的注释'''
try:
l = 0;
for i in fun:
if i.replace(" ",'')[0] == '#':
l += 1;
return len(fun)-l;
except:
return -1;
def funSplit(bigFile,maxCmdLen : int = 10000 ):
'''分割bigFile大的函数文件bigFile需要读入文件流\n
返回的部分每行指令皆带有行尾换行符\\n\n
返回-1为大小低于maxCmdLen最长函数指令长度'''
bigFile = bigFile.readlines()
if lenFunction(bigFile) < maxCmdLen:
return -1;
part = [];
parts = [];
l = 0;
for i in bigFile:
if i.replace(" ",'')[0] == '#':
part.append(i+'\n');
else:
part.append(i+'\n');
l += 1;
if l >= 10000:
parts.append(part)
part = [];
l = 0;
return parts;
def makeFuncFiles(musicset, path='./'):
'''在指定目录下生成函数文件'''
from nmcsup.trans import Note2Cmd
commands = []
starts = []
log("=========================正在在此处生成文件:"+path)
maxlen = -1
for i in range(len(musicset['musics'])):
log('写入第'+str(i)+'个数据')
commands.append("scoreboard players add @e[name=\""+musicset['musics'][i]['set']['EntityName']+"\"] "+musicset['musics'][i]['set']['ScoreboardName']+" 1\n")
commands.append("execute @e[name=\""+musicset['musics'][i]['set']['EntityName'] +"\",scores={"+musicset['musics'][i]['set']['ScoreboardName']+"=1..10}] ~~~ title @a"+musicset['mainset']['PlayerSelect']+" title "+musicset['mainset']['MusicTitle']+"\n")
commands.append("execute @e[name=\""+musicset['musics'][i]['set']['EntityName'] +"\",scores={"+musicset['musics'][i]['set']['ScoreboardName']+"=1..10}] ~~~ title @a"+musicset['mainset']['PlayerSelect']+" subtitle 本函数乐曲由§b§l凌云§r§3函数音乐创建§r生成\n")
if len(musicset['musics'][i]['notes']) > maxlen:
maxlen = len(musicset['musics'][i]['notes'])
starts.append("scoreboard objectives add " +musicset['musics'][i]['set']['ScoreboardName']+" dummy\n")
starts.append("summon armor_stand " +musicset['musics'][i]['set']['EntityName']+'\n')
with open(path+musicset['mainset']['MusicTitle']+'_Part'+str(i)+'.mcfunction', 'w', encoding='UTF-8') as f:
f.writelines(Note2Cmd(musicset['musics'][i]['notes'],musicset['musics'][i]['set']['ScoreboardName'],musicset['musics'][i]['set']['Instrument'],musicset['mainset']['PlayerSelect'],True))
if musicset['mainset']['IsRepeat']:
log("增加重复语句")
for i in range(len(musicset['musics'])):
commands.append("execute @e[name=\""+musicset['musics'][i]['set']['EntityName']+"\",scores={"+musicset['musics'][i]['set']['ScoreboardName']+"="+str((maxlen+2)*10)+"}] ~~~ scoreboard players set @e[name=\""+musicset['musics'][i]['set']['EntityName']+"\"] "+musicset['musics'][i]['set']['ScoreboardName']+" -1\n")
log("增加版权语句")
commands.append("\n\n# 凌云我的世界开发团队 x 凌云软件开发团队 : W-YI金羿\n")
starts.append("\n\n# 凌云我的世界开发团队 x 凌云软件开发团队 : W-YI金羿\n")
log("写入支持文件")
with open(path+musicset['mainset']['MusicTitle']+'_Support.mcfunction', 'w', encoding='UTF-8') as f:
f.writelines(commands)
log("写入开始文件")
with open(path+'Start_'+musicset['mainset']['MusicTitle']+'.mcfunction', 'w', encoding='UTF-8') as f:
f.writelines(starts)
del commands, starts, maxlen
log("完成============================")
def makeFunDir(musicset, path='./'):
'''在指定目录下生成函数包文件夹'''
import os
import uuid
log("=============================生成函数包文件夹")
# note,packname="Ryoun",FileName="Music",EntityName_='music_support',ScoreboardName_='music_support',MusicTitle_='Noname',PlayerSelect_='',Repeat_=False,Instrument_='harp'
try:
os.makedirs(path+musicset['mainset']['PackName'] +"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/functions")
log("已创建目录"+path+musicset['mainset']['PackName'] +"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/functions")
except:
log("目录已有无需创建")
pass
# 判断文件皆存在
if not(os.path.exists(path+musicset['mainset']['PackName']+"Pack/world_behavior_packs.json") and os.path.exists(path+musicset['mainset']['PackName']+"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/manifest.json")):
log("创建manifest.json以及world_behavior_packs.json")
behaviorUuid = uuid.uuid4()
with open(path+musicset['mainset']['PackName']+"Pack/world_behavior_packs.json", "w") as f:
f.write("[\n {\"pack_id\": \"" + str(behaviorUuid) +"\",\n \"version\": [ 0, 0, 1 ]}\n]")
with open(path+musicset['mainset']['PackName']+"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/manifest.json", "w") as f:
f.write("{\n \"format_version\": 1,\n \"header\": {\n \"description\": \""+musicset['mainset']['PackName']+" Pack : behavior pack\",\n \"version\": [ 0, 0, 1 ],\n \"name\": \""+musicset['mainset']['PackName']+"Pack\",\n \"uuid\": \"" + str(behaviorUuid) + "\"\n },\n \"modules\": [\n {\n \"description\": \""+musicset['mainset']['PackName']+" Pack : behavior pack\",\n \"type\": \"data\",\n \"version\": [ 0, 0, 1 ],\n \"uuid\": \"" + str(uuid.uuid4()) + "\"\n }\n ]\n}")
makeFuncFiles(musicset, path+musicset['mainset']['PackName'] +"Pack/behavior_packs/"+musicset['mainset']['PackName']+"/functions/")
log("完成============================")
'''
这里是往事用于记载一些用不到的功能
#存在于 Musicreater.py 播放(试听)音乐
def PlayNote(Notes, t=480): # Notes是音符列表t是一拍占有的毫秒数
tkinter.messagebox.showinfo(title='提示!', message="播放发音不一定标准\n说不定还会坏音响/(ㄒoㄒ)/~~qwq\n请注意。")
import winsound
import time
from nmcsup.trans import mcnote2freq
Notes = mcnote2freq(Notes)
for frequency, duration in Notes:
log("播放:"+str([int(frequency), int(duration*t)]))
if int(frequency) != 0:
winsound.Beep(int(frequency), int(duration*t))
elif int(frequency) == 0:
time.sleep(duration*t/1000)
#同上,执行播放命令
def PlayOne():
log("试听")
tkinter.messagebox.showwarning(title="警告⚠", message="试听音质可能引起您的不适,更可能引起您的扬声器的不适,请酌情播放。")
global NowMusic
PlayNote(dataset[0]['musics'][NowMusic]['notes'])
#同上,是早期 MinecraftMusicFunctionMaker.py (函数音创)的代码转移至音·创时的注解
n2c(dataset[0]['musics'][i]['notes'],EntityName=dataset[0]['musics'][i]['set']['EntityName'],ScoreboardName=dataset[0]['musics'][i]['set']['ScoreboardName'],PlayerSelect=dataset[0]['mainset']['PlayerSelect'],Instrument=dataset[0]['musics'][i]['set']["Instrument"])
'''

27
msctspt/threadOpera.py Normal file
View File

@ -0,0 +1,27 @@
import threading
class NewThread(threading.Thread):
'''新建一个进程来运行函数,函数运行完毕后可以使用.getResult方法获取其返回值'''
def __init__(self, func, args=()):
super(NewThread, self).__init__()
self.func = func
self.args = args
def run(self):
self.result = self.func(*self.args)
def getResult(self):
threading.Thread.join(self) # 等待线程执行完毕
try:
return self.result
except Exception:
return None
#
# ————————————————
# 版权声明上面的类NewThread修改自CSDN博主「星火燎愿」的原创文章中的内容遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明。
# 原文链接https://blog.csdn.net/xpt211314/article/details/109543014
# ————————————————
#

227
msctspt/transfer.py Normal file
View File

@ -0,0 +1,227 @@
"""音·创 的转换工具库"""
def hans2pinyin(hans,style=3):
"""将汉字字符串转化为拼音字符串"""
from pypinyin import lazy_pinyin
result = lazy_pinyin(hans=hans,style=style)
final = ''
for i in result:
final += i;
return final
def formCmdBlock(direction:list,command:str,particularValue:int,impluse:int,condition:bool=False,needRedstone:bool=True,tickDelay:int=0,customName:str='',lastOutput:str='',executeOnFirstTick:bool=False,trackOutput:bool=True):
"""
使用指定项目返回指定的指令方块格式字典
:param block: {
"direction": [x: int, y: int, z: int] #方块位置
"block_name": str, #方块名称无需指定默认为command_block
"particular_value": int, #方块特殊值
"impluse": int, #方块类型0脉冲 1循环 2连锁 unsigned_int32
"command": str, #指令
"customName": str, #悬浮字
"lastOutput": str, #上次输出
"tickdelay": int, #方块延时 int32
"executeOnFirstTick": int, #执行第一个选项 1 bytes
"trackOutput": int, #是否输出 1 bytes
"conditional": int, #是否有条件 1 bytes
"needRedstone": int #是否需要红石 1 bytes
}
:return: 指令方块字典结构
"""
return {"direction": direction,
"block_name": "command_block",
"particular_value": particularValue,
"impluse": impluse,
"command": command,
"customName": customName,
"lastOutput": lastOutput,
"tickdelay": tickDelay,
"executeOnFirstTick": executeOnFirstTick,
"trackOutput": trackOutput,
"conditional": condition,
"needRedstone": needRedstone
}
def note2bdx(filePath:str,dire:list,Notes : list,ScoreboardName:str,Instrument:str, PlayerSelect:str='',isProsess:bool=False) :
'''使用方法同Note2Cmd
:param 参数说明
filePath: 生成.bdx文件的位置
dire: 指令方块在地图中生成的起始位置相对位置
Notes: list[ list[ float我的世界playsound指令音调 , float延续时常单位s ] ] 格式存储的音符列表 例如Musicreater.py的(dataset[0]['musics'][NowMusic]['notes'])
ScoreboardName: 用于执行的计分板名称
Instrument: 播放的乐器
PlayerSelect: 执行的玩家选择器
isProsess: 是否显示进度条会很卡
:return 返回一个BdxConverter类实际上没研究过同时在指定位置生成.bdx文件'''
from msctspt.transfer import formCmdBlock
from nmcsup.trans import Note2Cmd
from msctspt.bdxOpera_CP import BdxConverter
cmd = Note2Cmd(Notes,ScoreboardName,Instrument, PlayerSelect,isProsess)
cdl = []
for i in cmd:
try:
if (i[:i.index('#')].replace(' ','') != '\n') and(i[:i.index('#')].replace(' ','') != ''):
cdl.append(i[:i.index('#')])
except:
cdl.append(i)
i = 0
down = False
blocks = [formCmdBlock(dire,cdl.pop(0),1,1)]
dire[1]+=1;
for j in cdl:
if dire[1]+i > 200:
dire[0]+=1
i=0
down = not down
if dire[1]+i == 200 :
blocks.append(formCmdBlock([dire[0],dire[1]+i,dire[2]],j,5,2,False,False))
else:
if down:
blocks.append(formCmdBlock([dire[0],dire[1]+i,dire[2]],j,0,2,False,False))
else:
blocks.append(formCmdBlock([dire[0],dire[1]+i,dire[2]],j,1,2,False,False))
i+=1
del i, cdl, down, cmd
return BdxConverter(filePath,'RyounMusicreater',blocks)
def note2webs(Notes : list,Instrument:str, speed:float = 5.0, PlayerSelect:str='',isProsess:bool=False) :
'''传入音符在oaclhost:8080上建立websocket服务器以供我的世界connect/wssever指令连接
:param 参数说明
Notes: list[ list[ float我的世界playsound指令音调 , float延续时常单位s ] ] 格式存储的音符列表 例如Musicreater.py的(dataset[0]['musics'][NowMusic]['notes'])
Instrument: 播放的乐器
speed: 用于控制播放速度数值越大播放速度越快相当于把一秒变为几拍
PlayerSelect: 执行的玩家选择器
isProsess: 是否显示进度条
:return None'''
import time
import fcwslib
import asyncio
from nmcsup.log import log
from nmcsup.vers import VER
async def run_server(websocket, path):
log('服务器连接创建')
await fcwslib.tellraw(websocket, '已连接服务器——音·创'+VER[1]+VER[0]+' 作者:金羿(W-YI)')
if isProsess:
length = len(Notes)
j = 1;
for i in range(len(Notes)):
await fcwslib.send_command(websocket,'execute @a'+PlayerSelect+' ~ ~ ~ playsound '+Instrument+' @s ~ ~ ~ 1000 '+str(Notes[i][0])+' 1000')
if isProsess:
fcwslib.send_command(websocket,'execute @a'+PlayerSelect+' ~ ~ ~ title @s actionbar §e▶ 播放中: §a'+str(j)+'/'+str(length)+' || '+str(int(j/length*1000)/10))
j+=1;
time.sleep(Notes[i][1]/speed)
fcwslib.run_server(run_server)
def note2RSworld(world:str,startpos:list,notes:list,instrument:str,speed:float = 2.5,posadder:list = [1,0,0],baseblock:str = 'stone') -> bool:
'''传入音符,生成以音符盒存储的红石音乐
:param 参数说明
world: 地图文件的路径
startpos: list[int,int,int] 开始生成的坐标
notes: list[list[float,float]] list[ list[ float我的世界playsound指令音调 , float延续时常单位s ] ] 格式存储的音符列表 例如Musicreater.py的dataset[0]['musics'][NowMusic]['notes']
instrument: 播放的乐器
speed: 一拍占多少个中继器延迟(红石刻/rt)
posadder: list[int,int,int] 坐标增加规律即红石的延长时按照此增加规律增加坐标
baseblock: 在中继器下垫着啥方块呢~
:return 是否生成成功
'''
import amulet
from amulet.api.block import Block
from amulet.utils.world_utils import block_coords_to_chunk_coords as bc2cc
from amulet_nbt import TAG_String as ts
from msctspt.values import height2note,instuments
from nmcsup.log import log
def formNoteBlock(note:int,instrument:str='note.harp',powered:bool = False):
'''生成音符盒方块
:param note: 0~24
:return Block()'''
if powered:
powered = 'true';
else:
powered = 'false';
return Block('universal_minecraft','noteblock',{"instrument":ts(instrument.replace("note.",'')),'note':ts(str(note)),'powered':ts(powered)})
def formRepeater(delay:int,facing:str,locked:bool=False,powered:bool=False):
'''生成中继器方块
:param delay: 1~4
:return Block()'''
if powered:powered = 'true';
else:powered = 'false';
if locked:locked = 'true';
else:locked = 'false';
return Block('universal_minecraft','repeater',{"delay":ts(str(delay)),'facing':ts(facing),'locked':ts(locked),'powered':ts(powered)})
level = amulet.load_level(world)
def setblock(block:Block,pos:list):
'''pos : list[int,int,int]'''
cx, cz = bc2cc(pos[0], pos[2])
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = pos[0] - 16 * cx, pos[2] - 16 * cz
chunk.blocks[offset_x, pos[1], offset_z] = level.block_palette.get_add_block(block)
chunk.changed = True
# 1拍 x 2.5 rt
def placeNoteBlock():
for i in notes:
try :
setblock(formNoteBlock(height2note[i[0]],instrument),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",instuments[i[0]][1]),startpos)
except :
log("无法放置音符:"+str(i)+''+str(startpos))
setblock(Block("universal_minecraft",baseblock),startpos)
setblock(Block("universal_minecraft",baseblock),[startpos[0],startpos[1]+1,startpos[2]])
delay = int(i[1]*speed+0.5)
if delay <= 4:
startpos[0]+=1
setblock(formRepeater(delay,'west'),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",baseblock),startpos)
else:
for i in range(int(delay/4)):
startpos[0]+=1
setblock(formRepeater(4,'west'),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",baseblock),startpos)
if delay % 4 != 0:
startpos[0]+=1
setblock(formRepeater(delay%4,'west'),[startpos[0],startpos[1]+1,startpos[2]])
setblock(Block("universal_minecraft",baseblock),startpos)
startpos[0]+=posadder[0]
startpos[1]+=posadder[1]
startpos[2]+=posadder[2]
try:
placeNoteBlock()
except:
log("无法放置方块了,可能是因为区块未加载叭")
level.save()
level.close()

59
msctspt/values.py Normal file
View File

@ -0,0 +1,59 @@
instuments = {
'note.banjo' : ['班卓琴','hay_block'],
'note.bass' : ['贝斯','planks'],
'note.bassattack' : ['低音鼓/贝斯','log'],
'note.bd' : ['底鼓','stone'], #即basedrum
'note.bell' : ['铃铛/钟琴','gold_block'],
'note.bit' : ['比特/“芯片”(方波)','emerald_block'],
'note.chime' : ['管钟','packed_ice'],
'note.cow_bell' : ['牛铃','soul_sand'],
'note.didgeridoo' : ['迪吉里杜管','pumpkin'],
'note.flute' : ['长笛','clay'],
'note.guitar' : ['吉他','wool'],
'note.harp' : ['竖琴/钢琴','concrete'], #任意其他类型的方块皆可
'note.hat' : ['击鼓沿/架子鼓','glass'],
'note.iron_xylophone' : ['“铁木琴”(颤音琴)','iron_block'],
'note.pling' : ['“扣弦”(电钢琴)','glowstone'],
'note.snare' : ['小军鼓','sand'],
'note.xylophone' : ['木琴','bone_block']
}
'''乐器对照表\n
乐器英文:[中文, 对应音符盒下方块名称]
方块仅取一个'''
height2note = {
0.5: 0,
0.53: 1,
0.56: 2,
0.6: 3,
0.63: 4,
0.67: 5,
0.7: 6,
0.75: 7,
0.8: 8,
0.84: 9,
0.9: 10,
0.94: 11,
1.0: 12,
1.05: 13,
1.12: 14,
1.2: 15,
1.25: 16,
1.33: 17,
1.4: 18,
1.5: 19,
1.6: 20,
1.7: 21,
1.8: 22,
1.9: 23,
2.0: 24,
}
'''音高对照表\n
MC音高:音符盒音调'''

BIN
msvcp140.dll Normal file

Binary file not shown.

BIN
nmcsup/EptWorld.zip Normal file

Binary file not shown.

72
nmcsup/NFC Dev Log.txt Normal file
View File

@ -0,0 +1,72 @@
从此日志开始,我的世界函数音乐构建更名为 函数音创 NoteFunCreater谐音NotFun[狗头]版本号更为0.1.0开始
注意,运行此文件需要第三方库:
1. mido 用于对midi文件的解码
2. py7zr 用于对7z压缩包的压缩与解压等需pycparser, cffi, texttable, pyzstd, pyppmd, pycryptodomex, multivolumefile, brotli, bcj-cffi支持 -(从0.1.3开始不需要)
3. zipfile 用于自动生成函数包的压缩
4. pystray 用于支持窗口任务栏
5. pillow 相当于Python2的PIL用于绘图
0.1.0
2021 7 10 - 2021 7 12
1.程序窗口化
2.仅支持基本的菜单操作
3.程序文件皆储存至其相应目录下
4.程序./bin/目录下文件将会自动防修改
5.删除了彩蛋
0.1.1
2021 7 14
1.新增版本辨别的提示
2.窗口中显示歌曲信息
0.1.2
2021 7 14 - 2021 7 15
1.在没运行过的机器上会自动安装库
2.从midi导入时不会删除其他音轨
3.改进UI样式
4.支持对于单个音轨设置的修改以及音乐主设置的修改
5.当未保存便退出时,会询问存储
6.新增加载进度提示
0.1.3
2021 7 15 - 2021 7 19
1.不再从文件中读取音符及乐器信息(所以包更小了)
2.改进UI
3.修复了修改玩家选择器时变更了音乐标题的bug
4.新增删除当前选定音轨按钮
5.新增重置设置按钮(将音乐总设置设置为开始时的设置)
6.运用多线程加载函数与文件等,程序运行效率更高
7.修复变量作用域混淆问题
0.1.3.1
2021 7 19
1.修复了菜单中无法退出程序的问题
0.1.4
2021 7 22
1.支持显示指令于列表中
TO-DO
1.支持从midi文件的元信息中收取音符信息并自动生成
2.支持生成zip函数包
3.支持使用WebSocket接口自动播放已编辑的音乐
4.可以编辑多个项目
5.能够自动将一个长串的音乐分成多个函数文件
6.支持用户导入自己的乐器
7.支持汇报崩溃记录(通过邮件附件的方式)
8.支持播放字幕
9.支持任务栏角标与通知
10.将控制台版本的彩蛋移植到此版本,开启了任务栏
11.可编辑音符

36
nmcsup/NMC Dev Log.txt Normal file
View File

@ -0,0 +1,36 @@
世界音创(NoteMapCreater)是金羿开发的一款用于生成我的世界中各类有关音乐的物件的软件
软件禁止商用,源代码始终公开,如使用未经授权的音乐经过此软件生成的任何物件侵犯了他人权利与本软件及其作者无关
Copyright © W-YI 2021
开头,特别感谢:
KCINE提供Cinemusicedit函数包(虽然函数包没怎么用过)
Charlie_Ping提供MusiCreaterBot(音乐地图生成QQ机器人)源码核心以及时不时的催更(虽然源码没有抄)
金羿(作者本人)提供NoteFunCreater(函数音创)的制作经验以及时不时的摸鱼(虽然不是很支持函数音创)
广大群友:高效的催更作业让我以蜗牛的速度前进
Alpha 0.0.0
2021 8 1 - 2021 8 10
1.确定了大概的功能
2.不支持无参数传入
3.可以查看帮助,但是帮助大多功能没实现
4.可以从格式文本、midi文件、钢琴声音MP3导入音轨
5.可以生成一些方块到世界里,但是没有播放器(半支持bw开关)
5.提供了修改文件地址的方法,但是不能修改
Alpha 0.0.1
2021 8 10
1.可以从函数音创的工程文件读取音轨
2.可以新建一个空白世界来生成
3.支持修改输出文件地址
4.支持修改输出方块起始位置
5.支持指定播放乐器,执行实体,执行积分板,播放玩家选择器
6.可以生成指令音乐地图(完全支持-w开关)
Beta 0.0.0
2021 8 X?
1.除了-nw 和 -f 开关不支持以外都支持了
Beta 0.0.1
2021 8 19
1.修复了大量bug

0
nmcsup/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

320
nmcsup/const.py Normal file
View File

@ -0,0 +1,320 @@
"""音创系列的音符对照表 以及一系列常数"""
notes = {
'....A' : [0.074, 27.5, 'wood', 8],
'....A#' : [0.0787, 29.135, 'wood', 9],
'....B' : [0.083, 30.868, 'wood', 10],
'...C' : [0.088, 32.703, 'wood', 11],
'...C#' : [0.094, 34.648, 'wood', 12],
'...D' : [0.1, 36.708, 'wood', 13],
'...D#' : [0.105, 38.891, 'log', 0],
'...E' : [0.11, 41.203, 'log', 1],
'...F' : [0.12, 43.654, 'log', 2],
'...F#' : [0.125, 46.249, 'wood', 0],
'...G' : [0.13, 48.999, 'wood', 1],
'...G#' : [0.14, 51.913, 'wood', 2],
'...A' : [0.15, 55.0, 'wood', 3],
'...A#' : [0.16, 58.27, 'wood', 4],
'...B' : [0.17, 61.735, 'wood', 5],
'..C' : [0.18, 65.406, 'wool', 0],
'..C#' : [0.19, 69.296, 'wool', 1],
'..D' : [0.2, 73.416, 'wool', 2],
'..D#' : [0.21, 77.782, 'wool', 3],
'..E' : [0.22, 82.407, 'wool', 4],
'..F' : [0.235, 87.307, 'wool', 5],
'..F#' : [0.25, 92.499, 'concretepowder', 0],
'..G' : [0.26, 97.999, 'concretepowder', 1],
'..G#' : [0.28, 103.826, 'concretepowder', 2],
'..A' : [0.3, 110.0, 'concretepowder', 3],
'..A#' : [0.31, 116.541, 'concretepowder', 4],
'..B' : [0.33, 123.471, 'concretepowder', 5],
'.C' : [0.35, 130.813, 'concretepowder', 6],
'.C#' : [0.37, 138.591, 'concretepowder', 7],
'.D' : [0.4, 146.832, 'concretepowder', 8],
'.D#' : [0.42, 155.563, 'concretepowder', 9],
'.E' : [0.44, 164.814, 'concretepowder', 10],
'.F' : [0.47, 174.614, 'concretepowder', 11],
'.F#' : [0.5, 184.997, 'concretepowder', 12],
'.G' : [0.53, 195.998, 'concretepowder', 13],
'.G#' : [0.56, 207.652, 'concretepowder', 14],
'.A' : [0.6, 220.0, 'concretepowder', 15],
'.A#' : [0.63, 233.082, 'concrete', 0],
'.B' : [0.67, 246.942, 'concrete', 1],
'C' : [0.7, 261.626, 'concrete', 2],
'C#' : [0.75, 277.183, 'concrete', 3],
'D' : [0.8, 293.665, 'concrete', 4],
'D#' : [0.84, 311.127, 'concrete', 5],
'E' : [0.9, 329.628, 'concrete', 6],
'F' : [0.94, 349.228, 'concrete', 7],
'F#' : [1.0, 369.994, 'concrete', 8],
'G' : [1.05, 391.995, 'concrete', 9],
'G#' : [1.12, 415.305, 'concrete', 10],
'A' : [1.2, 440.0, 'concrete', 11],
'A#' : [1.25, 466.164, 'concrete', 12],
'B' : [1.33, 493.883, 'concrete', 13],
'`C' : [1.4, 523.251, 'concrete', 14],
'`C#' : [1.5, 554.365, 'concrete', 15],
'`D' : [1.6, 587.33, 'stained_hardened_clay', 0],
'`D#' : [1.7, 622.254, 'stained_hardened_clay', 1],
'`E' : [1.8, 659.255, 'stained_hardened_clay', 2],
'`F' : [1.9, 698.456, 'stained_hardened_clay', 3],
'`F#' : [2.0, 739.989, 'stained_hardened_clay', 4],
'`G' : [2.1, 783.991, 'stained_hardened_clay', 5],
'`G#' : [2.24, 830.609, 'stained_hardened_clay', 6],
'`A' : [2.4, 880.0, 'stained_hardened_clay', 7],
'`A#' : [2.5, 932.328, 'stained_hardened_clay', 8],
'`B' : [2.67, 987.767, 'stained_hardened_clay', 9],
'``C' : [2.83, 1046.502, 'stained_hardened_clay', 10],
'``C#' : [3.0, 1108.731, 'stained_hardened_clay', 11],
'``D' : [3.17, 1174.659, 'stained_hardened_clay', 12],
'``D#' : [3.36, 1244.508, 'stained_hardened_clay', 13],
'``E' : [3.56, 1318.51, 'stained_hardened_clay', 14],
'``F' : [3.78, 1396.913, 'stained_hardened_clay', 15],
'``F#' : [4.0, 1479.978, 'white_glazed_terracotta', 0],
'``G' : [4.24, 1567.982, 'orange_glazed_terracotta', 0],
'``G#' : [4.5, 1661.219, 'magenta_glazed_terracotta', 0],
'``A' : [4.76, 1760.0, 'light_blue_glazed_terracotta', 0],
'``A#' : [5.04, 1864.655, 'yellow_glazed_terracotta', 0],
'``B' : [5.34, 1975.533, 'lime_glazed_terracotta', 0],
'```C' : [5.66, 2093.005, 'pink_glazed_terracotta', 0],
'```C#' : [6.0, 2217.461, 'gray_glazed_terracotta', 0],
'```D' : [6.35, 2349.318, 'silver_glazed_terracotta', 0],
'```D#' : [6.73, 2489.016, 'cyan_glazed_terracotta', 0],
'```E' : [7.13, 2637.02, 'purple_glazed_terracotta', 0],
'```F' : [7.55, 2793.826, 'blue_glazed_terracotta', 0],
'```F#' : [8.0, 2959.955, 'brown_glazed_terracotta', 0],
'```G' : [8.47, 3135.963, 'green_glazed_terracotta', 0],
'```G#' : [8.98, 3322.438, 'red_glazed_terracotta', 0],
'```A' : [9.51, 3520.0, 'black_glazed_terracotta', 0],
'```A#' : [10.08, 3729.31, 'stained_glass', 0],
'```B' : [10.68, 3951.066, 'stained_glass', 1],
'````C' : [11.31, 4186.009, 'stained_glass', 2],
'0' : [0.0, 0.0, 'glass', 0]
}
'''音符对照表\n
音符:[MC音调, 声音频率, 方块名称, 数据值]'''
#方块
'''
blocks = {
0.074 : ['stained_glass', 3],
0.0787 : ['stained_glass', 4],
0.083 : ['stained_glass', 5],
0.088 : ['stained_glass', 6],
0.094 : ['stained_glass', 7],
0.1 : ['stained_glass', 8],
0.105 : ['stained_glass', 9],
0.11 : ['stained_glass', 10],
0.12 : ['stained_glass', 11],
0.125 : ['stained_glass', 12],
0.13 : ['stained_glass', 13],
0.14 : ['stained_glass', 14],
0.15 : ['stained_glass', 15],
0.16 : ['wool', 0],
0.17 : ['wool', 1],
0.18 : ['wool', 2],
0.19 : ['wool', 3],
0.2 : ['wool', 4],
0.21 : ['wool', 5],
0.22 : ['wool', 6],
0.235 : ['wool', 7],
0.25 : ['concretepowder', 0],
0.26 : ['concretepowder', 1],
0.28 : ['concretepowder', 2],
0.3 : ['concretepowder', 3],
0.31 : ['concretepowder', 4],
0.33 : ['concretepowder', 5],
0.35 : ['concretepowder', 6],
0.37 : ['concretepowder', 7],
0.4 : ['concretepowder', 8],
0.42 : ['concretepowder', 9],
0.44 : ['concretepowder', 10],
0.47 : ['concretepowder', 11],
0.5 : ['concretepowder', 12],
0.53 : ['concretepowder', 13],
0.56 : ['concretepowder', 14],
0.6 : ['concretepowder', 15],
0.63 : ['concrete', 0],
0.67 : ['concrete', 1],
0.7 : ['concrete', 2],
0.75 : ['concrete', 3],
0.8 : ['concrete', 4],
0.84 : ['concrete', 5],
0.9 : ['concrete', 6],
0.94 : ['concrete', 7],
1.0 : ['concrete', 8],
1.05 : ['concrete', 9],
1.12 : ['concrete', 10],
1.2 : ['concrete', 11],
1.25 : ['concrete', 12],
1.33 : ['concrete', 13],
1.4 : ['concrete', 14],
1.5 : ['concrete', 15],
1.6 : ['stained_hardened_clay', 0],
1.7 : ['stained_hardened_clay', 1],
1.8 : ['stained_hardened_clay', 2],
1.9 : ['stained_hardened_clay', 3],
2.0 : ['stained_hardened_clay', 4],
2.1 : ['stained_hardened_clay', 5],
2.24 : ['stained_hardened_clay', 6],
2.4 : ['stained_hardened_clay', 7],
2.5 : ['stained_hardened_clay', 8],
2.67 : ['stained_hardened_clay', 9],
2.83 : ['stained_hardened_clay', 10],
3.0 : ['stained_hardened_clay', 11],
3.17 : ['stained_hardened_clay', 12],
3.36 : ['stained_hardened_clay', 13],
3.56 : ['stained_hardened_clay', 14],
3.78 : ['stained_hardened_clay', 15],
4.0 : ['stained_glass_pane', 0],
4.24 : ['stained_glass_pane', 1],
4.5 : ['stained_glass_pane', 2],
4.76 : ['stained_glass_pane', 3],
5.04 : ['stained_glass_pane', 4],
5.34 : ['stained_glass_pane', 5],
5.66 : ['stained_glass_pane', 6],
6.0 : ['stained_glass_pane', 7],
6.35 : ['stained_glass_pane', 8],
6.73 : ['stained_glass_pane', 9],
7.13 : ['stained_glass_pane', 10],
7.55 : ['stained_glass_pane', 11],
8.0 : ['stained_glass_pane', 12],
8.47 : ['stained_glass_pane', 13],
8.98 : ['stained_glass_pane', 14],
9.51 : ['stained_glass_pane', 15],
10.08 : ['stained_glass', 0],
10.68 : ['stained_glass', 1],
11.31 : ['stained_glass', 2],
0.0 : ['glass', 0]
}
#向查理平致敬!!!!!
'''
Blocks = {
0.074: 'barrel',
0.0787: 'beacon',
0.083: 'bedrock',
0.088: 'black_glazed_terracotta',
0.094: 'blast_furnace',
0.1: 'blue_glazed_terracotta',
0.105: 'blue_ice',
0.11: 'bone_block',
0.12: 'bookshelf',
0.125: 'brick_block',
0.13: 'brown_glazed_terracotta',
0.14: 'cartography_table',
0.15: 'carved_pumpkin',
0.16: 'clay',
0.17: 'coal_block',
0.18: 'coal_ore',
0.19: 'cobblestone',
0.2: 'concrete',
0.21: 'crafting_table',
0.22: 'cyan_glazed_terracotta',
0.235: 'diamond_block',
0.25: 'diamond_ore',
0.26: 'white_glazed_terracotta',
0.28: 'dispenser',
0.3: 'dried_kelp_block',
0.31: 'dropper',
0.33: 'emerald_block',
0.35: 'emerald_ore',
0.37: 'end_bricks',
0.4: 'end_stone',
0.42: 'fletching_table',
0.44: 'furnace',
0.47: 'glass',
0.5: 'glowingobsidian',
0.53: 'glowstone',
0.56: 'gold_block',
0.6: 'gold_ore',
0.63: 'grass',
0.67: 'gray_glazed_terracotta',
0.7: 'green_glazed_terracotta',
0.75: 'hardened_clay',
0.8: 'hay_block',
0.84: 'iron_block',
0.9: 'iron_ore',
0.94: 'jukebox',
1.0: 'lapis_block',
1.05: 'lapis_ore',
1.12: 'light_blue_glazed_terracotta',
1.2: 'lime_glazed_terracotta',
1.25: 'lit_pumpkin',
1.33: 'log',
1.4: 'loom',
1.5: 'magenta_glazed_terracotta',
1.6: 'magma',
1.7: 'melon_block',
1.8: 'web',
1.9: 'mossy_cobblestone',
2.0: 'nether_brick',
2.1: 'nether_wart_block',
2.24: 'netherrack',
2.4: 'noteblock',
2.5: 'observer',
2.67: 'obsidian',
2.83: 'orange_glazed_terracotta',
3.0: 'pink_glazed_terracotta',
3.17: 'piston',
3.36: 'planks',
3.56: 'prismarine',
3.78: 'pumpkin',
4.0: 'purple_glazed_terracotta',
4.24: 'purpur_block',
4.5: 'quartz_block',
4.76: 'quartz_ore',
5.04: 'red_glazed_terracotta',
5.34: 'red_nether_brick',
5.66: 'red_sandstone',
6.0: 'redstone_block',
6.35: 'yellow_glazed_terracotta',
6.73: 'sandstone',
7.13: 'stonebrick',
7.55: 'silver_glazed_terracotta',
8.0: 'slime',
8.47: 'smithing_table',
8.98: 'smoker',
9.51: 'smooth_stone',
10.08: 'snow',
10.68: 'soul_sand',
11.31: 'sponge',
0.0: 'stone'
}
'''频率对照表\n
MC音调:方块名称'''
# 乐器
Instuments = {
'note.banjo' : '班卓',
'note.bass' : '低音',
'note.bassattack' : '贝斯',
'note.bd' : '鼓声',
'note.bell' : '铃声',
'note.bit' : '比特',
'note.cow_bell' : '牛铃',
'note.didgeridoo' : '迪吉',
'note.flute' : '长笛',
'note.guitar' : '吉他',
'note.harp' : '竖琴',
'note.hat' : '架鼓',
'note.chime' : '钟声',
'note.iron_xylophone' : '铁琴',
'note.pling' : '叮叮',
'note.snare' : '响弦',
'note.xylophone' : '木琴'
}
'''乐器对照表\n
乐器英文:中文
翻译雪莹工坊Fun-Fer'''

16
nmcsup/log.py Normal file
View File

@ -0,0 +1,16 @@
"""提供对于音创系列的日志"""
import datetime,os
#载入日志功能
StrStartTime = str(datetime.datetime.now()).replace(':', '_')[:-7]
'''字符串型的程序开始时间'''
def log(info:str = ''):
'''将信息连同当前时间载入日志'''
if not os.path.exists('./log/'):
os.makedirs('./log/')
with open('./log/'+StrStartTime+'.msct.log', 'a',encoding='UTF-8') as f:
f.write(str(datetime.datetime.now())[11:19]+' '+info+'\n')
print(str(datetime.datetime.now())[11:19]+' '+info)

87
nmcsup/nmcreader.py Normal file
View File

@ -0,0 +1,87 @@
"""音创系列的文件读取功能"""
from nmcsup.log import log
from nmcsup.const import notes
#从格式文本文件读入一个音轨并存入一个列表
def ReadFile(fn : str) -> list:
from nmcsup.trans import note2list
log('打开'+fn+"并读取音符")
try:
nat = open(fn, 'r', encoding='UTF-8').read().split(" ")
del fn
except:
log("找不到读取目标文件")
return False
Notes = []
log(str(nat)+"已读取")
for i in range(int(len(nat)/2)):
Notes.append([nat[i*2], float(nat[i*2+1])])
Notes = note2list(Notes)
log('音符数据更新'+str(Notes))
return [Notes,]
#从midi读入多个音轨返回多个音轨列表
def ReadMidi(midfile : str ) -> list:
import mido
from msctspt.threadOpera import NewThread
Notes = []
try:
mid = mido.MidiFile(midfile)
except:
log("找不到文件或无法读取文件"+midfile)
return False
# 解析
ks = list(notes.values())
def loadMidi(track):
datas = []
for i in track:
if i.is_meta:
log('元信息'+str(i))
pass # 不处理元信息
elif 'note_on' in str(i):
msg = str(i).replace("note=", '').replace("time=", '').split(" ")
log('音符on消息处理后'+str(msg))
if msg[4] == '0':
datas.append([ks[int(msg[2])-20][0], 1.0])
log('延续时间0tick--:添加音符'+str([ks[int(msg[2])-20][0], 1.0]))
else:
datas.append([ks[int(msg[2])-20][0], float(msg[4])/480])
log('延续时间'+msg[4]+'tick--:添加音符' +str([ks[int(msg[2])-20][0], float(msg[4])/480]))
del msg
log('音符增加'+str(datas))
return datas
for j, track in enumerate(mid.tracks):
th = NewThread(loadMidi,(track,))
th.start()
Notes.append(th.getResult())
del ks
return Notes
def ReadOldProject(fn:str) -> list:
import json
from nmcsup.trans import note2list
log("读取文件:"+fn)
try:
with open(fn, 'r', encoding='UTF-8') as c:
dataset = json.load(c)
except:
print('找不到文件:'+fn+",请查看您是否输入正确")
log("丢失"+fn)
return False
for i in range(len(dataset['musics'])):
dataset['musics'][i]['notes'] = note2list(dataset['musics'][i]['notes'])
#返回 音轨列表 选择器
return dataset

292
nmcsup/trans.py Normal file
View File

@ -0,0 +1,292 @@
"""音创系列的转换功能"""
from nmcsup.log import log
# 输入一个列表 [ [str, float ], [], ... ] 音符str 值为持续时间float
def note2list(Notes : list) -> list:
from nmcsup.const import notes
def change(base):
enwo = {
'a': 'A',
'b': 'B',
'c': 'C',
'd': "D",
"e": "E",
'f': 'F',
'g': "G"
}
nuwo = {
'6': 'A',
'7': 'B',
'1': 'C',
'2': "D",
"3": "E",
'4': 'F',
'5': "G"
}
for k, v in enwo.items():
if k in base:
base = base.replace(k, v)
for k, v in nuwo.items():
if k in base:
base = base.replace(k, v)
return base
res = []
log(" === 音符列表=>音调列表")
for i in Notes:
s2 = change(i[0])
log(' === 正在操作音符'+i[0]+'->'+s2)
if s2 in notes.keys():
log(" === 找到此音符,加入:"+str(notes[s2][0]))
res.append([notes[s2][0], float(i[1])])
else:
log(' === '+s2+'不在音符表内,此处自动替换为 休止符0 ')
res.append(['0', float(i[1])])
log(' === 最终反回'+str(res))
return res
def mcnote2freq(Notes):
from nmcsup.const import notes
mcnback = {}
for i,j in notes.items():
mcnback[j[0]] = i
res = []
log(" === 我的世界音调表=>频率列表")
for i in Notes:
log(' === 正在操作音符'+i[0]+'->'+mcnback[i[0]])
res.append([notes[mcnback[i[0]]][1], float(i[1])])
log(' === 最终反回'+str(res))
return res
#MP3文件转midi文件
def Mp32Mid(mp3File, midFile):
from piano_transcription_inference import PianoTranscription, sample_rate, load_audio
# 加载
(audio, _) = load_audio(mp3File, sr=sample_rate, mono=True)
# 实例化并转换
PianoTranscription(device="cpu").transcribe(audio, midFile)
#传入一个音符列表转为指令列表
def Note2Cmd(Notes : list,ScoreboardName:str,Instrument:str, PlayerSelect:str='',isProsess:bool=False) -> list:
commands = []
a = 0.0
if isProsess:
length = len(Notes)
j = 1
for i in range(len(Notes)):
commands.append("execute @a"+PlayerSelect+" ~ ~ ~ execute @s[scores={"+ScoreboardName+"="+str(int((a+2)*5+int(Notes[i][1]*5)))+"}] ~ ~ ~ playsound "+Instrument+" @s ~ ~ ~ 1000 "+str(Notes[i][0])+" 1000\n")
a += Notes[i][1]
if isProsess:
commands.append("execute @a"+PlayerSelect+" ~ ~ ~ execute @s[scores={"+ScoreboardName+"="+str(int((a+2)*5+int(Notes[i][1]*5)))+"}] ~ ~ ~ title @s actionbar §e▶ 播放中: §a"+str(j)+"/"+str(length)+" || "+str(int(j/length*1000)/10)+"\n")
j+=1
commands.append("\n\n# 凌云我的世界开发团队 x 凌云软件开发团队 : W-YI金羿\n")
return commands
import amulet
import amulet_nbt
from amulet.api.block import Block
from amulet.api.block_entity import BlockEntity
from amulet.utils.world_utils import block_coords_to_chunk_coords
from amulet_nbt import TAG_String,TAG_Compound,TAG_Byte
#简单载入方块
#level.set_version_block(posx,posy,posz,"minecraft:overworld",("bedrock", (1, 16, 20)),Block(namespace, name))
#转入指令列表与位置信息转至世界
def Cmd2World(cmd:list,world:str,dire:list):
'''将指令以命令链的形式载入世界\n
cmd指令列表位为一个序列中包含指令字符串\n
world为地图所在位置需要指向文件夹dire为指令方块生成之位置'''
level = amulet.load_level(world)
cdl = []
for i in cmd:
try:
if (i[:i.index('#')].replace(' ','') != '\n') and(i[:i.index('#')].replace(' ','') != ''):
cdl.append(i[:i.index('#')])
except:
cdl.append(i)
i = 0
#第一个是特殊
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('up'),'mode':TAG_String("repeating")})
cx, cz = block_coords_to_chunk_coords(dire[0], dire[2])
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = dire[0] - 16 * cx, dire[2] - 16 * cz
universal_block_entity = BlockEntity( 'universal_minecraft','command_block',dire[0],dire[1],dire[2],amulet_nbt.NBTFile(TAG_Compound({'utags': TAG_Compound({'auto': TAG_Byte(0),'Command': TAG_String(cdl.pop(0))}) })))
chunk.blocks[offset_x, dire[1], offset_z] = level.block_palette.get_add_block(universal_block)
chunk.block_entities[(dire[0], dire[1], dire[2])] = universal_block_entity
chunk.changed = True
#集体上移
dire[1]+=1;
#真正开始
down = False
for j in cdl:
if dire[1]+i >= 255:
dire[0]+=1
i=0
down = not down
#定义此方块
if dire[1]+i == 254 :
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('east'),'mode':TAG_String("chain")})
else:
if down:
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('down'),'mode':TAG_String("chain")})
else:
universal_block = Block('universal_minecraft','command_block',{'conditional':TAG_String("false"),'facing':TAG_String('up'),'mode':TAG_String("chain")})
cx, cz = block_coords_to_chunk_coords(dire[0], dire[2])
#获取区块
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = dire[0] - 16 * cx, dire[2] - 16 * cz
if down:
#定义方块实体
universal_block_entity = BlockEntity( 'universal_minecraft','command_block',dire[0],254-i,dire[2],amulet_nbt.NBTFile(TAG_Compound({'utags': TAG_Compound({'auto': TAG_Byte(1),'Command': TAG_String(j)}) })))
#将方块加入世界
chunk.blocks[offset_x, 254-i, offset_z] = level.block_palette.get_add_block(universal_block)
chunk.block_entities[(dire[0], 254-i, dire[2])] = universal_block_entity
else:
#定义方块实体
universal_block_entity = BlockEntity( 'universal_minecraft','command_block',dire[0],dire[1]+i,dire[2],amulet_nbt.NBTFile(TAG_Compound({'utags': TAG_Compound({'auto': TAG_Byte(1),'Command': TAG_String(j)}) })))
#将方块加入世界
chunk.blocks[offset_x, dire[1]+i, offset_z] = level.block_palette.get_add_block(universal_block)
chunk.block_entities[(dire[0], dire[1]+i, dire[2])] = universal_block_entity
#设置为已更新区块
chunk.changed = True
i+=1
del i, cdl
#保存世界并退出
level.save()
level.close()
#音符转成方块再加载到世界里头
def Blocks2World(world:str,dire:list,Datas:list):
from nmcsup.const import Blocks
level = amulet.load_level(world)
i = 0
def setblock(block:Block,pos:list):
'''pos : list[int,int,int]'''
cx, cz = block_coords_to_chunk_coords(pos[0], pos[2])
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
offset_x, offset_z = pos[0] - 16 * cx, pos[2] - 16 * cz
chunk.blocks[offset_x, pos[1], offset_z] = level.block_palette.get_add_block(block)
chunk.changed = True
for j in Datas:
if dire[1]+1 >= 255:
i = 0
dire[0]+=1
setblock(Blocks[j[0]],[dire[0],dire[1]+i,dire[2]])
i = int(i+j[1]+0.5) #四舍五入
level.save()
level.close()
#传入音符列表制作播放器指令
def Notes2Player(Note,dire:list,CmdData:dict):
'''传入音符列表、坐标、指令数据,生成播放器指令'''
Notes = {}
for i in Note:
Notes[i[0]] = ''
Notes = list(Notes.keys())
from nmcsup.const import Blocks
Cmds = []
for j in Notes:
Cmds.append('execute @e[x='+str(dire[0])+',y='+str(dire[1])+',z='+str(dire[2])+',dy='+str(255-dire[1])+',name='+CmdData['Ent']+'] ~ ~ ~ detect ~ ~ ~ '+Blocks[j]+' 0 execute @a '+CmdData['Pls']+' ~ ~ ~ playsound '+CmdData['Ins']+' @s ~ ~ ~ 1000 '+str(j)+' 1000\n')
Cmds+=['#本函数由 金羿 音·创 生成\n','execute @e[y='+str(dire[1])+',dy='+str(255-dire[1])+',name='+CmdData['Ent']+'] ~ ~ ~ tp ~ ~1 ~\n','execute @e[y=255,dy=100,name='+CmdData['Ent']+'] ~ ~ ~ tp ~1 '+str(dire[1])+' ~\n','#音·创 开发交流群 861684859']
return Cmds
#传入音符列表生成方块至世界
def Datas2BlkWorld(NoteData,world:str,dire:list):
for i in range(len(NoteData)):
Blocks2World(world,[dire[0],dire[1],dire[2]+i],NoteData[i])
def World2Rys(world:str,startp:list,endp:list,isAir:bool=False ):
level = amulet.load_level(world)
RyStruct = {}
for x in range(startp[0],endp[0]):
for y in range(startp[1],endp[1]):
for z in range(startp[2],endp[2]):
RyStructBlock = {}
cx, cz = block_coords_to_chunk_coords(x, z)
chunk = level.get_chunk(cx, cz, "minecraft:overworld")
universal_block = chunk.block_palette[chunk.blocks[x - 16 * cx, y, z - 16 * cz]]
if universal_block == Block("universal_minecraft","air") and isAir:
continue
universal_block_entity = chunk.block_entities.get((x, y, z), None)
universal_block.namespace
universal_block_entity.namespace
#not be finished
'''
RyStruct = {
(0,0,0) = {
"block":{
"namespace":"",
"base_name":"",
"properties":{}
},
"block_entity":{
"namespace":"",
"base_name":"",
}
}
}
'''

86
nmcsup/vers.py Normal file
View File

@ -0,0 +1,86 @@
"""音创系列版本号和版本操作函数"""
VER = ('0.0.3.4','Beta',)
'''当前版本'''
LIBS = ('mido','amulet','amulet-core','amulet-nbt','piano_transcription_inference','pypinyin','briefcase','toga','pyinstaller','kivy','py7zr')
'''当前所需库'''
#判断版本、临时文件与补全库
def compver(ver1, ver2):
"""
传入不带英文的版本号,特殊情况"10.12.2.6.5">"10.12.2.6"
:param ver1: 版本号1
:param ver2: 版本号2
:return: ver1< = >ver2返回-1/0/1
"""
list1 = str(ver1).split(".")
list2 = str(ver2).split(".")
# 循环次数为短的列表的len
for i in range(len(list1)) if len(list1) < len(list2) else range(len(list2)):
if int(list1[i]) == int(list2[i]):
pass
elif int(list1[i]) < int(list2[i]):
return -1
else:
return 1
# 循环结束,哪个列表长哪个版本号高
if len(list1) == len(list2):
return 0
elif len(list1) < len(list2):
return -1
else:
return 1
#
# ————————————————
# 版权声明上面的函数compver为CSDN博主「基友死得早」的原创文章中的函数遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明。
# 原文链接https://blog.csdn.net/tinyjm/article/details/93514261
# ————————————————
#
import os
def InstallLibs(now,LIBS):
from os import system as run
'''比对库信息并安装库'''
for i in LIBS:
if not i in now:
print("安装库:"+i)
run("python -m pip install "+i+" -i https://pypi.tuna.tsinghua.edu.cn/simple")
def chkver(ver = VER,libs = LIBS):
'''通过文件比对版本信息并安装库'''
if not os.path.exists(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct'):
print("新安装库")
os.makedirs(os.getenv('APPDATA')+'\\Musicreater\\')
with open(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct', 'w') as f:
f.write(ver[0]+'\n')
for i in libs:
f.write(i+'\n')
InstallLibs([],libs)
else:
with open(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct', 'r') as f:
v = f.readlines()
cp = compver(ver[0], v[0])
if cp != 0:
InstallLibs(v[1:],libs)
with open(os.getenv('APPDATA')+'\\Musicreater\\msct.ActiveDatas.msct', 'w') as f:
f.write(ver[0]+'\n')
for i in libs:
f.write(i+'\n')
del cp
def resetver():
'''重置版本信息'''
import shutil
shutil.rmtree(os.getenv('APPDATA')+'\\Musicreater\\')

3
testPythonVersion.py Normal file
View File

@ -0,0 +1,3 @@
import sys
print("当前\n"+sys.executable+"\n版本为:\n"+sys.version)
input()

27
查看代码数.py Normal file
View File

@ -0,0 +1,27 @@
# -*- conding: utf8 -*-
import os
from msctspt.funcOpera import keepart
l = 0
for path,dir_list,file_list in os.walk(r"./") :
for file_name in file_list:
if keepart(file_name,'.',None) == '.py':
file = os.path.join(path, file_name)
print("得到文件名:"+str(file))
for i in open(file,'r',encoding="utf-8"):
code = i.replace(' ','').replace('\n','')
try:
code -= code[code.index('#'):]
except:
pass;
if code:
print("\t"+code)
l+=1
else:
pass;
input("\n最终代码行数为:"+str(l))

1
测试用/Canyon.msct Normal file

File diff suppressed because one or more lines are too long

View File

Binary file not shown.

View File

@ -0,0 +1 @@
MANIFEST-000060

View File

Binary file not shown.

View File

@ -0,0 +1 @@
[]

Binary file not shown.

View File

@ -0,0 +1 @@
476168

BIN
测试用/HarryPorter.mid Normal file

Binary file not shown.

BIN
测试用/canyon.mid Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
from nmcsup.vers import chkver
chkver()

5
遍历代码.py Normal file
View File

@ -0,0 +1,5 @@
file = open(input("FileName"),'r',encoding=input("Encodeing")).read()
for i in file: