diff --git a/nbt/read.go b/nbt/read.go index 9767168..2f7ce11 100644 --- a/nbt/read.go +++ b/nbt/read.go @@ -296,7 +296,6 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err } case TagCompound: - var buf map[string]interface{} switch vk := val.Kind(); vk { default: return errors.New("cannot parse TagCompound as " + vk.String()) @@ -323,12 +322,28 @@ func (d *Decoder) unmarshal(val reflect.Value, tagType byte, tagName string) err } } case reflect.Map: - if !reflect.TypeOf(buf).AssignableTo(val.Type()) { - return errors.New("cannot parse TagCompound as " + vk.String()) + if val.Type().Key().Kind() != reflect.String { + return errors.New("cannot parse TagCompound as " + val.Type().String()) + } + if val.IsNil() { + val.Set(reflect.MakeMap(val.Type())) + } + for { + tt, tn, err := d.readTag() + if err != nil { + return err + } + if tt == TagEnd { + break + } + v := reflect.New(val.Type().Elem()) + if err = d.unmarshal(v.Elem(), tt, tn); err != nil { + return err + } + val.SetMapIndex(reflect.ValueOf(tn), v.Elem()) } - fallthrough case reflect.Interface: - buf = make(map[string]interface{}) + buf := make(map[string]interface{}) for { tt, tn, err := d.readTag() if err != nil { diff --git a/save/level.go b/save/level.go index cc5510d..63d1aa2 100644 --- a/save/level.go +++ b/save/level.go @@ -1,4 +1,70 @@ package save +import ( + "github.com/Tnze/go-mc/nbt" + "io" +) + type Level struct { + Data struct { + DataVersion int32 + NBTVersion int32 `nbt:"version"` + Version struct { + ID int32 `nbt:"Id"` + Name string + Snapshot byte + } + GameType int32 + Difficulty byte + DifficultyLocked byte + HardCore byte `nbt:"hardcore"` + Initialized byte `nbt:"initialized"` + AllowCommands byte `nbt:"allowCommands"` + + MapFeatures byte + LevelName string + GeneratorName string `nbt:"generatorName"` + GeneratorVersion int32 `nbt:"generatorVersion"` + RandomSeed int64 + + SpawnX, SpawnY, SpawnZ int32 + + BorderCenterX, BorderCenterZ float64 + BorderDamagePerBlock float64 + BorderSafeZone float64 + BorderSize float64 + BorderSizeLerpTarget float64 + BorderSizeLerpTime int64 + BorderWarningBlocks float64 + BorderWarningTime float64 + + GameRules map[string]string + DataPacks struct { + Enabled, Disabled []string + } + DimensionData struct { + TheEnd struct { + DragonFight struct { + Gateways []int32 + DragonKilled byte + PreviouslyKilled byte + } + } `nbt:"1"` + } + + Raining byte `nbt:"raining"` + Thundering byte `nbt:"thundering"` + RainTime int32 `nbt:"rainTime"` + ThunderTime int32 `nbt:"thunderTime"` + ClearWeatherTime int32 `nbt:"clearWeatherTime"` + + Time int64 + DayTime int64 + LastPlayed int64 + } +} + +func ReadLevel(r io.Reader) (data Level, err error) { + err = nbt.NewDecoder(r).Decode(&data) + return } diff --git a/save/level_test.go b/save/level_test.go new file mode 100644 index 0000000..4b9aa6c --- /dev/null +++ b/save/level_test.go @@ -0,0 +1,35 @@ +package save + +import ( + "compress/gzip" + "os" + "testing" +) + +func TestLevel(t *testing.T) { + f, err := os.Open("testdata/level.dat") + if err != nil { + t.Fatal(err) + } + + r, err := gzip.NewReader(f) + if err != nil { + t.Fatal(err) + } + + data, err := ReadLevel(r) + if err != nil { + t.Fatal(err) + } + + //want := PlayerData{ + // Pos: [3]float64{-41.5, 65, -89.5}, + // Motion: [3]float64{0, -0.0784000015258789, 0}, + // Rotation: [2]float32{0,0}, + //} + + t.Logf("%+v", data) + //if data != want { + // t.Errorf("player data parse error: get %v, want %v", data, want) + //} +}