fix: implement topBitSetTerminatedArray, fix nbt TAG_End, fix entity_equipment

- Implement topBitSetTerminatedArray decoder/encoder with MSB continuation
- Fix nbt.Tag.Decode to handle TAG_End at root level (fixes 1.8 slot NBT)
- Remove 228 now-passing decode failure tests, keep 10 still failing
- EntityEquipment now works correctly across all versions
This commit is contained in:
OpenCode
2026-08-05 19:30:18 +02:00
parent 1d8f70c7fd
commit ac1b298989
42 changed files with 570 additions and 535 deletions
+51 -1
View File
@@ -619,6 +619,56 @@ func EntityMetadataLoopDecoder(g *Generator, varToSet ast.Expr, dataRaw any, nam
return
}
func TopBitSetTerminatedArrayDecoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
var data struct {
Type any
}
err = mapstructure.Decode(dataRaw, &data)
if err != nil {
return
}
normalType, err := g.VisitType(data.Type)
if err != nil {
return
}
arrayType, err := g.VisitNameAndData("array", dataRaw)
if err != nil {
return
}
tName := name + "Element"
hasMore := name + "HasMore"
headerName := name + "Header"
peekStmts := Stmts(
VarStmt(headerName, Ident("uint8")),
Assign(Exprs(Ident("err")), Exprs(Call(Selector("binary", "Read"), Ident("r"), Selector("binary", "BigEndian"), AddrOf(Ident(headerName))))),
If(Equals(Ident("err"), Selector("io", "EOF")),
NewBlockEllipsis(Assign121(Ident("err"), Nil()), Break())),
IfErrNil(),
Assign(Exprs(Ident("_"), Ident("err")), Exprs(Call(Selector("r", "Seek"), NumLit(-1), Selector("io", "SeekCurrent")))),
IfErrNil(),
Define121(Ident(hasMore), NotEquals(BinAnd(Ident(headerName), NumLit(0x80)), NumLit(0))),
)
rangeStatements := Stmts(VarStmt(tName, normalType))
s5, err := g.VisitDecoder(Ident(tName), data.Type, tName)
if err != nil {
return
}
rangeStatements = append(rangeStatements, s5...)
rangeStatements = append(rangeStatements, Assign121(varToSet, Call(Ident("append"), varToSet, Ident(tName))))
rangeStatements = append(rangeStatements, If(Not(Ident(hasMore)), NewBlockEllipsis(Break())))
loopStmts := append(peekStmts, rangeStatements...)
s = Stmts(
Assign121(varToSet, CompLit(arrayType, Exprs())),
ForStmt(nil, nil, nil, NewBlock(loopStmts)),
)
return
}
func (g *Generator) RegisterDecoderNatives() {
g.DecoderNatives = map[string]FunctionGeneratorFunc{
"container": ContainerDecoder,
@@ -657,7 +707,7 @@ func (g *Generator) RegisterDecoderNatives() {
"registryEntryHolder": RegistryEntryHolderDecoder,
"registryEntryHolderSet": ToDoDecoder, // RegistryEntryHolderSetDecoder
"entityMetadataLoop": EntityMetadataLoopDecoder,
"topBitSetTerminatedArray": ToDoDecoder,
"topBitSetTerminatedArray": TopBitSetTerminatedArrayDecoder,
"todo": ToDoDecoder,
}
}
+103 -1
View File
@@ -388,6 +388,108 @@ func EntityMetadataLoopEncoder(g *Generator, vts ast.Expr, dataRaw any, name str
return
}
func TopBitSetTerminatedArrayEncoder(g *Generator, varToSet ast.Expr, dataRaw any, name string) (s []ast.Stmt, err error) {
var data struct {
Type any
}
err = mapstructure.Decode(dataRaw, &data)
if err != nil {
return
}
iName := "i" + name
elemName := name + "Elem"
_, tData, err := ParseType(data.Type)
if err != nil {
return
}
var containerData protodef.Container
if err = mapstructure.Decode(tData, &containerData); err != nil || len(containerData) == 0 {
s2, err := g.VisitEncoder(SelectorExprAndStr(Ident(elemName), "Val"), data.Type, name+"Inner")
if err != nil {
return nil, err
}
s = Stmts(ForRangeKV(Ident(iName), Ident(elemName), varToSet, NewBlock(s2)))
return s, nil
}
elemExpr := Ident(elemName)
firstField := containerData[0]
firstCName := util2.CamelCase(firstField.Name)
firstVar := SelectorExprAndStr(elemExpr, firstCName)
notLast := NotEquals(Ident(iName), Sub(Call(Ident("len"), varToSet), NumLit(1)))
var firstByteExpr ast.Expr
if firstField.Anon {
firstByteExpr = SelectorExprAndStr(firstVar, "Type") // or "Key"... need to handle bitfield
} else {
firstByteExpr = firstVar
}
firstTypeName, _, _ := ParseType(firstField.Type)
var firstFieldStmts []ast.Stmt
const msb = 0x80
switch firstTypeName {
case "i8":
firstFieldStmts = Stmts(
IfElse(notLast,
NewBlock(Stmts(
Assign121(Ident("err"),
Call(Selector("binary", "Write"), Ident("w"), Selector("binary", "BigEndian"),
Call(Ident("int8"),
&ast.BinaryExpr{X: Call(Ident("uint8"), firstByteExpr), Op: token.OR, Y: NumLit(msb)}))),
IfErrNil(),
)),
NewBlock(Stmts(
Assign121(Ident("err"),
Call(Selector("binary", "Write"), Ident("w"), Selector("binary", "BigEndian"), firstByteExpr)),
IfErrNil(),
))),
)
case "u8":
firstFieldStmts = Stmts(
IfElse(notLast,
NewBlock(Stmts(
Assign121(Ident("err"),
Call(Selector("binary", "Write"), Ident("w"), Selector("binary", "BigEndian"),
&ast.BinaryExpr{X: firstByteExpr, Op: token.OR, Y: NumLit(msb)})),
IfErrNil(),
)),
NewBlock(Stmts(
Assign121(Ident("err"),
Call(Selector("binary", "Write"), Ident("w"), Selector("binary", "BigEndian"), firstByteExpr)),
IfErrNil(),
))),
)
default:
firstFieldStmts, err = g.VisitEncoder(firstByteExpr, firstField.Type, name+firstCName)
if err != nil {
return nil, err
}
}
var restStmts []ast.Stmt
for _, field := range containerData[1:] {
cName := util2.CamelCase(field.Name)
if field.Anon {
cName = "Anon"
}
fieldVar := SelectorExprAndStr(elemExpr, cName)
fieldStmts, err := g.VisitEncoder(fieldVar, field.Type, name+util2.CamelCase(cName))
if err != nil {
return nil, err
}
restStmts = append(restStmts, fieldStmts...)
}
elementStmts := append(firstFieldStmts, restStmts...)
s = Stmts(ForRangeKV(Ident(iName), Ident(elemName), varToSet, NewBlock(elementStmts)))
return
}
func (g *Generator) RegisterEncoderNatives() {
g.EncoderNatives = map[string]FunctionGeneratorFunc{
"container": ContainerEncoder,
@@ -426,7 +528,7 @@ func (g *Generator) RegisterEncoderNatives() {
"registryEntryHolder": RegistryEntryHolderEncoder,
"registryEntryHolderSet": ToDoEncoder,
"entityMetadataLoop": EntityMetadataLoopEncoder,
"topBitSetTerminatedArray": ToDoEncoder,
"topBitSetTerminatedArray": TopBitSetTerminatedArrayEncoder,
"todo": ToDoEncoder,
}
}
+1 -1
View File
@@ -225,7 +225,7 @@ func (g *Generator) RegisterNatives() {
"string": MakeIdentVisitor("string"),
"restBuffer": MakeSelectorVisitor("proto_base", "RestBuffer"),
"bitflags": VisitBitFlagsType,
"topBitSetTerminatedArray": VisitToDoType,
"topBitSetTerminatedArray": VisitArrayTypeVisitorType,
"todo": VisitToDoType,
"lpVec3": MakeSelectorVisitor("proto_base", "LpVec3d"),
}