taolu
本帖最后由 taolu 于 2020-8-22 20:24 编辑

前言
可能有些人的程序需要对Schematic格式的支持,所以我自己摸索了几天(其实还是去年暑假了)。

其实Wiki里有关于Schematic的格式的介绍,但是我自己通过导出MCEdit发现和Wiki中有些不一样,比如有些项目我导出来并没有,这里我就按我导出的的文件格式讲一下Schematic的格式。
格式
对象
作用
大小
Schematic识别Schematic格式12字节
Height高度Y 最大64K(mod突破高度限制?)2字节
Length长度Z 最大64K2字节
Width宽度X 最大64K2字节
Entities实体不固定默认8字节
TileEntitiesNBT不固定默认8字节
TileTicks方块要更新的数据不固定默认8字节
Materials版本 默认Alpha 随意填其他没有事不固定
Data方块数据 也就是setblock后面那个数字X × Y × Z字节
Biomes每个方块的生物群系X × Z字节
Block方块(数字ID, 1. 8及之后可以使用MCEdit-Unified查看)X × Y × Z字节
名称
数字ID
数字ID(16进制)
海洋00
平原11
沙漠22
山地33
森林44
针叶林55
沼泽66
河流77
下界荒地88
末地99
冻洋10A
冻河11B
积雪的冻原12C
雪山13D
蘑菇岛14E
蘑菇岛岸15F
沙滩1610
沙漠丘陵1711
繁茂的丘陵1812
针叶林丘陵1913
山地边缘2014
丛林2115
丛林丘陵2216
丛林边缘2317
深海2418
石岸2519
积雪的沙滩261A
桦木森林271B
桦木森林丘陵281C
黑森林291D
积雪的针叶林301E
积雪的针叶林丘陵311F
巨型针叶林3220
巨型针叶林丘陵3321
繁茂的山地3422
热带草原3523
热带高原3624
恶地3725
繁茂的恶地高原3826
恶地高原3927
末地小型岛屿4028
末地中型岛屿4129
末地高岛422A
末地荒岛432B
暖水海洋442C
温水海洋452D
冷水海洋462E
暖水深海472F
温水深海4830
冷水深海4931
封冻深海5032
虚空1277F
向日葵平原12981
沙漠湖泊13082
沙砾山地13183
繁花森林13284
针叶林山地13385
沼泽山丘13486
冰刺平原1408C
丛林变种14995
丛林边缘变种15197
高大桦木森林1559B
高大桦木丘陵1569C
黑森林丘陵1579D
积雪的针叶林山地1589E
巨型云杉针叶林160A0
巨型云杉针叶林丘陵161A1
沙砾山地+162A2
破碎的热带草原163A3
破碎的热带高原164A4
被风蚀的恶地165A5
繁茂的恶地高原变种166A6
恶地高原变种167A7
竹林168A8
竹林丘陵169A9
灵魂沙峡谷170AA
绯红森林171AB
诡异森林172AC
玄武岩三角洲173AD
识别对象
0x0A 0x00Schematic文件
0x02 0x00Height,Length,Width
0x09 0x00Entities,TileEntities,TileTicks
0x08 0x00Materials
0x07 0x00Data,Biomes,Blocks

Schematic(gzip压缩后)

temp(gzip压缩前)
      个数据内(Width 和 Height为总的长和宽),只要在这个位置写入Block数据和Data数据就可以了。
      可能你不怎么理解,如果看一下这张图和实例也许就知道了

      可见图中红色方块在第5行第4列,代入公式:
            (5-1)×5+4=24 成立
      你也可以去试试其他方块,得到的结果是一样的。
      其实也能想象得出来,多了一维的情况下,公式变成了这样
            (所在高度-1)×总的行数×总的列数+(所在行数-1)×总的列数+所在列数
VB代码注意


