mojang cant get their cases straight
i had to implement like 4 cases just to parse that file
This commit is contained in:
+168
-7
@@ -7,18 +7,179 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Level struct {
|
type Level struct {
|
||||||
DataPacks struct {
|
ClearWeatherTime int32 `nbt:"clearWeatherTime"`
|
||||||
Enabled []string
|
DataPacks DataPacks
|
||||||
Disabled []string
|
DayTime int64
|
||||||
}
|
WanderingTraderSpawnChance int32
|
||||||
|
GameType int32
|
||||||
|
WorldGenSettings WorldGenSettings
|
||||||
|
ScheduledEvents []interface{}
|
||||||
|
WasModded bool
|
||||||
|
ThunderTime int32 `nbt:"thunderTime"`
|
||||||
|
Difficulty int8
|
||||||
|
DragonFight DragonFight
|
||||||
|
Initialized bool
|
||||||
|
AllowCommands bool `nbt:"allowCommands"`
|
||||||
|
Thundering bool
|
||||||
|
LastPlayed int64
|
||||||
|
GameRules map[string]interface{}
|
||||||
|
RainTime int32 `nbt:"rainTime"`
|
||||||
|
DataVersion int32
|
||||||
|
Version Version
|
||||||
|
WanderingTraderSpawnDelay int32
|
||||||
|
Hardcore bool
|
||||||
|
Spawn Spawn
|
||||||
|
Raining bool
|
||||||
|
Player Player
|
||||||
|
DifficultyLocked bool
|
||||||
|
LevelName string
|
||||||
|
Time int64
|
||||||
|
ServerBrands []string
|
||||||
|
CustomBossEvents map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DataPacks struct {
|
||||||
|
Enabled []string
|
||||||
|
Disabled []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WorldGenSettings struct {
|
||||||
|
BonusChest bool
|
||||||
|
Seed int64
|
||||||
|
GenerateFeatures bool
|
||||||
|
Dimensions map[string]Dimension
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dimensions struct {
|
||||||
|
Overworld Dimension
|
||||||
|
Nether Dimension
|
||||||
|
End Dimension
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dimension struct {
|
||||||
|
Generator Generator
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Generator struct {
|
||||||
|
Type string
|
||||||
|
Settings string
|
||||||
|
BiomeSource BiomeSource
|
||||||
|
}
|
||||||
|
|
||||||
|
type BiomeSource struct {
|
||||||
|
Preset string `nbt:"preset,omitempty"`
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DragonFight struct {
|
||||||
|
NeedsStateScanning bool
|
||||||
|
Gateways []int32
|
||||||
|
DragonKilled bool
|
||||||
|
PreviouslyKilled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
Snapshot bool
|
||||||
|
Series string
|
||||||
|
Id int32
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Spawn struct {
|
||||||
|
Dimension string
|
||||||
|
Yaw float32
|
||||||
|
Pos [3]int32
|
||||||
|
Pitch float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
XpLevel int32
|
||||||
|
HurtTime int16
|
||||||
|
DeathTime int16
|
||||||
|
XpTotal int32
|
||||||
|
FoodExhaustionLevel float32
|
||||||
|
RecipeBook RecipeBook
|
||||||
|
Fire int16
|
||||||
|
Brain Brain
|
||||||
|
XpSeed int32
|
||||||
|
XpP float32
|
||||||
|
EnderItems []interface{}
|
||||||
|
Invulnerable bool
|
||||||
|
Pos [3]float64
|
||||||
|
Inventory []InventoryItem
|
||||||
|
SleepTimer int16
|
||||||
|
Abilities Abilities
|
||||||
|
PlayerGameType int32
|
||||||
|
SeenCredits bool
|
||||||
|
FoodSaturationLevel float32
|
||||||
|
FallDistance float64
|
||||||
|
OnGround bool
|
||||||
|
Dimension string
|
||||||
|
PortalCooldown int32
|
||||||
|
Motion [3]float64
|
||||||
|
IgnoreFallDamageFromCurrentExplosion bool
|
||||||
|
Rotation [2]float32
|
||||||
|
CurrentImpulseContextResetGraceTime int32
|
||||||
|
WardenSpawnTracker WardenSpawnTracker
|
||||||
|
PreviousPlayerGameType int32
|
||||||
|
Attributes []Attribute
|
||||||
|
HurtByTimestamp int32
|
||||||
|
UUID [4]int32
|
||||||
|
Score int32
|
||||||
|
DataVersion int32
|
||||||
|
FoodLevel int32
|
||||||
|
SpawnExtraParticlesOnFall bool
|
||||||
|
SelectedItemSlot int32
|
||||||
|
FoodTickTimer int32
|
||||||
|
FallFlying bool
|
||||||
|
AbsorptionAmount float32
|
||||||
|
Health float32
|
||||||
|
Air int16
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecipeBook struct {
|
||||||
|
Recipes []string
|
||||||
|
ToBeDisplayed []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Brain struct {
|
||||||
|
Memories map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type InventoryItem struct {
|
||||||
|
Count int32
|
||||||
|
Slot int8
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Abilities struct {
|
||||||
|
Invulnerable bool
|
||||||
|
Mayfly bool
|
||||||
|
Instabuild bool
|
||||||
|
WalkSpeed float32
|
||||||
|
MayBuild bool
|
||||||
|
Flying bool
|
||||||
|
FlySpeed float32
|
||||||
|
}
|
||||||
|
|
||||||
|
type WardenSpawnTracker struct {
|
||||||
|
WarningLevel int32
|
||||||
|
TicksSinceLastWarning int32
|
||||||
|
CooldownTicks int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type Attribute struct {
|
||||||
|
Id string
|
||||||
|
Base float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadLevelData(r io.Reader) (l Level, err error) {
|
func ReadLevelData(r io.Reader) (l Level, err error) {
|
||||||
var lDtatWrapped struct{ Data Level }
|
var lDataWrapped struct{ Data Level }
|
||||||
err = nbt.ReadFile(r, &lDtatWrapped)
|
err = nbt.ReadFile(r, &lDataWrapped)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
l = lDtatWrapped.Data
|
l = lDataWrapped.Data
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package mapstructure
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *Format) decode(m map[string]any, target reflect.Value) error {
|
func (f *Format) decode(m map[string]any, target reflect.Value) error {
|
||||||
@@ -26,12 +28,19 @@ func (f *Format) decode(m map[string]any, target reflect.Value) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val, exists := m[key]
|
val, exists := m[key]
|
||||||
|
if !exists && f.trySnakeCase {
|
||||||
|
val, exists = m[util.SnakeCase(key)]
|
||||||
|
}
|
||||||
|
if !exists && f.tryLowCase {
|
||||||
|
val, exists = m[util.FirstLetterLower(key)]
|
||||||
|
}
|
||||||
if !exists {
|
if !exists {
|
||||||
if tag.Required || (f.requireAll && !tag.OmitEmpty) {
|
if tag.Required || (f.requireAll && !tag.OmitEmpty) {
|
||||||
return fmt.Errorf("%w: %s", ErrMissingRequired, key)
|
return fmt.Errorf("%w: %s", ErrMissingRequired, key)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
usedKeys[key] = true
|
usedKeys[key] = true
|
||||||
|
|
||||||
fieldVal := target.Field(i)
|
fieldVal := target.Field(i)
|
||||||
@@ -108,6 +117,26 @@ func (f *Format) setField(target reflect.Value, value any, opts tagOptions) erro
|
|||||||
return f.setBool(target, value)
|
return f.setBool(target, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this fixes this edge case
|
||||||
|
// level_test.go:19: field Data: field Player: field Pos: cannot set [3]float64 from []interface {}
|
||||||
|
if targetType.Kind() == reflect.Array && src.Kind() == reflect.Slice {
|
||||||
|
if target.Len() != src.Len() {
|
||||||
|
return fmt.Errorf("cannot set %s from %T because lengths do not match", targetType, value)
|
||||||
|
}
|
||||||
|
for i := 0; i < src.Len(); i++ {
|
||||||
|
if src.Index(i).Type().AssignableTo(targetType.Elem()) {
|
||||||
|
target.Index(i).Set(src.Index(i))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if src.Index(i).Elem().Type().AssignableTo(targetType.Elem()) {
|
||||||
|
target.Index(i).Set(src.Index(i).Elem())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot set %s from %T because element %d cannot be converted to %s", targetType, value, i, targetType.Elem())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Errorf("cannot set %s from %T", targetType, value)
|
return fmt.Errorf("cannot set %s from %T", targetType, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-2
@@ -18,8 +18,11 @@ type Format struct {
|
|||||||
Name string
|
Name string
|
||||||
requireAll bool
|
requireAll bool
|
||||||
errorOnExtra bool
|
errorOnExtra bool
|
||||||
decoders map[reflect.Type]func(any, string) (any, error)
|
trySnakeCase bool
|
||||||
encoders map[reflect.Type]func(any, string) (any, error)
|
tryLowCase bool
|
||||||
|
|
||||||
|
decoders map[reflect.Type]func(any, string) (any, error)
|
||||||
|
encoders map[reflect.Type]func(any, string) (any, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFormat(name string, opts ...Option) *Format {
|
func NewFormat(name string, opts ...Option) *Format {
|
||||||
@@ -48,6 +51,18 @@ func WithErrorOnExtra() Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithTrySnakeCase() Option {
|
||||||
|
return func(f *Format) {
|
||||||
|
f.trySnakeCase = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTryLowCase() Option {
|
||||||
|
return func(f *Format) {
|
||||||
|
f.tryLowCase = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithTypeCodec(decodeFn, encodeFn any) Option {
|
func WithTypeCodec(decodeFn, encodeFn any) Option {
|
||||||
dt := reflect.TypeOf(decodeFn)
|
dt := reflect.TypeOf(decodeFn)
|
||||||
et := reflect.TypeOf(encodeFn)
|
et := reflect.TypeOf(encodeFn)
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/admin-else/strom/mapstructure"
|
"github.com/admin-else/strom/mapstructure"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Format = mapstructure.NewFormat("nbt", mapstructure.WithRequireAll())
|
var Format = mapstructure.NewFormat("nbt", mapstructure.WithRequireAll(), mapstructure.WithTrySnakeCase(), mapstructure.WithTryLowCase())
|
||||||
|
|
||||||
func ReadFile(file io.Reader, data any) (err error) {
|
func ReadFile(file io.Reader, data any) (err error) {
|
||||||
n, err := ReadUnstructuredFile(file)
|
n, err := ReadUnstructuredFile(file)
|
||||||
|
|||||||
@@ -4,28 +4,8 @@ import (
|
|||||||
"cmp"
|
"cmp"
|
||||||
"maps"
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
"unicode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var boundaries = []rune{' ', '_', '-', '.', ',', ';', ':', '(', ')', '[', ']', '{', '}'}
|
|
||||||
|
|
||||||
func CamelCase(s string) string {
|
|
||||||
ret := ""
|
|
||||||
atWordBoundary := true
|
|
||||||
for _, c := range s {
|
|
||||||
if slices.Contains(boundaries, c) {
|
|
||||||
atWordBoundary = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if atWordBoundary && unicode.IsLower(c) {
|
|
||||||
c = unicode.ToUpper(c)
|
|
||||||
}
|
|
||||||
atWordBoundary = false
|
|
||||||
ret += string(c)
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func OrderedKeys[Map map[K]V, K cmp.Ordered, V any](m Map) []K {
|
func OrderedKeys[Map map[K]V, K cmp.Ordered, V any](m Map) []K {
|
||||||
keysIter := maps.Keys(m)
|
keysIter := maps.Keys(m)
|
||||||
keys := slices.Collect(keysIter)
|
keys := slices.Collect(keysIter)
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var boundaries = []rune{' ', '_', '-', '.', ',', ';', ':', '(', ')', '[', ']', '{', '}'}
|
||||||
|
|
||||||
|
func CamelCase(s string) string {
|
||||||
|
ret := ""
|
||||||
|
atWordBoundary := true
|
||||||
|
for _, c := range s {
|
||||||
|
if slices.Contains(boundaries, c) {
|
||||||
|
atWordBoundary = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if atWordBoundary && unicode.IsLower(c) {
|
||||||
|
c = unicode.ToUpper(c)
|
||||||
|
}
|
||||||
|
atWordBoundary = false
|
||||||
|
ret += string(c)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func SnakeCase(s string) string {
|
||||||
|
ret := ""
|
||||||
|
for i, c := range s {
|
||||||
|
if unicode.IsUpper(c) {
|
||||||
|
if i > 0 {
|
||||||
|
ret += "_"
|
||||||
|
}
|
||||||
|
c = unicode.ToLower(c)
|
||||||
|
}
|
||||||
|
ret += string(c)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func FirstLetterLower(s string) string {
|
||||||
|
return string(unicode.ToLower(rune(s[0]))) + s[1:]
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
package main
|
package util_test
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/admin-else/strom/util"
|
||||||
|
)
|
||||||
|
|
||||||
func TestCamelCase(t *testing.T) {
|
func TestCamelCase(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -17,7 +21,7 @@ func TestCamelCase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
result := CamelCase(test.input)
|
result := util.CamelCase(test.input)
|
||||||
if result != test.expected {
|
if result != test.expected {
|
||||||
t.Errorf("CamelCase(%q) = %q; expected %q", test.input, result, test.expected)
|
t.Errorf("CamelCase(%q) = %q; expected %q", test.input, result, test.expected)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user