mojang cant get their cases straight
i had to implement like 4 cases just to parse that file
This commit is contained in:
+165
-4
@@ -7,18 +7,179 @@ import (
|
||||
)
|
||||
|
||||
type Level struct {
|
||||
DataPacks struct {
|
||||
ClearWeatherTime int32 `nbt:"clearWeatherTime"`
|
||||
DataPacks DataPacks
|
||||
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) {
|
||||
var lDtatWrapped struct{ Data Level }
|
||||
err = nbt.ReadFile(r, &lDtatWrapped)
|
||||
var lDataWrapped struct{ Data Level }
|
||||
err = nbt.ReadFile(r, &lDataWrapped)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
l = lDtatWrapped.Data
|
||||
l = lDataWrapped.Data
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package mapstructure
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/admin-else/strom/util"
|
||||
)
|
||||
|
||||
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]
|
||||
if !exists && f.trySnakeCase {
|
||||
val, exists = m[util.SnakeCase(key)]
|
||||
}
|
||||
if !exists && f.tryLowCase {
|
||||
val, exists = m[util.FirstLetterLower(key)]
|
||||
}
|
||||
if !exists {
|
||||
if tag.Required || (f.requireAll && !tag.OmitEmpty) {
|
||||
return fmt.Errorf("%w: %s", ErrMissingRequired, key)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
usedKeys[key] = true
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ type Format struct {
|
||||
Name string
|
||||
requireAll bool
|
||||
errorOnExtra bool
|
||||
trySnakeCase bool
|
||||
tryLowCase bool
|
||||
|
||||
decoders map[reflect.Type]func(any, string) (any, error)
|
||||
encoders map[reflect.Type]func(any, string) (any, error)
|
||||
}
|
||||
@@ -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 {
|
||||
dt := reflect.TypeOf(decodeFn)
|
||||
et := reflect.TypeOf(encodeFn)
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"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) {
|
||||
n, err := ReadUnstructuredFile(file)
|
||||
|
||||
@@ -4,28 +4,8 @@ import (
|
||||
"cmp"
|
||||
"maps"
|
||||
"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 {
|
||||
keysIter := maps.Keys(m)
|
||||
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) {
|
||||
tests := []struct {
|
||||
@@ -17,7 +21,7 @@ func TestCamelCase(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := CamelCase(test.input)
|
||||
result := util.CamelCase(test.input)
|
||||
if result != test.expected {
|
||||
t.Errorf("CamelCase(%q) = %q; expected %q", test.input, result, test.expected)
|
||||
}
|
||||
Reference in New Issue
Block a user