2021.12 数据,可能有更多内容前言可能有些人的程序需要对Schematic格式的支持,所以我自己摸索了几天(其实还是去年暑假了)。
其实Wiki里有关于Schematic的格式的介绍,但是我自己通过导出MCEdit发现和Wiki中有些不一样,比如有些项目我导出来并没有,这里我就按我导出的的文件格式讲一下Schematic的格式。格式
对象
作用
大小
Schematic
识别Schematic格式
12字节
Height
高度Y 最大64K(mod突破高度限制?)
2字节
Length
长度Z 最大64K
2字节
Width
宽度X 最大64K
2字节
Entities
实体
不固定默认8字节
TileEntities
NBT
不固定默认8字节
TileTicks
方块要更新的数据
不固定默认8字节
Materials
版本 默认Alpha 随意填其他没有事
不固定
Data
方块数据 也就是setblock后面那个数字
X × Y × Z字节
Biomes
每个方块的生物群系
X × Z字节
Block
方块(数字ID, 1. 8及之后可以使用MCEdit-Unified查看)
X × Y × Z字节
  • 如果生物群系对你有作用的话,请看下表
名称
数字ID
数字ID(16进制)
海洋
0
0
平原
1
1
沙漠
2
2
山地
3
3
森林
4
4
针叶林
5
5
沼泽
6
6
河流
7
7
下界荒地
8
8
末地
9
9
冻洋
10
A
冻河
11
B
积雪的冻原
12
C
雪山
13
D
蘑菇岛
14
E
蘑菇岛岸
15
F
沙滩
16
10
沙漠丘陵
17
11
繁茂的丘陵
18
12
针叶林丘陵
19
13
山地边缘
20
14
丛林
21
15
丛林丘陵
22
16
丛林边缘
23
17
深海
24
18
石岸
25
19
积雪的沙滩
26
1A
桦木森林
27
1B
桦木森林丘陵
28
1C
黑森林
29
1D
积雪的针叶林
30
1E
积雪的针叶林丘陵
31
1F
巨型针叶林
32
20
巨型针叶林丘陵
33
21
繁茂的山地
34
22
热带草原
35
23
热带高原
36
24
恶地
37
25
繁茂的恶地高原
38
26
恶地高原
39
27
末地小型岛屿
40
28
末地中型岛屿
41
29
末地高岛
42
2A
末地荒岛
43
2B
暖水海洋
44
2C
温水海洋
45
2D
冷水海洋
46
2E
暖水深海
47
2F
温水深海
48
30
冷水深海
49
31
封冻深海
50
32
虚空
127
7F
向日葵平原
129
81
沙漠湖泊
130
82
沙砾山地
131
83
繁花森林
132
84
针叶林山地
133
85
沼泽山丘
134
86
冰刺平原
140
8C
丛林变种
149
95
丛林边缘变种
151
97
高大桦木森林
155
9B
高大桦木丘陵
156
9C
黑森林丘陵
157
9D
积雪的针叶林山地
158
9E
巨型云杉针叶林
160
A0
巨型云杉针叶林丘陵
161
A1
沙砾山地+
162
A2
破碎的热带草原
163
A3
破碎的热带高原
164
A4
被风蚀的恶地
165
A5
繁茂的恶地高原变种
166
A6
恶地高原变种
167
A7
竹林
168
A8
竹林丘陵
169
A9
灵魂沙峡谷
170
AA
绯红森林
171
AB
诡异森林
172
AC
玄武岩三角洲
173
AD
  • 谜?
    对于其中的高度限制,我还是不太清楚。虽然说有些Mod可以突破高度限制,但是似乎Mcedit也有高度限制。几次实验后发现如果高度大于255,那么高度好像会从第一行重新写入,也就是说把从第一行已经生成的方块给覆盖掉。如果只有255方块,那么为什么大小要设置成两个字节,难道一个字节不就能用完呢?希望有人能帮我解惑。
  • 格式中每个数据(姑且就这么叫吧)之间有指定的字符串去隔开,而在指定的字符串后还要声明此数据的长度。所以每一个数据的格式如下指定的分隔字节(两字节)+数据标题长度(一字节)+数据标题+数据长度(+数据)(当为Data,Biomes,Block…时)
识别对象
0x0A 0x00
Schematic文件
0x02 0x00
Height,Length,Width
0x09 0x00
Entities,TileEntities,TileTicks
0x08 0x00
Materials
0x07 0x00
Data,Biomes,Blocks
  • 知道了格式接下来就好做了。
    将数据写入文件,再通过同目录下的gzip.exe压缩成x.gz文件,最后重命名为Schematic文件即可用MCEdit打开。

