fix: schematic versions

This commit is contained in:
trafficlunar 2025-01-23 19:18:43 +00:00
parent e6788d529a
commit 6ab167e7db
5 changed files with 59 additions and 22 deletions

View file

@ -12,8 +12,8 @@ const versions = [
"1.21.4", "1.21.4",
"1.21", "1.21",
"1.20", "1.20",
"1.18",
"1.19", "1.19",
"1.18",
"1.17", "1.17",
"1.16", "1.16",
"1.15", "1.15",

View file

@ -7,18 +7,24 @@ import * as nbt from "nbtify";
import { CanvasContext } from "@/context/Canvas"; import { CanvasContext } from "@/context/Canvas";
import { LoadingContext } from "@/context/Loading"; import { LoadingContext } from "@/context/Loading";
import { decodeVarint } from "@/utils/varint";
import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import _blockData from "@/data/blocks/data.json"; import _blockData from "@/data/blocks/data.json";
const blockData: BlockData = _blockData; const blockData: BlockData = _blockData;
import _versionData from "@/data/versions.json";
const versionData: Record<string, number> = _versionData;
interface LitematicaBlockPalette extends nbt.CompoundTagLike { interface LitematicaBlockPalette extends nbt.CompoundTagLike {
Name: string; Name: string;
Properties?: Record<string, string>; Properties?: Record<string, string>;
} }
interface LitematicNBT extends nbt.ListTagLike { interface LitematicNBT extends nbt.ListTagLike {
MinecraftDataVersion: number;
Regions: { Regions: {
Image: { Image: {
BlockStatePalette: LitematicaBlockPalette[]; BlockStatePalette: LitematicaBlockPalette[];
@ -34,13 +40,14 @@ interface SpongeNBT extends nbt.ListTagLike {
Data: Int8Array; Data: Int8Array;
Palette: Record<string, number>; Palette: Record<string, number>;
}; };
DataVersion: number;
Width: number; Width: number;
Height: number; Height: number;
}; };
} }
function OpenSchematic({ close }: DialogProps) { function OpenSchematic({ close }: DialogProps) {
const { setBlocks } = useContext(CanvasContext); const { setBlocks, setVersion } = useContext(CanvasContext);
const { setLoading } = useContext(LoadingContext); const { setLoading } = useContext(LoadingContext);
const { acceptedFiles, getRootProps, getInputProps } = useDropzone({ const { acceptedFiles, getRootProps, getInputProps } = useDropzone({
@ -61,16 +68,20 @@ function OpenSchematic({ close }: DialogProps) {
const data = await nbt.read(bytes); const data = await nbt.read(bytes);
if (fileExtension == "litematic") { if (fileExtension == "litematic") {
const litematicData = (data as nbt.NBTData<LitematicNBT>).data.Regions.Image; const litematicData = (data as nbt.NBTData<LitematicNBT>).data;
const litematicRegionData = litematicData.Regions.Image;
const litematicVersion = Object.keys(versionData).find((key) => versionData[key] == litematicData.MinecraftDataVersion);
// todo: set version // Set version to schematic version. If it doesn't find it in the data, return the latest version
const requiredBits = Math.max(Math.ceil(Math.log2(litematicData.BlockStatePalette.length)), 2); setVersion(parseInt(litematicVersion || Object.keys(versionData)[0]));
const requiredBits = Math.max(Math.ceil(Math.log2(litematicRegionData.BlockStatePalette.length)), 2);
const getPaletteIndex = (index: number): bigint => { const getPaletteIndex = (index: number): bigint => {
const originalY = Math.floor(index / litematicData.Size.x); const originalY = Math.floor(index / litematicRegionData.Size.x);
const originalX = index % litematicData.Size.x; const originalX = index % litematicRegionData.Size.x;
const reversedY = litematicData.Size.y - 1 - originalY; const reversedY = litematicRegionData.Size.y - 1 - originalY;
const reversedIndex = reversedY * litematicData.Size.x + originalX; const reversedIndex = reversedY * litematicRegionData.Size.x + originalX;
// getAt() implementation - LitematicaBitArray.java // getAt() implementation - LitematicaBitArray.java
const startOffset = reversedIndex * requiredBits; const startOffset = reversedIndex * requiredBits;
@ -80,11 +91,12 @@ function OpenSchematic({ close }: DialogProps) {
const mask = (1 << requiredBits) - 1; const mask = (1 << requiredBits) - 1;
if (startArrayIndex === endArrayIndex) { if (startArrayIndex === endArrayIndex) {
return (litematicData.BlockStates[startArrayIndex] >> BigInt(bitOffset)) & BigInt(mask); return (litematicRegionData.BlockStates[startArrayIndex] >> BigInt(bitOffset)) & BigInt(mask);
} else { } else {
const endOffset = 64 - bitOffset; const endOffset = 64 - bitOffset;
return ( return (
((litematicData.BlockStates[startArrayIndex] >> BigInt(bitOffset)) | (litematicData.BlockStates[endArrayIndex] << BigInt(endOffset))) & ((litematicRegionData.BlockStates[startArrayIndex] >> BigInt(bitOffset)) |
(litematicRegionData.BlockStates[endArrayIndex] << BigInt(endOffset))) &
BigInt(mask) BigInt(mask)
); );
} }
@ -94,11 +106,11 @@ function OpenSchematic({ close }: DialogProps) {
const blocks: Block[] = []; const blocks: Block[] = [];
let index = 0; let index = 0;
for (let y = 0; y < litematicData.Size.y; y++) { for (let y = 0; y < litematicRegionData.Size.y; y++) {
for (let x = 0; x < litematicData.Size.x; x++) { for (let x = 0; x < litematicRegionData.Size.x; x++) {
const paletteIndex = Number(getPaletteIndex(index)); const paletteIndex = Number(getPaletteIndex(index));
console.log(paletteIndex); console.log(paletteIndex);
const paletteBlock = litematicData.BlockStatePalette[paletteIndex]; const paletteBlock = litematicRegionData.BlockStatePalette[paletteIndex];
index++; index++;
if (!paletteBlock) continue; if (!paletteBlock) continue;
@ -134,7 +146,10 @@ function OpenSchematic({ close }: DialogProps) {
setBlocks(blocks); setBlocks(blocks);
} else if (fileExtension == "schem") { } else if (fileExtension == "schem") {
const spongeData = (data as nbt.NBTData<SpongeNBT>).data.Schematic; const spongeData = (data as nbt.NBTData<SpongeNBT>).data.Schematic;
// todo: set version const schematicVersion = Object.keys(versionData).find((key) => versionData[key] == spongeData.DataVersion);
// Set version to schematic version. If it doesn't find it in the data, return the latest version
setVersion(parseInt(schematicVersion || Object.keys(versionData)[0]));
// Add every block to the canvas // Add every block to the canvas
const blocks: Block[] = []; const blocks: Block[] = [];

View file

@ -11,8 +11,11 @@ import { Input } from "@/components/ui/input";
import _blockData from "@/data/blocks/data.json"; import _blockData from "@/data/blocks/data.json";
const blockData: BlockData = _blockData; const blockData: BlockData = _blockData;
import _versionData from "@/data/versions.json";
const versionData: Record<string, number> = _versionData;
function SaveLitematic({ close }: DialogProps) { function SaveLitematic({ close }: DialogProps) {
const { canvasSize, blocks } = useContext(CanvasContext); const { canvasSize, blocks, version } = useContext(CanvasContext);
const { setLoading } = useContext(LoadingContext); const { setLoading } = useContext(LoadingContext);
const [fileName, setFileName] = useState("blockmatic"); const [fileName, setFileName] = useState("blockmatic");
@ -92,7 +95,7 @@ function SaveLitematic({ close }: DialogProps) {
// Generate NBT data // Generate NBT data
const data = { const data = {
MinecraftDataVersion: new nbt.Int32(4189), MinecraftDataVersion: new nbt.Int32(versionData[version]),
Version: new nbt.Int32(7), Version: new nbt.Int32(7),
SubVersion: new nbt.Int32(1), SubVersion: new nbt.Int32(1),
Metadata: { Metadata: {

View file

@ -13,6 +13,9 @@ import { Input } from "@/components/ui/input";
import _blockData from "@/data/blocks/data.json"; import _blockData from "@/data/blocks/data.json";
const blockData: BlockData = _blockData; const blockData: BlockData = _blockData;
import _versionData from "@/data/versions.json";
const versionData: Record<string, number> = _versionData;
interface BlockEntity { interface BlockEntity {
Id: string; Id: string;
Pos: Int32Array; Pos: Int32Array;
@ -40,8 +43,8 @@ const blockEntitiesWhitelist = [
"vault", "vault",
]; ];
function SaveLitematic({ close }: DialogProps) { function SaveSchem({ close }: DialogProps) {
const { canvasSize, blocks } = useContext(CanvasContext); const { canvasSize, blocks, version } = useContext(CanvasContext);
const { setLoading } = useContext(LoadingContext); const { setLoading } = useContext(LoadingContext);
const [fileName, setFileName] = useState("blockmatic"); const [fileName, setFileName] = useState("blockmatic");
@ -130,7 +133,7 @@ function SaveLitematic({ close }: DialogProps) {
// Generate NBT data // Generate NBT data
const data = { const data = {
Schematic: { Schematic: {
DataVersion: new nbt.Int32(4189), DataVersion: new nbt.Int32(versionData[version]),
Version: new nbt.Int32(3), Version: new nbt.Int32(3),
Width: new nbt.Int16(width), Width: new nbt.Int16(width),
Height: new nbt.Int16(height), Height: new nbt.Int16(height),
@ -167,7 +170,7 @@ function SaveLitematic({ close }: DialogProps) {
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle>Save as .schem</DialogTitle> <DialogTitle>Save as .schem</DialogTitle>
<DialogDescription>Save your image as a .schem (Sponge)</DialogDescription> <DialogDescription>Save your image as a .schem (Sponge Version 3)</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -187,4 +190,4 @@ function SaveLitematic({ close }: DialogProps) {
); );
} }
export default SaveLitematic; export default SaveSchem;

16
src/data/versions.json Normal file
View file

@ -0,0 +1,16 @@
{
"1214": 4189,
"1210": 3953,
"1200": 3463,
"1190": 3105,
"1180": 2860,
"1170": 2724,
"1160": 2566,
"1150": 2225,
"1140": 1952,
"1130": 1519,
"1120": 1139,
"1110": 819,
"1000": 510,
"1090": 169
}