github.com/df-mc/dragonfly@v0.9.13/server/cmd/parameter.go (about) 1 package cmd 2 3 import ( 4 "github.com/go-gl/mathgl/mgl64" 5 "reflect" 6 "strings" 7 ) 8 9 // Parameter is an interface for a generic parameters. Users may have types as command parameters that 10 // implement this parameter. 11 type Parameter interface { 12 // Parse takes an arbitrary amount of arguments from the command Line passed and parses it, so that it can 13 // store it to value v. If the arguments cannot be parsed from the Line, an error should be returned. 14 Parse(line *Line, v reflect.Value) error 15 // Type returns the type of the parameter. It will show up in the usage of the command, and, if one of the 16 // known type names, will also show up client-side. 17 Type() string 18 } 19 20 // Enum is an interface for enum-type parameters. Users may have types as command parameters that implement 21 // this parameter in order to allow a specific set of options only. 22 // Enum implementations must be of the type string, for example: 23 // 24 // type GameMode string 25 // func (GameMode) Type() string { return "GameMode" } 26 // func (GameMode) Options(Source) []string { return []string{"survival", "creative"} } 27 // 28 // Their values will then automatically be set to whichever option returned in Enum.Options is selected by 29 // the user. 30 type Enum interface { 31 // Type returns the type of the enum. This type shows up client-side in the command usage, in the spot 32 // where parameter types otherwise are. 33 // Type names returned are used as an identifier for this enum type. Different Enum implementations must 34 // return a different string in the Type method. 35 Type() string 36 // Options should return a list of options that show up on the client side. The command will ensure that 37 // the argument passed to the enum parameter will be equal to one of these options. The provided Source 38 // can also be used to change the enums for each player. 39 Options(source Source) []string 40 } 41 42 // SubCommand represents a subcommand that may be added as a static value that must be written. Adding 43 // multiple Runnable implementations to the command in New with different SubCommand fields as the 44 // first parameter allows for commands with subcommands. 45 type SubCommand struct{} 46 47 // Varargs is an argument type that may be used to capture all arguments that follow. This is useful for, 48 // for example, messages and names. 49 type Varargs string 50 51 // Optional is an argument type that may be used to make any of the available parameter types optional. Optional command 52 // parameters may only occur at the end of the Runnable struct. No non-optional parameter is allowed after an optional 53 // parameter. 54 type Optional[T any] struct { 55 val T 56 set bool 57 } 58 59 // Load returns the value specified upon executing the command and a bool that is true if the parameter was filled out 60 // by the Source. 61 func (o Optional[T]) Load() (T, bool) { 62 return o.val, o.set 63 } 64 65 // LoadOr returns the value specified upon executing the command, or a value 'or' if the parameter was not filled out 66 // by the Source. 67 func (o Optional[T]) LoadOr(or T) T { 68 if o.set { 69 return o.val 70 } 71 return or 72 } 73 74 // with returns an Optional[T] with the value passed. It also sets the 'set' field to true. 75 func (o Optional[T]) with(val any) any { 76 return Optional[T]{val: val.(T), set: true} 77 } 78 79 // optionalT is used to identify a parameter of the Optional type. 80 type optionalT interface { 81 with(val any) any 82 } 83 84 // typeNameOf returns a readable type name for the interface value passed. If none could be found, 'value' 85 // is returned. 86 func typeNameOf(i any, name string) string { 87 switch i.(type) { 88 case int, int8, int16, int32, int64: 89 return "int" 90 case uint, uint8, uint16, uint32, uint64: 91 return "uint" 92 case float32, float64: 93 return "float" 94 case string: 95 return "string" 96 case Varargs: 97 return "text" 98 case bool: 99 return "bool" 100 case mgl64.Vec3: 101 return "x y z" 102 case []Target: 103 return "target" 104 case SubCommand: 105 return name 106 } 107 if param, ok := i.(Parameter); ok { 108 return param.Type() 109 } 110 if enum, ok := i.(Enum); ok { 111 return enum.Type() 112 } 113 return "value" 114 } 115 116 // unwrap returns the underlying reflect.Value of a reflect.Value, assuming it is of the Optional[T] type. 117 func unwrap(v reflect.Value) reflect.Value { 118 if _, ok := v.Interface().(optionalT); ok { 119 return reflect.New(v.Field(0).Type()).Elem() 120 } 121 return v 122 } 123 124 // optional checks if the reflect.Value passed implements the optionalT interface. 125 func optional(v reflect.Value) bool { 126 _, ok := v.Interface().(optionalT) 127 return ok 128 } 129 130 // suffix returns the suffix of the parameter as set in the struct field, if any. 131 func suffix(v reflect.StructField) string { 132 _, str := tag(v) 133 return str 134 } 135 136 // name returns the name of the parameter as set in the struct tag if it exists, or the field's name if not. 137 func name(v reflect.StructField) string { 138 str, _ := tag(v) 139 if str == "" { 140 return v.Name 141 } 142 return str 143 } 144 145 // tag returns the name and suffix as specified in the 'cmd' tag, or empty strings if not present. 146 func tag(v reflect.StructField) (name string, suffix string) { 147 t, _ := v.Tag.Lookup("cmd") 148 a, b, _ := strings.Cut(t, ",") 149 return a, b 150 }