Schematic(gzip压缩后)

temp(gzip压缩前)
  • 那么这里就讲一下data和block数据吧
    通过立体坐标计算那么坐标为(X,Y,Z)的方块的data和block数据位于第   (Y - 1) × (Width × Height) + ( Z - 1) × Width + X
  个数据内(Width 和 Height为总的长和宽),只要在这个位置写入Block数据和Data数据就可以了。  可能你不怎么理解,如果看一下这张图和实例也许就知道了

  • 按X和Z轴来进行,这里有一个5*5的二维平面。我们从第一个方块开始,给每一个方块标记数字。在二维中公式是这样的   (所在行数-1)×总的列数+所在列数
  可见图中红色方块在第5行第4列,代入公式:     (5-1)×5+4=24 成立  你也可以去试试其他方块,得到的结果是一样的。
   其实也能想象得出来,多了一维的情况下,公式变成了这样      (所在高度-1)×总的行数×总的列数+(所在行数-1)×总的列数+所在列数VB代码
  • Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
  • Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
  • Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
  • Private Sub Gen()
  • '....
  • LoopDB = BmpWidth * BmpHeight * BY '长*宽*高
  • LoopBI = BmpWidth * BmpHeight '长*宽
  • TempHX = Replace(Format(Hex(BmpWidth), "@@@@"), " ", "0") '转换成16进制的字符串,以便于写入文件
  • TempHY = Replace(Format(Hex(BY), "@@@@"), " ", "0")
  • TempHZ = Replace(Format(Hex(BmpHeight), "@@@@"), " ", "0")
  • TempHLoopDB = Replace(Format(Hex(LoopDB), "@@@@@@@@"), " ", "0")
  • TempHLoopBI = Replace(Format(Hex(LoopBI), "@@@@@@@@"), " ", "0")
  • HX = TempHX
  • HY = TempHY
  • HZ = TempHZ
  • HLoopDB = TempHLoopDB
  • HLoopBI = TempHLoopBI
  • ReDim StrDT(LoopDB)
  • ReDim StrBL(LoopDB)
  • For Y=1 To BY
  • For X=1 To BmpWidth
  • For Z=1 To BmpHeight
  • StrP = (Y - 1) * (BmpWidth * BmpHeight) + (Z - 1) * BmpWidth+ X '3维方块对应Block和Data数据的位置
  • StrBL(StrP) = m_Names(Block) '也许用结构体会好点?
  • StrDT(StrP) = m_Nbt(Block)
  • '....Block的处理
  • Next
  • Next
  • Next
  • Open App.Path & "\temp" For Binary As #1 '在当前目录下新建temp文件
  • Put #1, , Chr$(10) & Chr$(0) & Chr$(9) & "Schematic" '指定的分隔字符串(两字节)+数据标题长度(一字节)+数据标题+数据长度(+数据)
  • Put #1, , Chr$(2) & Chr$(0) & Chr$(6) & "Height"
  • Put #1, , CByte("&H" & CStr(Left(HY, 2))) '数据的长度(要以二进制的文件写入)
  • Put #1, , CByte("&H" & CStr(Right(HY, 2)))
  • Put #1, , Chr$(2) & Chr$(0) & Chr$(6) & "Length"
  • Put #1, , CByte("&H" & CStr(Left(HZ, 2)))
  • Put #1, , CByte("&H" & CStr(Right(HZ, 2)))
  • Put #1, , Chr$(2) & Chr$(0) & Chr$(5) & "Width"
  • Put #1, , CByte("&H" & CStr(Left(HX, 2)))
  • Put #1, , CByte("&H" & CStr(Right(HX, 2)))
  • Put #1, , Chr$(9) & Chr$(0) & Chr$(8) & "Entities" & Chr$(1) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(9) & Chr$(0) & Chr$(12) & "TileEntities" & Chr$(1) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(9) & Chr$(0) & Chr$(9) & "TileTicks" & Chr$(1) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(8) & Chr$(0) & Chr$(9) & "Materials" & Chr$(0) & Chr$(5) & "Alpha" & Chr$(7) & Chr$(0) & Chr$(4) & "Data"
  • Put #1, , CByte("&H" & CStr(Left(HLoopDB, 2)))
  • Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 3, 2)))
  • Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 5, 2)))
  • Put #1, , CByte("&H" & CStr(Right(HLoopDB, 2)))
  • For I = 0 To LoopDB - 1
  •     If Len(StrDT(I)) = 0 Then
  •   Put #1, , Chr$(0)
  •     Else
  •   Put #1, , CByte(StrDT(I)) 'VB的bug?直接Put #1, , StrDT每两个字节之间会多出00
  •     End If
  • Next
  • Put #1, , Chr$(7) & Chr$(0) & Chr$(6) & "Biomes"
  • Put #1, , CByte("&H" & CStr(Left(HLoopBI, 2)))
  • Put #1, , CByte("&H" & CStr(Mid(HLoopBI, 3, 2)))
  • Put #1, , CByte("&H" & CStr(Mid(HLoopBI, 5, 2)))
  • Put #1, , CByte("&H" & CStr(Right(HLoopBI, 2)))
  • For I = 0 To LoopBI - 1
  •   Put #1, , Chr$(0)
  • Next
  • Put #1, , Chr$(7) & Chr$(0) & Chr$(6) & "Blocks"
  • Put #1, , CByte("&H" & CStr(Left(HLoopDB, 2)))
  • Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 3, 2)))
  • Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 5, 2)))
  • Put #1, , CByte("&H" & CStr(Right(HLoopDB, 2)))
  • For I = 0 To LoopDB - 1
  •     If Len(StrBL(I)) = 0 Then
  •   Put #1, , Chr$(0)
  •     Else
  •   Put #1, , CByte(StrBL(I))
  •     End If
  • Next
  • Put #1, , Chr$(0)
  • Close #1
  • OutPut
  • End Sub
  • Private Sub OutPut()
  • Dim Path As String
  • Dim I As Long, R As Long, P As Long
  •     Path = SaveFile()'文件对话框,这里不多讲了,此时Path已经成为一个Schematic的目录
  •     I = Shell(App.Path & "\gzip.exe -f """ & App.Path & "\temp""", vbNormalFocus) '调用Gzip压缩(输出为.gz文件)
  •     P = OpenProcess(SYNCHRONIZE, False, I) '等待进程(文件都没有生成怎么进行下一步啊喂)
  •     R = WaitForSingleObject(P, INFINITE)
  •     R = CloseHandle(P)
  •     I = Shell("cmd /c copy """ & App.Path & "\temp.gz"" " & Path & " /y", vbNormalFocus)'重命名成.Schematic文件
  •     P = OpenProcess(SYNCHRONIZE, False, I)
  •     R = WaitForSingleObject(P, INFINITE)
  •     R = CloseHandle(P)
  •     I = Shell("cmd /c del /f /q """ & App.Path & "\temp.gz""", vbNormalFocus)'删除Temp
  •     P = OpenProcess(SYNCHRONIZE, False, I)
  •     R = WaitForSingleObject(P, INFINITE)
  •     R = CloseHandle(P)
  • End Sub


注意
  • 在VB写入文件时,方块等数据不能使用Chr,这是因为当Chr的Acsii编码超过128的都会被翻译成”?”,所以需要通过写入Binary文件,调用CByte来写入文件。
  • 除数据标题以外以上数据都以二进制的方式写入文件。
  • 数据可以调换顺序。




ShuoChenFeng
欸,如果有支持基岩版的schematic制作软件就好了

Loli_Wolf
投影mod的sch文件不通用吗

kuqingshu
应该是可以互相用的吧

1930940598
新版本还有高度限制吗

1930940598
有些东西好厉害的样子看不懂

taolu
1930940598 发表于 2020-3-3 16:03
新版本还有高度限制吗

这个是根据Mcedit和投影mod的来的,我这里使用mcedit加载高度超过255的Schematic文件如果超过的话会会从Y=1重新写入,也就是第一层被256层给覆盖了。虽然说这个高度的值为2个字节,也就是65535层高,但是也就用了一个字节(255层)如果你要硬试的话,加上一个更高世界的mod可能会出错的(mod存储大于255层的方块和mc存档中的mca存档可能不是一样的)。

xiaozhang421
good job!!!

第一页 上一页 下一页 最后一页