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",
"1.20",
"1.18",
"1.19",
"1.18",
"1.17",
"1.16",
"1.15",

View file

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

View file

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

View file

@ -13,6 +13,9 @@ import { Input } from "@/components/ui/input";
import _blockData from "@/data/blocks/data.json";
const blockData: BlockData = _blockData;
import _versionData from "@/data/versions.json";
const versionData: Record<string, number> = _versionData;
interface BlockEntity {
Id: string;
Pos: Int32Array;
@ -40,8 +43,8 @@ const blockEntitiesWhitelist = [
"vault",
];
function SaveLitematic({ close }: DialogProps) {
const { canvasSize, blocks } = useContext(CanvasContext);
function SaveSchem({ close }: DialogProps) {
const { canvasSize, blocks, version } = useContext(CanvasContext);
const { setLoading } = useContext(LoadingContext);
const [fileName, setFileName] = useState("blockmatic");
@ -130,7 +133,7 @@ function SaveLitematic({ close }: DialogProps) {
// Generate NBT data
const data = {
Schematic: {
DataVersion: new nbt.Int32(4189),
DataVersion: new nbt.Int32(versionData[version]),
Version: new nbt.Int32(3),
Width: new nbt.Int16(width),
Height: new nbt.Int16(height),
@ -167,7 +170,7 @@ function SaveLitematic({ close }: DialogProps) {
<DialogContent>
<DialogHeader>
<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>
<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
}