Repository: GoLangMc/minecraft-server Branch: master Commit: ca992e94ba13 Files: 99 Total size: 152.1 KB Directory structure: gitextract_p9xoijfz/ ├── .github/ │ └── dependabot.yml ├── .gitignore ├── LICENSE ├── README.md ├── apis/ │ ├── base/ │ │ ├── funcs.go │ │ ├── named.go │ │ ├── state.go │ │ └── unique.go │ ├── buff/ │ │ └── buffers.go │ ├── cmds/ │ │ ├── command.go │ │ └── commandmanager.go │ ├── data/ │ │ ├── chat/ │ │ │ └── coloring.go │ │ ├── location.go │ │ ├── material.go │ │ ├── msgs/ │ │ │ └── messages.go │ │ ├── position.go │ │ ├── rotation.go │ │ ├── tags/ │ │ │ └── nbt.go │ │ └── versions.go │ ├── ents/ │ │ ├── entity.go │ │ ├── living.go │ │ ├── player.go │ │ └── sender.go │ ├── game/ │ │ ├── difficulty.go │ │ ├── dimension.go │ │ ├── event/ │ │ │ ├── block.go │ │ │ ├── cancel.go │ │ │ └── player.go │ │ ├── gamemode.go │ │ ├── level/ │ │ │ ├── block.go │ │ │ ├── chunk.go │ │ │ ├── level.go │ │ │ ├── slice.go │ │ │ └── value.go │ │ ├── leveltype.go │ │ └── profile.go │ ├── logs/ │ │ └── logging.go │ ├── math/ │ │ └── vector.go │ ├── rand/ │ │ └── random.go │ ├── server.go │ ├── task/ │ │ ├── tasking.go │ │ └── tasking_test.go │ ├── urls/ │ │ └── urls.go │ ├── util/ │ │ ├── formats.go │ │ └── watcher.go │ └── uuid/ │ └── uuids.go ├── go.mod ├── go.sum ├── impl/ │ ├── base/ │ │ ├── combine.go │ │ ├── compact.go │ │ ├── connect.go │ │ ├── network.go │ │ └── packets.go │ ├── conf/ │ │ └── config.go │ ├── conn/ │ │ ├── buffers.go │ │ ├── connect.go │ │ ├── crypto/ │ │ │ └── cfb8.go │ │ └── network.go │ ├── cons/ │ │ └── console.go │ ├── data/ │ │ ├── client/ │ │ │ ├── abilities.go │ │ │ ├── chat.go │ │ │ ├── hand.go │ │ │ ├── playerinfo.go │ │ │ ├── position.go │ │ │ ├── skin.go │ │ │ ├── slot.go │ │ │ └── status.go │ │ ├── plugin/ │ │ │ └── message.go │ │ ├── status/ │ │ │ └── response.go │ │ ├── system/ │ │ │ └── command.go │ │ └── values/ │ │ └── constants.go │ ├── game/ │ │ ├── auth/ │ │ │ ├── authenticate.go │ │ │ └── cryptography.go │ │ ├── ents/ │ │ │ ├── entity.go │ │ │ ├── living.go │ │ │ └── player.go │ │ ├── event/ │ │ │ └── events.go │ │ ├── level/ │ │ │ ├── block.go │ │ │ ├── chunk.go │ │ │ ├── level.go │ │ │ ├── slice.go │ │ │ └── value.go │ │ └── mode/ │ │ ├── mode_state0.go │ │ ├── mode_state1.go │ │ ├── mode_state2.go │ │ └── mode_state3.go │ ├── mask/ │ │ ├── masking.go │ │ └── masking_test.go │ ├── prot/ │ │ ├── client/ │ │ │ ├── to_client_state0.go │ │ │ ├── to_client_state1.go │ │ │ ├── to_client_state2.go │ │ │ └── to_client_state3.go │ │ ├── packets.go │ │ └── server/ │ │ ├── to_server_state0.go │ │ ├── to_server_state1.go │ │ ├── to_server_state2.go │ │ └── to_server_state3.go │ └── server.go └── main.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "gomod" directory: "/" # Location of package manifests schedule: interval: "daily" assignees: - "Sxtanna" ================================================ FILE: .gitignore ================================================ # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ # IntelliJ project files .idea *.iml out gen ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 GoLangMc Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

logo

minecraft-server

Minecraft Server implementation written in Go

Report Bug · Request Feature · Send a Pull Request

================================================ FILE: apis/base/funcs.go ================================================ package base import ( "crypto/sha256" "encoding/binary" "fmt" "strings" ) func ConvertToString(data ...interface{}) string { strs := make([]string, len(data)) for i, str := range data { strs[i] = fmt.Sprintf("%v", str) } return strings.Join(strs, "") } func Attempt(function func()) (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("caught: %v", r) } }() function() return } func JavaStringHashCode(value string) int32 { var h int32 if len(value) > 0 { for _, r := range value { h = 31*h + r } } return h } func JavaSHA256HashLong(value int64) []byte { bytes := make([]byte, 8) binary.LittleEndian.PutUint64(bytes, uint64(value)) hash := sha256.Sum256(bytes) return hash[:] } ================================================ FILE: apis/base/named.go ================================================ package base type Named interface { Name() string } ================================================ FILE: apis/base/state.go ================================================ package base type Loads interface { Load() } type Kills interface { Kill() } type State interface { Loads Kills } ================================================ FILE: apis/base/unique.go ================================================ package base import "github.com/golangmc/minecraft-server/apis/uuid" type Unique interface { UUID() uuid.UUID } ================================================ FILE: apis/buff/buffers.go ================================================ package buff import ( "github.com/golangmc/minecraft-server/apis/data" "github.com/golangmc/minecraft-server/apis/data/tags" "github.com/golangmc/minecraft-server/apis/uuid" ) type Buffer interface { Len() int32 SAS() []int8 UAS() []byte InI() int32 InO() int32 SkpAll() SkpLen(delta int32) // pull PullBit() bool PullByt() byte PullI16() int16 PullU16() uint16 PullI32() int32 PullI64() int64 PullU64() uint64 PullF32() float32 PullF64() float64 PullVrI() int32 PullVrL() int64 PullTxt() string PullUAS() []byte PullSAS() []int8 PullUID() uuid.UUID PullPos() data.PositionI PullNbt() *tags.NbtCompound // push PushBit(data bool) PushByt(data byte) PushI16(data int16) PushI32(data int32) PushI64(data int64) PushF32(data float32) PushF64(data float64) PushVrI(data int32) PushVrL(data int64) PushTxt(data string) PushUAS(data []byte, prefixWithLen bool) PushSAS(data []int8, prefixWithLen bool) PushUID(data uuid.UUID) PushPos(data data.PositionI) PushNbt(data *tags.NbtCompound) } type BufferPush interface { Push(writer Buffer) } type BufferPull interface { Pull(reader Buffer) } type BufferCodec interface { BufferPush BufferPull } ================================================ FILE: apis/cmds/command.go ================================================ package cmds import ( "github.com/golangmc/minecraft-server/apis/base" "github.com/golangmc/minecraft-server/apis/ents" ) type Command interface { base.Named base.State Evaluate(sender ents.Sender, params []string) Complete(sender ents.Sender, params []string, output *[]string) } ================================================ FILE: apis/cmds/commandmanager.go ================================================ package cmds import ( "strings" "github.com/golangmc/minecraft-server/apis/ents" ) type CommandManager struct { commands map[string]*Command } func NewCommandManager() *CommandManager { return &CommandManager{ commands: make(map[string]*Command), } } func (c *CommandManager) Load() { } func (c *CommandManager) Kill() { c.commands = nil } func (c *CommandManager) RegisterCommand(command Command) { c.commands[command.Name()] = &command command.Load() } func (c *CommandManager) Register(name string, evaluate func(sender ents.Sender, params []string)) { command := simpleCommand{ name: name, evaluate: evaluate, } c.RegisterCommand(&command) } func (c *CommandManager) Search(named string) *Command { for name, command := range c.commands { if strings.EqualFold(name, named) { return command } } return nil } type simpleCommand struct { name string evaluate func(sender ents.Sender, params []string) } func (s *simpleCommand) Name() string { return s.name } func (s *simpleCommand) Load() { } func (s *simpleCommand) Kill() { } func (s *simpleCommand) Evaluate(sender ents.Sender, params []string) { s.evaluate(sender, params) } func (s *simpleCommand) Complete(sender ents.Sender, params []string, output *[]string) { } ================================================ FILE: apis/data/chat/coloring.go ================================================ package chat import ( "fmt" "strings" "github.com/fatih/color" ) type ChatColor int type ColorCode struct { Chat string Motd string Json string Dec string Hex string } const ( DarkRed ChatColor = iota Red Gold Yellow DarkGreen Green DarkAqua Aqua DarkBlue Blue DarkPurple Purple White Black DarkGray Gray Obfuscated Bold Strikethrough Underline Italic Reset ) const ColorCChar = '§' const ColorAChar = '&' var codeToCode = map[ChatColor]*ColorCode{ DarkRed: { Chat: `§4`, Motd: `\u00A74`, Json: `dark_red`, Dec: `11141120`, Hex: `AA0000`, }, Red: { Chat: `§c`, Motd: `\u00A7c`, Json: `red`, Dec: `16733525`, Hex: `FF5555`, }, Gold: { Chat: `§6`, Motd: `\u00A76`, Json: `gold`, Dec: `16755200`, Hex: `FFAA00`, }, Yellow: { Chat: `§e`, Motd: `\u00A7e`, Json: `yellow`, Dec: `16777045`, Hex: `FFFF55`, }, DarkGreen: { Chat: `§2`, Motd: `\u00A72`, Json: `dark_green`, Dec: `43520`, Hex: `00AA00`, }, Green: { Chat: `§a`, Motd: `\u00A7a`, Dec: `5635925`, Hex: `55FF55`, }, DarkAqua: { Chat: `§3`, Motd: `\u00A73`, Json: `dark_aqua`, Dec: `43690`, Hex: `00AAAA`, }, Aqua: { Chat: `§b`, Motd: `\u00A7b`, Json: `aqua`, Dec: `5636095`, Hex: `55FFFF`, }, DarkBlue: { Chat: `§1`, Motd: `\u00A71`, Json: `dark_blue`, Dec: `170`, Hex: `0000AA`, }, Blue: { Chat: `§9`, Motd: `\u00A79`, Json: `blue`, Dec: `5592575`, Hex: `5555FF`, }, DarkPurple: { Chat: `§5`, Motd: `\u00A75`, Json: `dark_purple`, Dec: `11141290`, Hex: `AA00AA`, }, Purple: { Chat: `§d`, Motd: `\u00A7d`, Json: `light_purple`, Dec: `16733695`, Hex: `FF55FF`, }, White: { Chat: `§f`, Motd: `\u00A7f`, Json: `white`, Dec: `16777215`, Hex: `FFFFFF`, }, Black: { Chat: `§0`, Motd: `\u00A70`, Json: `black`, Dec: `0`, Hex: `000000`, }, DarkGray: { Chat: `§8`, Motd: `\u00A78`, Json: `dark_gray`, Dec: `5592405`, Hex: `555555`, }, Gray: { Chat: `§7`, Motd: `\u00A77`, Json: `gray`, Dec: `11184810`, Hex: `AAAAAA`, }, Obfuscated: { Chat: `§k`, Motd: `\u00A7k`, Json: `obfuscated`, }, Bold: { Chat: `§l`, Motd: `\u00A7l`, Json: `bold`, }, Strikethrough: { Chat: `§m`, Motd: `\u00A7m`, Json: `strikethrough`, }, Underline: { Chat: `§n`, Motd: `\u00A7n`, Json: `underline`, }, Italic: { Chat: `§o`, Motd: `\u00A7o`, Json: `italic`, }, Reset: { Chat: `§r`, Motd: `\u00A7r`, Json: `reset`, }, } var codeToForm = map[ChatColor]color.Attribute{ DarkRed: color.FgHiRed, Red: color.FgRed, Gold: color.FgYellow, Yellow: color.FgHiYellow, DarkGreen: color.FgGreen, Green: color.FgHiGreen, DarkAqua: color.FgCyan, Aqua: color.FgHiCyan, DarkBlue: color.FgBlue, Blue: color.FgHiBlue, DarkPurple: color.FgMagenta, Purple: color.FgHiMagenta, White: color.FgHiWhite, Black: color.FgBlack, DarkGray: color.FgHiBlack, Gray: color.FgWhite, Reset: color.Reset, Obfuscated: color.BlinkRapid, Bold: color.Bold, Strikethrough: color.CrossedOut, Underline: color.Underline, Italic: color.Italic, } var charToCode = map[rune]ChatColor{ '4': DarkRed, 'c': Red, '6': Gold, 'e': Yellow, '2': DarkGreen, 'a': Green, '3': DarkAqua, 'b': Aqua, '1': DarkBlue, '9': Blue, '5': DarkPurple, 'd': Purple, 'f': White, '0': Black, '8': DarkGray, '7': Gray, 'k': Obfuscated, 'l': Bold, 'm': Strikethrough, 'n': Underline, 'o': Italic, 'r': Reset, } var jsonToCode = map[string]ChatColor{ `dark_red`: DarkRed, `red`: Red, `gold`: Gold, `yellow`: Yellow, `dark_green`: DarkGreen, `green`: Green, `dark_aqua`: DarkAqua, `aqua`: Aqua, `dark_blue`: DarkBlue, `blue`: Blue, `dark_purple`: DarkPurple, `light_purple`: Purple, `white`: White, `black`: Black, `dark_gray`: DarkGray, `gray`: Gray, `obfuscated`: Obfuscated, `bold`: Bold, `strikethrough`: Strikethrough, `underline`: Underline, `italic`: Italic, `reset`: Reset, } func (code ChatColor) String() string { return codeToCode[code].Chat } func (code *ChatColor) MarshalJSON() ([]byte, error) { return []byte(`"` + codeToCode[*code].Json + `"`), nil } func (code *ChatColor) UnmarshalJSON(bytes []byte) error { *code = jsonToCode[string(bytes)] return nil } func (code *ChatColor) On(text string) string { if len(text) == 0 { return "" } return fmt.Sprintf("%p%s%v", code, text, Reset) } func Translate(text string) string { build := strings.Builder{} chars := []rune(text) for i := 0; i < len(chars); i++ { r := chars[i] if r != ColorAChar || i+1 >= len(chars) { build.WriteRune(r) } else { c := codeToCode[charToCode[chars[i+1]]] if c == nil { build.WriteRune(r) } else { build.WriteString(c.Chat) i++ } } } return build.String() } func TranslateConsole(text string) string { text = Translate(text) build := strings.Builder{} temps := strings.Builder{} chars := []rune(text) forms := make([]color.Attribute, 0) for i := 0; i < len(chars); i++ { r := chars[i] if r != ColorCChar || i+1 >= len(chars) { temps.WriteRune(r) continue } f, con := codeToForm[charToCode[chars[i+1]]] if !con { temps.WriteRune(r) continue } if temps.Len() > 0 { build.WriteString(color.New(forms...).Sprint(temps.String())) temps.Reset() } i++ if f <= color.CrossedOut { forms = append(forms, f) } else { forms = make([]color.Attribute, 0) forms = append(forms, f) } } if temps.Len() > 0 { build.WriteString(color.New(forms...).Sprint(temps.String())) } return build.String() } ================================================ FILE: apis/data/location.go ================================================ package data type Location struct { PositionF RotationF } ================================================ FILE: apis/data/material.go ================================================ package data type Material int32 const ( AIR Material = iota STONE GRANITE POLISHED_GRANITE ANDESITE POLISHED_ANDESITE DIORITE POLISHED_DIORITE ) ================================================ FILE: apis/data/msgs/messages.go ================================================ package msgs import ( "encoding/json" "strings" "github.com/golangmc/minecraft-server/apis/data/chat" ) type MessagePosition byte const ( NormalChat MessagePosition = iota SystemChat HotBarText ) type Message struct { Text string `json:"text"` Color *chat.ChatColor `json:"color,string,omitempty"` Bold *bool `json:"bold,boolean,omitempty"` Italic *bool `json:"italic,boolean,omitempty"` Underlined *bool `json:"underlined,boolean,omitempty"` Strikethrough *bool `json:"strikethrough,boolean,omitempty"` Obfuscated *bool `json:"obfuscated,boolean,omitempty"` Extra []*Message `json:"extra,omitempty"` head *Message } func New(text string) *Message { return &Message{ Text: text, } } func (c *Message) SetColor(code chat.ChatColor) *Message { c.Color = &code return c } func (c *Message) SetBold(value bool) *Message { c.Bold = &value return c } func (c *Message) SetItalic(value bool) *Message { c.Italic = &value return c } func (c *Message) SetUnderlined(value bool) *Message { c.Underlined = &value return c } func (c *Message) SetStrikethrough(value bool) *Message { c.Strikethrough = &value return c } func (c *Message) SetObfuscated(value bool) *Message { c.Obfuscated = &value return c } // creates and returns a new Chat object, and adds it to the caller's extra slice func (c *Message) Add(text string) *Message { chat := New(text) chat.head = c c.Extra = append(c.Extra, chat) return chat } func (c *Message) Reset() *Message { next := c.Add("").SetColor(chat.Reset) if c.Bold != nil && *c.Bold == true { next.SetBold(false) } if c.Italic != nil && *c.Italic == true { next.SetItalic(false) } if c.Underlined != nil && *c.Underlined == true { next.SetUnderlined(false) } if c.Strikethrough != nil && *c.Strikethrough == true { next.SetStrikethrough(false) } if c.Obfuscated != nil && *c.Obfuscated == true { next.SetObfuscated(false) } return next } func (c *Message) AsJson() string { chat := c for chat.head != nil { chat = chat.head } if text, err := json.Marshal(chat); err != nil { panic(err) } else { return string(text) } } func (c *Message) AsText() string { builder := strings.Builder{} curr := c for curr.head != nil { curr = curr.head } builder.WriteString(curr.asText()) return builder.String() } func (c *Message) asText() string { builder := strings.Builder{} if c.Color != nil { builder.WriteString(c.Color.String()) } if c.Bold != nil && *c.Bold == true { builder.WriteString(chat.Bold.String()) } if c.Italic != nil && *c.Italic == true { builder.WriteString(chat.Italic.String()) } if c.Underlined != nil && *c.Underlined == true { builder.WriteString(chat.Underline.String()) } if c.Strikethrough != nil && *c.Strikethrough == true { builder.WriteString(chat.Strikethrough.String()) } if c.Obfuscated != nil && *c.Obfuscated == true { builder.WriteString(chat.Obfuscated.String()) } builder.WriteString(c.Text) for _, extra := range c.Extra { builder.WriteString(extra.asText()) } return builder.String() } func (c *Message) String() string { return c.AsJson() } ================================================ FILE: apis/data/position.go ================================================ package data type PositionI struct { X int64 Y int64 Z int64 } type PositionF struct { X float64 Y float64 Z float64 } ================================================ FILE: apis/data/rotation.go ================================================ package data type RotationF struct { AxisX float32 // 'yaw' AxisY float32 // 'pitch' } ================================================ FILE: apis/data/tags/nbt.go ================================================ package tags type Typ byte const ( TAG_End Typ = iota TAG_Byte TAG_Short TAG_Int TAG_Long TAG_Float TAG_Double TAG_Byte_Array TAG_String TAG_List TAG_Compound TAG_Int_Array TAG_Long_Array ) type Nbt interface { Type() Typ Name() string } // end type NbtEnd struct{} func (n *NbtEnd) Type() Typ { return TAG_End } func (n *NbtEnd) Name() string { return "TAG_End" } // byte type NbtByt struct { Value int8 } func (n *NbtByt) Type() Typ { return TAG_Byte } func (n *NbtByt) Name() string { return "TAG_Byte" } // short type NbtI16 struct { Value int16 } func (n *NbtI16) Type() Typ { return TAG_Short } func (n *NbtI16) Name() string { return "TAG_Short" } /*func (n *NbtI16) Push(writer buff.Buffer) { writer.PushI16(n.Value) } func (n *NbtI16) Pull(reader buff.Buffer) { n.Value = reader.PullI16() }*/ // int type NbtI32 struct { Value int32 } func (n *NbtI32) Type() Typ { return TAG_Int } func (n *NbtI32) Name() string { return "TAG_Int" } /*func (n *NbtI32) Push(writer buff.Buffer) { writer.PushI32(n.Value) } func (n *NbtI32) Pull(reader buff.Buffer) { n.Value = reader.PullI32() }*/ // long type NbtI64 struct { Value int64 } func (n *NbtI64) Type() Typ { return TAG_Long } func (n *NbtI64) Name() string { return "TAG_Long" } /*func (n *NbtI64) Push(writer buff.Buffer) { writer.PushI64(n.Value) } func (n *NbtI64) Pull(reader buff.Buffer) { n.Value = reader.PullI64() }*/ // float type NbtF32 struct { Value float32 } func (n *NbtF32) Type() Typ { return TAG_Float } func (n *NbtF32) Name() string { return "TAG_Float" } /*func (n *NbtF32) Push(writer buff.Buffer) { writer.PushF32(n.Value) } func (n *NbtF32) Pull(reader buff.Buffer) { n.Value = reader.PullF32() }*/ // double type NbtF64 struct { Value float64 } func (n *NbtF64) Type() Typ { return TAG_Double } func (n *NbtF64) Name() string { return "TAG_Double" } /*func (n *NbtF64) Push(writer buff.Buffer) { writer.PushF64(n.Value) } func (n *NbtF64) Pull(reader buff.Buffer) { n.Value = reader.PullF64() }*/ // byte array type NbtArrByt struct { Value []int8 } func (n *NbtArrByt) Type() Typ { return TAG_Byte_Array } func (n *NbtArrByt) Name() string { return "TAG_Byte_Array" } /*func (n *NbtArrByt) Push(writer buff.Buffer) { writer.PushSAS(n.Value, true) } func (n *NbtArrByt) Pull(reader buff.Buffer) { n.Value = reader.PullSAS() }*/ // string type NbtTxt struct { Value string } func (n *NbtTxt) Type() Typ { return TAG_String } func (n *NbtTxt) Name() string { return "TAG_String" } /*func (n *NbtTxt) Push(writer buff.Buffer) { writer.PushTxt(n.Value) } func (n *NbtTxt) Pull(reader buff.Buffer) { n.Value = reader.PullTxt() }*/ // typed list type NbtArrAny struct { NType Typ Value []Nbt } func (n *NbtArrAny) Type() Typ { return TAG_List } func (n *NbtArrAny) Name() string { return "TAG_List" } /*func (n *NbtArrAny) Push(writer buff.Buffer) { if len(n.Value) == 0 { writer.PushByt(0) } else { writer.PushByt(byte(n.NType)) } writer.PushI32(int32(len(n.Value))) for _, nbt := range n.Value { nbt.Push(writer) } } func (n *NbtArrAny) Pull(reader buff.Buffer) { nType := Typ(reader.PullByt()) // this can probably fail... size := reader.PullI32() value := make([]Nbt, size, size) for i := int32(0); i < size; i++ { inst := typeToInst[nType]() inst.Pull(reader) value[i] = inst } }*/ // compound (map) type NbtCompound struct { Named string Value map[string]Nbt } func (n *NbtCompound) Type() Typ { return TAG_Compound } func (n *NbtCompound) Name() string { return "TAG_Compound" } func (n *NbtCompound) Set(name string, data Nbt) { n.Value[name] = data } func (n *NbtCompound) Get(name string) (nbt Nbt, con bool) { nbt, con = n.Value[name] return } // int list type NbtArrI32 struct { Value []int32 } func (n *NbtArrI32) Type() Typ { return TAG_Int_Array } func (n *NbtArrI32) Name() string { return "TAG_Int_Array" } /*func (n *NbtArrI32) Push(writer buff.Buffer) { writer.PushI32(int32(len(n.Value))) for _, value := range n.Value { writer.PushI32(value) } } func (n *NbtArrI32) Pull(reader buff.Buffer) { value := make([]int32, reader.PullI32()) for i := 0; i < len(value); i++ { value[i] = reader.PullI32() } }*/ // long list type NbtArrI64 struct { Value []int64 } func (n *NbtArrI64) Type() Typ { return TAG_Long_Array } func (n *NbtArrI64) Name() string { return "TAG_Long_Array" } ================================================ FILE: apis/data/versions.go ================================================ package data type MinecraftVersion int const ( MC1_12_2 MinecraftVersion = iota MC1_13_2 MC1_14_4 MC1_15_2 ) var CurrentProtocol = MC1_15_2 var protocolVersion = map[MinecraftVersion]int{ MC1_12_2: 340, MC1_13_2: 404, MC1_14_4: 498, MC1_15_2: 578, } func (m MinecraftVersion) Protocol() int { return protocolVersion[m] } func (m MinecraftVersion) String() string { switch m { case MC1_12_2: return "1.12.2" case MC1_13_2: return "1.13.2" case MC1_14_4: return "1.14.4" case MC1_15_2: return "1.15.2" default: return "Unknown" } } ================================================ FILE: apis/ents/entity.go ================================================ package ents import "github.com/golangmc/minecraft-server/apis/base" type Entity interface { Sender base.Unique EntityUUID() int64 } ================================================ FILE: apis/ents/living.go ================================================ package ents type EntityLiving interface { Entity GetHealth() float64 SetHealth(health float64) } ================================================ FILE: apis/ents/player.go ================================================ package ents import "github.com/golangmc/minecraft-server/apis/game" type Player interface { EntityLiving GetGameMode() game.GameMode SetGameMode(mode game.GameMode) GetIsOnline() bool SetIsOnline(state bool) GetProfile() *game.Profile } ================================================ FILE: apis/ents/sender.go ================================================ package ents import ( "github.com/golangmc/minecraft-server/apis/base" ) type Sender interface { base.Named SendMessage(message ...interface{}) } ================================================ FILE: apis/game/difficulty.go ================================================ package game import "fmt" type Difficulty byte const ( PEACEFUL Difficulty = iota EASY NORMAL HARD ) func (d Difficulty) String() string { switch d { case PEACEFUL: return "Peaceful" case EASY: return "Easy" case NORMAL: return "Normal" case HARD: return "Hard" default: panic(fmt.Errorf("no difficulty for id %d", byte(d))) } } func ValueOfDifficulty(d Difficulty) byte { return byte(d) } func DifficultyValueOf(id byte) Difficulty { switch id { case 0: return PEACEFUL case 1: return EASY case 2: return NORMAL case 3: return HARD default: panic(fmt.Errorf("no difficulty for id %d", id)) } } ================================================ FILE: apis/game/dimension.go ================================================ package game type Dimension int const ( NETHER = -1 OVERWORLD = 0 THE_END = 1 ) ================================================ FILE: apis/game/event/block.go ================================================ package event import ( "github.com/golangmc/minecraft-server/apis/game/level" ) type BlockEvent struct { level.Block } type BlockBreakEvent struct { BlockEvent PlayerEvent Cancellable } ================================================ FILE: apis/game/event/cancel.go ================================================ package event type Cancellable struct { cancelled bool } func (c *Cancellable) GetCancelled() bool { return c.cancelled } func (c *Cancellable) SetCancelled(cancelled bool) { c.cancelled = cancelled } ================================================ FILE: apis/game/event/player.go ================================================ package event import "github.com/golangmc/minecraft-server/apis/ents" type PlayerEvent struct { ents.Player } type PlayerJoinEvent struct { PlayerEvent } type PlayerQuitEvent struct { PlayerEvent } ================================================ FILE: apis/game/gamemode.go ================================================ package game type GameMode int const ( SURVIVAL GameMode = iota CREATIVE ADVENTURE SPECTATOR ) func (g GameMode) Encoded(hardcore bool) byte { bit := 0 if hardcore { bit = 0x8 } return byte(g) | byte(bit) } ================================================ FILE: apis/game/level/block.go ================================================ package level type Block interface { X() int Y() int Z() int Chunk() Chunk Level() Level GetBlockType() int SetBlockType(value int) } ================================================ FILE: apis/game/level/chunk.go ================================================ package level import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/data/tags" ) type Chunk interface { buff.BufferPush ChunkX() int ChunkZ() int Slices() []Slice Level() Level // supports values y:[0:15] GetSlice(y int) Slice // supports values x:[0:15] y:[0:255] z: [0:15] GetBlock(x, y, z int) Block HeightMapNbtCompound() *tags.NbtCompound } ================================================ FILE: apis/game/level/level.go ================================================ package level import "github.com/golangmc/minecraft-server/apis/base" type Level interface { base.Named base.Unique Chunks() []Chunk GetChunk(x, z int) Chunk GetChunkIfLoaded(x, z int) Chunk GetBlock(x, y, z int) Block } ================================================ FILE: apis/game/level/slice.go ================================================ package level import ( "github.com/golangmc/minecraft-server/apis/buff" ) type Slice interface { buff.BufferPush Index() int Chunk() Chunk Level() Level // supports values x:[0:15] y:[0:15] z: [0:15] GetBlock(x, y, z int) Block } ================================================ FILE: apis/game/level/value.go ================================================ package level const ( ChunkW = 16 ChunkH = 256 ChunkL = 16 SliceC = 16 SliceH = ChunkH / SliceC SliceS = ChunkW * ChunkL * SliceH BitsPerBlock = 14 MaxPaletteID = (1 << BitsPerBlock) - 1 ) ================================================ FILE: apis/game/leveltype.go ================================================ package game type LevelType int const ( DEFAULT LevelType = iota FLAT LARGEBIOMES AMPLIFIED CUSTOMIZED BUFFET DEFAULT11 ) var typeToName = map[LevelType]string{ DEFAULT: "default", FLAT: "flat", LARGEBIOMES: "largeBiomes", AMPLIFIED: "amplified", CUSTOMIZED: "customized", BUFFET: "buffet", DEFAULT11: "default_1_1", } func (l LevelType) String() string { return typeToName[l] } ================================================ FILE: apis/game/profile.go ================================================ package game import "github.com/golangmc/minecraft-server/apis/uuid" type Profile struct { UUID uuid.UUID Name string Properties []*ProfileProperty } type ProfileProperty struct { Name string Value string Signature *string } ================================================ FILE: apis/logs/logging.go ================================================ package logs import ( "fmt" "io" "os" "time" "github.com/fatih/color" "github.com/golangmc/minecraft-server/apis/base" "github.com/golangmc/minecraft-server/apis/data/chat" ) type LogLevel int const ( Info LogLevel = iota Warn Fail Data ) var BasicLevel = []LogLevel{Info, Warn, Fail} var EveryLevel = []LogLevel{Info, Warn, Fail, Data} type Logging struct { name string writer io.Writer show []LogLevel } func (log *Logging) Name() string { return log.name } func (log *Logging) Show() []LogLevel { return log.show } func (log *Logging) formatPrint(level, message string) { _, _ = fmt.Fprint(log.writer, fmt.Sprintf("[%s] [%s] [%s] %s\n", color.HiGreenString(currentTimeAsText()), level, color.WhiteString(log.Name()), chat.TranslateConsole(message))) } func (log *Logging) info(message string) { log.formatPrint(color.CyanString("INFO"), message) } func (log *Logging) warn(message string) { log.formatPrint(color.YellowString("WARN"), message) } func (log *Logging) fail(message string) { log.formatPrint(color.RedString("FAIL"), message) } func (log *Logging) data(message string) { log.formatPrint(color.MagentaString("DATA"), message) } func (log *Logging) Info(message ...interface{}) { if !checkIfLevelShows(log, Info) { return } log.info(base.ConvertToString(message...)) } func (log *Logging) Warn(message ...interface{}) { if !checkIfLevelShows(log, Warn) { return } log.warn(base.ConvertToString(message...)) } func (log *Logging) Fail(message ...interface{}) { if !checkIfLevelShows(log, Fail) { return } log.fail(base.ConvertToString(message...)) } func (log *Logging) Data(message ...interface{}) { if !checkIfLevelShows(log, Data) { return } log.data(base.ConvertToString(message...)) } func (log *Logging) InfoF(format string, a ...interface{}) { if !checkIfLevelShows(log, Info) { return } log.info(fmt.Sprintf(format, a...)) } func (log *Logging) WarnF(format string, a ...interface{}) { if !checkIfLevelShows(log, Warn) { return } log.warn(fmt.Sprintf(format, a...)) } func (log *Logging) FailF(format string, a ...interface{}) { if !checkIfLevelShows(log, Fail) { return } log.fail(fmt.Sprintf(format, a...)) } func (log *Logging) DataF(format string, a ...interface{}) { if !checkIfLevelShows(log, Data) { return } log.data(fmt.Sprintf(format, a...)) } func NewLogging(name string, show ...LogLevel) *Logging { return NewLoggingWith(name, os.Stdout, show...) } func NewLoggingWith(name string, writer io.Writer, show ...LogLevel) *Logging { return &Logging{name: name, writer: writer, show: show} } func currentTimeAsText() string { h, m, s := time.Now().Clock() return fmt.Sprintf("%02d:%02d:%02d", h, m, s) } func checkIfLevelShows(log *Logging, lvl LogLevel) bool { for _, a := range log.Show() { if a == lvl { return true } } return false } ================================================ FILE: apis/math/vector.go ================================================ package math type Vector2F struct { X float64 Z float64 } type Vector3F struct { Vector2F Y float64 } ================================================ FILE: apis/rand/random.go ================================================ package rand import "crypto/rand" func RandomByteArray(len int) []byte { array := make([]byte, 4) _, _ = rand.Read(array) return array } ================================================ FILE: apis/server.go ================================================ package apis import ( "sync" "github.com/golangmc/minecraft-server/apis/cmds" "github.com/golangmc/minecraft-server/apis/ents" "github.com/golangmc/minecraft-server/apis/logs" "github.com/golangmc/minecraft-server/apis/task" "github.com/golangmc/minecraft-server/apis/util" "github.com/golangmc/minecraft-server/apis/uuid" apis_base "github.com/golangmc/minecraft-server/apis/base" impl_base "github.com/golangmc/minecraft-server/impl/base" ) type Server interface { apis_base.State Logging() *logs.Logging Command() *cmds.CommandManager Tasking() *task.Tasking Watcher() util.Watcher Players() []ents.Player ConnByUUID(uuid uuid.UUID) impl_base.Connection PlayerByUUID(uuid uuid.UUID) ents.Player PlayerByConn(conn impl_base.Connection) ents.Player ServerVersion() string Broadcast(message string) } var instance *Server var syncOnce sync.Once func MinecraftServer() Server { if instance == nil { panic("server is unavailable") } return *instance } func SetMinecraftServer(server Server) { syncOnce.Do(func() { instance = &server }) } ================================================ FILE: apis/task/tasking.go ================================================ package task import ( "fmt" "sync/atomic" "time" "github.com/golangmc/minecraft-server/apis/base" ) type Task struct { uuid int64 exec *func(task *Task) cancel bool period int64 paused int64 tasker *Tasking } type Tasking struct { // milliseconds per tick mpt int64 // uuid -> task tasks map[int64]*Task // task -> last ran ticks map[*Task]int64 // time -> tasks queue map[int64][]*Task next uint64 done bool kill chan bool } func NewTasking(mpt int64) *Tasking { return &Tasking{ mpt: mpt, tasks: make(map[int64]*Task), ticks: make(map[*Task]int64), queue: make(map[int64][]*Task), } } func (t *Tasking) Load() { t.done = false t.kill = make(chan bool, 1) go t.tick() } func (t *Tasking) Kill() { if t.done { return } t.done = true t.kill <- true for k := range t.ticks { delete(t.ticks, k) } for k := range t.queue { delete(t.queue, k) } for k, v := range t.tasks { delete(t.tasks, k) v.Cancel() } close(t.kill) } func (t *Tasking) tick() { tick := time.NewTicker(time.Millisecond) defer tick.Stop() for { select { case <-t.kill: return case curr := <-tick.C: t.tickQueue(curr) t.tickTasks(curr) } } } func (t *Tasking) tickTasks(curr time.Time) { unix := curr.UnixNano() / 1e6 for task, last := range t.ticks { if unix-last < task.period { continue // not ready to be executed } if err := task.attemptExec(); err != nil { task.cancel = true fmt.Printf("%v", err) } if task.cancel || task.period <= 0 { delete(t.ticks, task) } else { t.ticks[task] = unix } } } func (t *Tasking) tickQueue(curr time.Time) { // this is for handling the delayed tasks // delay gets counted down, and then the tasks are added to the queue used for tickTasks unix := curr.UnixNano() / 1e6 for when, tasks := range t.queue { if unix < when { continue // not ready to be executed } delete(t.queue, when) for _, task := range tasks { t.ticks[task] = 0 } } } func (t *Tasking) nextTaskU() int64 { return int64(atomic.AddUint64(&t.next, 1)) } func (t *Tasking) repeats(period int64, function func(task *Task)) { task := t.newTask(period, 0, &function) t.ticks[task] = 0 t.tasks[task.uuid] = task } func (t *Tasking) delayed(paused int64, function func(task *Task)) { task := t.newTask(0, paused, &function) unix := time.Now().UnixNano() / 1e6 when := unix + paused queue, exists := t.queue[when] if !exists { queue = make([]*Task, 0) } queue = append(queue, task) t.queue[when] = queue t.tasks[task.uuid] = task } // repeats the function every period, in ticks func (t *Tasking) Every(period int64, function func(task *Task)) { t.repeats(period*t.mpt, function) } // executes the function after paused, in ticks func (t *Tasking) After(paused int64, function func(task *Task)) { t.delayed(paused*t.mpt, function) } func (t *Tasking) EveryTime(period int64, duration time.Duration, function func(task *Task)) { t.repeats(period*duration.Milliseconds(), function) } func (t *Tasking) AfterTime(paused int64, duration time.Duration, function func(task *Task)) { t.delayed(paused*duration.Milliseconds(), function) } func (t *Tasking) newTask(period int64, paused int64, function *func(task *Task)) *Task { return &Task{ tasker: t, uuid: t.nextTaskU(), exec: function, period: period, paused: paused, } } func (t *Task) attemptExec() (error error) { return base.Attempt(func() { (*t.exec)(t) }) } func (t *Task) Cancel() { t.cancel = true } func (t *Task) Tasker() *Tasking { return t.tasker } ================================================ FILE: apis/task/tasking_test.go ================================================ package task import ( "fmt" "testing" ) func TestTasker_Load(t *testing.T) { tasker := NewTasking(1_000 / 20) tasker.Load() tasker.Every(20, printCurrentTask) done := <-tasker.kill fmt.Printf("Tasker done: %t\n", done) } var count = 0 func printCurrentTask(_ *Task) { fmt.Printf("Running print task %d\n", count) if count++; count >= 2 { panic("hi") } } ================================================ FILE: apis/urls/urls.go ================================================ package urls import ( "io/ioutil" "net/http" ) func GetByte(url string) (res []byte, err error) { // get out, err := http.Get(url) if err != nil { return } // read body bdy, err := ioutil.ReadAll(out.Body) if err != nil { return } // assign response res = bdy return } func GetText(url string) (res string, err error) { arr, err := GetByte(url) if err != nil { return } res = string(arr) return } ================================================ FILE: apis/util/formats.go ================================================ package util import ( "time" "github.com/hako/durafmt" ) func FormatTime(durationInSeconds int64) string { return durafmt.Parse(time.Second * time.Duration(durationInSeconds)).String() } ================================================ FILE: apis/util/watcher.go ================================================ package util import ( "fmt" "reflect" "sync" ) type Watcher interface { Has(topic string) bool Pub(topic string, args ...interface{}) PubAs(topicValue ...interface{}) PubTo(topicType reflect.Type, args ...interface{}) Sub(topic string, function interface{}) Handler SubAs(function interface{}) Handler SubTo(topicType reflect.Type, function interface{}) Handler } type Handler interface { UnSub() } func NewWatcher() Watcher { return &watcher{ locker: sync.Mutex{}, topics: make(map[string][]*handler), } } type watcher struct { locker sync.Mutex topics map[string][]*handler } func (w *watcher) Has(topic string) bool { handlers, contains := w.topics[topic] return contains && len(handlers) > 0 } func (w *watcher) Pub(topic string, args ...interface{}) { if handlers, contains := w.topics[topic]; contains && len(handlers) > 0 { callArgs := make([]reflect.Value, 0) for _, arg := range args { callArgs = append(callArgs, reflect.ValueOf(arg)) } for _, handler := range handlers { if handler.function.Type().NumIn() != len(callArgs) { continue } handler.function.Call(callArgs) } } } func (w *watcher) PubAs(topicValue ...interface{}) { w.PubTo(reflect.TypeOf(topicValue[0]), topicValue...) } func (w *watcher) PubTo(topicType reflect.Type, args ...interface{}) { w.Pub(topicType.String(), args...) } func (w *watcher) Sub(topic string, function interface{}) Handler { w.locker.Lock() defer w.locker.Unlock() // check if function is actual a function if reflect.TypeOf(function).Kind() != reflect.Func { panic(fmt.Errorf("cannot sub with %v, must be reflect.Func", reflect.TypeOf(function).Kind())) } handler := &handler{ topic: topic, watch: w, function: reflect.ValueOf(function), } // append watch handler to topic list w.topics[topic] = append(w.topics[topic], handler) return handler } func (w *watcher) SubAs(function interface{}) Handler { funcType := reflect.TypeOf(function) // check if function is actual a function if funcType.Kind() != reflect.Func { panic(fmt.Errorf("cannot sub with %v, must be reflect.Func", funcType.Kind())) } if funcType.NumIn() == 0 { panic(fmt.Errorf("cannot sub with %v, must have at least 1 input parameter", funcType)) } return w.SubTo(funcType.In(0), function) } func (w *watcher) SubTo(topicType reflect.Type, function interface{}) Handler { return w.Sub(topicType.String(), function) } type handler struct { topic string watch *watcher function reflect.Value } func (h *handler) UnSub() { handlers := h.watch.topics[h.topic] if handlers == nil { return } for i, elem := range handlers { if elem == h { h.watch.topics[h.topic] = append(h.watch.topics[h.topic][:i], h.watch.topics[h.topic][i+1:]...) } } } ================================================ FILE: apis/uuid/uuids.go ================================================ package uuid import ( "encoding/binary" "github.com/satori/go.uuid" ) type UUID = uuid.UUID func NewUUID() UUID { gen := uuid.NewV4() /*if err != nil { panic(err) }*/ return gen } func TextToUUID(text string) (data UUID, err error) { return uuid.FromString(text) } func BitsToUUID(msb, lsb int64) (data UUID, err error) { mBytes := make([]byte, 8) lBytes := make([]byte, 8) binary.BigEndian.PutUint64(mBytes, uint64(msb)) binary.BigEndian.PutUint64(lBytes, uint64(lsb)) return uuid.FromBytes(append(mBytes, lBytes...)) } func UUIDToText(uuid UUID) (text string, err error) { data, err := uuid.MarshalText() if err == nil { text = string(data) } return } func SigBits(uuid UUID) (msb, lsb int64) { bytes := uuid.Bytes() msb = 0 lsb = 0 for i := 0; i < 8; i++ { msb = (msb << 0x08) | int64(bytes[i]&0xFF) } for i := 8; i < 16; i++ { lsb = (lsb << 0x08) | int64(bytes[i]&0xFF) } return } ================================================ FILE: go.mod ================================================ module github.com/golangmc/minecraft-server go 1.13 require ( github.com/fatih/color v1.9.0 github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4 github.com/mattn/go-colorable v0.1.6 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/satori/go.uuid v1.2.0 golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect ) ================================================ FILE: go.sum ================================================ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/golangmc/minecraft-server v0.0.0-20200307201348-f5daeb2b6d07 h1:BCos3PMMuZxapSHWy/FfT1PBai2qInTqk1z5JjsiCE4= github.com/golangmc/minecraft-server v0.0.0-20200307201348-f5daeb2b6d07/go.mod h1:KXyWige3rVvzcZEnSzJJYCeyTdyaBATU03/dVMpS72M= github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4 h1:60gBOooTSmNtrqNaRvrDbi8VAne0REaek2agjnITKSw= github.com/hako/durafmt v0.0.0-20191009132224-3f39dc1ed9f4/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= ================================================ FILE: impl/base/combine.go ================================================ package base import "github.com/golangmc/minecraft-server/apis/ents" type PlayerAndConnection struct { Connection ents.Player } ================================================ FILE: impl/base/compact.go ================================================ package base type Compacter struct { Values []int64 bpb int max int } func NewCompacter(bits, size int) *Compacter { sliceSize := iDontKnowWhatThisDoes(size*bits, 64) / 64 return &Compacter{ bpb: bits, max: (1 << bits) - 1, Values: make([]int64, sliceSize, sliceSize), } } func (c *Compacter) Set(index int, value int) int { bIndex := index * c.bpb sIndex := bIndex >> 0x06 eIndex := (((index + 1) * c.bpb) - 1) >> 0x06 uIndex := bIndex ^ (sIndex << 0x06) previousValue := int64(uint64(c.Values[sIndex])>>uIndex) & int64(c.max) c.Values[sIndex] = c.Values[sIndex]&int64(^(c.max<> pIndex) << pIndex) | uint64((value&c.max)>>zIndex)) } return int(previousValue) } func (c *Compacter) Get(index int) int { bIndex := index * c.bpb sIndex := bIndex >> 0x06 eIndex := (((index + 1) * c.bpb) - 1) >> 0x06 uIndex := bIndex ^ (sIndex << 0x06) if sIndex == eIndex { return int((uint64(c.Values[sIndex]) >> uIndex) & uint64(c.max)) } zIndex := 64 - uIndex return int(uint64(c.Values[sIndex]>>uIndex) | uint64(c.Values[eIndex]<> 38 y := int64(val) & 0xFFF z := int64(val) << 26 >> 38 return data.PositionI{ X: x, Y: y, Z: z, } } func (b *buffer) PullNbt() *tags.NbtCompound { typ := tags.Typ(b.PullByt()) fmt.Println("==type") fmt.Println(typ) if typ != tags.TAG_Compound { panic("root tag must be compound") // probably shouldn't panic? } name := b.PullTxt() if len(name) != 0 { panic("root compound should have an empty name") } fmt.Println("==name") fmt.Println(name) tag := &tags.NbtCompound{} b.pullNbt(tag) return tag } // push func (b *buffer) PushBit(data bool) { if data { b.pushNext(byte(0x01)) } else { b.pushNext(byte(0x00)) } } func (b *buffer) PushByt(data byte) { b.pushNext(data) } func (b *buffer) PushI16(data int16) { b.pushNext( byte(data)>>8, byte(data)) } func (b *buffer) PushI32(data int32) { b.pushNext( byte(data>>24), byte(data>>16), byte(data>>8), byte(data)) } func (b *buffer) PushI64(data int64) { b.pushNext( byte(data>>56), byte(data>>48), byte(data>>40), byte(data>>32), byte(data>>24), byte(data>>16), byte(data>>8), byte(data)) } func (b *buffer) PushF32(data float32) { b.PushI32(int32(math.Float32bits(data))) } func (b *buffer) PushF64(data float64) { b.PushI64(int64(math.Float64bits(data))) } func (b *buffer) PushVrI(data int32) { for { temp := data & 0x7F data >>= 7 if data != 0 { temp |= 0x80 } b.pushNext(byte(temp)) if data == 0 { break } } } func (b *buffer) PushVrL(data int64) { for { temp := data & 0x7F data >>= 7 if data != 0 { temp |= 0x80 } b.pushNext(byte(temp)) if data == 0 { break } } } func (b *buffer) PushTxt(data string) { b.PushUAS([]byte(data), true) } func (b *buffer) PushUAS(data []byte, prefixWithLen bool) { if prefixWithLen { b.PushVrI(int32(len(data))) } b.pushNext(data...) } func (b *buffer) PushSAS(data []int8, prefixWithLen bool) { b.PushUAS(asUArray(data), prefixWithLen) } func (b *buffer) PushUID(data uuid.UUID) { msb, lsb := uuid.SigBits(data) b.PushI64(msb) b.PushI64(lsb) } func (b *buffer) PushPos(data data.PositionI) { b.PushI64(((data.X & 0x3FFFFFF) << 38) | ((data.Z & 0x3FFFFFF) << 12) | (data.Y & 0xFFF)) } func (b *buffer) PushNbt(data *tags.NbtCompound) { if data == nil { b.PushByt(0) } else { b.PushByt(byte(data.Type())) b.pushNext(0, 0) b.pushNbt(data) } } // internal func (b *buffer) pullNext() byte { if b.iIndex >= b.Len() { return 0 // panic("reached end of buffer") } next := b.bArray[b.iIndex] b.iIndex++ if b.oIndex > 0 { b.oIndex-- } return next } func (b *buffer) pullSize(next int) []byte { bytes := make([]byte, next) for i := 0; i < next; i++ { bytes[i] = b.pullNext() } return bytes } func (b *buffer) pushNext(bArray ...byte) { b.oIndex += int32(len(bArray)) b.bArray = append(b.bArray, bArray...) } func (b *buffer) pullVariable(max int) int64 { var num int var res int64 for { tmp := int64(b.pullNext()) res |= (tmp & 0x7F) << uint(num*7) if num++; num > max { panic("VarInt > " + strconv.Itoa(max)) } if tmp&0x80 != 0x80 { break } } return res } func asSArray(bytes []byte) []int8 { array := make([]int8, 0) for _, b := range bytes { array = append(array, int8(b)) } return array } func asUArray(bytes []int8) []byte { array := make([]byte, 0) for _, b := range bytes { array = append(array, byte(b)) } return array } var typeToInst = map[tags.Typ]func() tags.Nbt{ tags.TAG_End: func() tags.Nbt { return &tags.NbtEnd{} }, tags.TAG_Byte: func() tags.Nbt { return &tags.NbtByt{} }, tags.TAG_Short: func() tags.Nbt { return &tags.NbtI16{} }, tags.TAG_Int: func() tags.Nbt { return &tags.NbtI32{} }, tags.TAG_Long: func() tags.Nbt { return &tags.NbtI64{} }, tags.TAG_Float: func() tags.Nbt { return &tags.NbtF32{} }, tags.TAG_Double: func() tags.Nbt { return &tags.NbtF64{} }, tags.TAG_Byte_Array: func() tags.Nbt { return &tags.NbtArrByt{} }, tags.TAG_String: func() tags.Nbt { return &tags.NbtTxt{} }, tags.TAG_List: func() tags.Nbt { return &tags.NbtArrAny{} }, tags.TAG_Compound: func() tags.Nbt { return &tags.NbtCompound{} }, tags.TAG_Int_Array: func() tags.Nbt { return &tags.NbtArrI32{} }, tags.TAG_Long_Array: func() tags.Nbt { return &tags.NbtArrI64{} }, } func (b *buffer) pullNbt(data tags.Nbt) { switch data.Type() { case tags.TAG_End: // nothing break case tags.TAG_Byte: data.(*tags.NbtByt).Value = int8(b.PullByt()) break case tags.TAG_Short: panic("unimplemented") break case tags.TAG_Int: panic("unimplemented") break case tags.TAG_Long: panic("unimplemented") break case tags.TAG_Float: panic("unimplemented") break case tags.TAG_Double: panic("unimplemented") break case tags.TAG_Byte_Array: panic("unimplemented") break case tags.TAG_String: panic("unimplemented") break case tags.TAG_List: panic("unimplemented") break case tags.TAG_Compound: value := make(map[string]tags.Nbt) fmt.Println("reading compound") for { typ := tags.Typ(b.PullByt()) if typ == tags.TAG_End { fmt.Println("encountered break") break } fmt.Println("===type") fmt.Println(typ) name := b.pullNbtTxt() fmt.Println("===name") fmt.Println(name) inst := typeToInst[typ]() b.pullNbt(inst) value[name] = inst } data.(*tags.NbtCompound).Value = value break case tags.TAG_Int_Array: panic("unimplemented") break case tags.TAG_Long_Array: value := make([]int64, b.PullI32()) for i := 0; i < len(value); i++ { value[i] = b.PullI64() } data.(*tags.NbtArrI64).Value = value break } } func (b *buffer) pushNbt(data tags.Nbt) { switch data.Type() { case tags.TAG_End: // nothing break case tags.TAG_Byte: b.PushByt(byte(data.(*tags.NbtByt).Value)) break case tags.TAG_Short: panic("unimplemented") break case tags.TAG_Int: panic("unimplemented") break case tags.TAG_Long: panic("unimplemented") break case tags.TAG_Float: panic("unimplemented") break case tags.TAG_Double: panic("unimplemented") break case tags.TAG_Byte_Array: panic("unimplemented") break case tags.TAG_String: panic("unimplemented") break case tags.TAG_List: panic("unimplemented") break case tags.TAG_Compound: for name, tag := range data.(*tags.NbtCompound).Value { b.PushByt(byte(tag.Type())) if tag.Type() == tags.TAG_End { continue } b.pushNbtTxt(name) b.pushNbt(tag) } b.PushByt(0) break case tags.TAG_Int_Array: panic("unimplemented") break case tags.TAG_Long_Array: value := data.(*tags.NbtArrI64).Value b.PushI32(int32(len(value))) for _, value := range value { b.PushI64(value) } break } } func (b *buffer) pullNbtTxt() string { size := b.PullI16() data := b.pullSize(int(size)) return string(data) } func (b *buffer) pushNbtTxt(data string) { b.PushI16(int16(len(data))) b.PushUAS([]byte(data), false) } ================================================ FILE: impl/conn/connect.go ================================================ package conn import ( "bytes" "compress/zlib" "crypto/cipher" "fmt" "io" "net" "github.com/golangmc/minecraft-server/apis/rand" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/conn/crypto" ) type connection struct { new bool tcp *net.TCPConn state base.PacketState certify Certify compact Compact } func NewConnection(conn *net.TCPConn) base.Connection { return &connection{ new: true, tcp: conn, certify: Certify{}, compact: Compact{}, } } func (c *connection) Address() net.Addr { return c.tcp.RemoteAddr() } func (c *connection) GetState() base.PacketState { return c.state } func (c *connection) SetState(state base.PacketState) { c.state = state } type Certify struct { name string used bool data []byte encrypt cipher.Stream decrypt cipher.Stream } func (c *connection) Encrypt(data []byte) (output []byte) { if !c.certify.used { return data } output = make([]byte, len(data)) c.certify.encrypt.XORKeyStream(output, data) return } func (c *connection) Decrypt(data []byte) (output []byte) { if !c.certify.used { return data } output = make([]byte, len(data)) c.certify.decrypt.XORKeyStream(output, data) return } func (c *connection) CertifyName() string { return c.certify.name } func (c *connection) CertifyData() []byte { return c.certify.data } func (c *connection) CertifyUpdate(secret []byte) { encrypt, decrypt, err := crypto.NewEncryptAndDecrypt(secret) c.certify.encrypt = encrypt c.certify.decrypt = decrypt if err != nil { panic(fmt.Errorf("failed to enable encryption for user: %s\n%v", c.CertifyName(), err)) } c.certify.used = true c.certify.data = secret } func (c *connection) CertifyValues(name string) { c.certify.name = name c.certify.data = rand.RandomByteArray(4) } type Compact struct { used bool size int32 } func (c *connection) Deflate(data []byte) (output []byte) { if !c.compact.used { return data } var out bytes.Buffer writer, _ := zlib.NewWriterLevel(&out, zlib.BestCompression) _, _ = writer.Write(data) _ = writer.Close() output = out.Bytes() return } func (c *connection) Inflate(data []byte) (output []byte) { if !c.compact.used { return data } reader, err := zlib.NewReader(bytes.NewReader(data)) if err != nil { panic(err) } var out bytes.Buffer _, _ = io.Copy(&out, reader) output = out.Bytes() return } func (c *connection) Pull(data []byte) (len int, err error) { len, err = c.tcp.Read(data) return } func (c *connection) Push(data []byte) (len int, err error) { len, err = c.tcp.Write(data) return } func (c *connection) Stop() (err error) { err = c.tcp.Close() return } func (c *connection) SendPacket(packet base.PacketO) { bufO := NewBuffer() temp := NewBuffer() // write buffer bufO.PushVrI(packet.UUID()) packet.Push(bufO, c) temp.PushVrI(bufO.Len()) temp.PushUAS(bufO.UAS(), false) _, _ = c.tcp.Write(c.Encrypt(temp.UAS())) } ================================================ FILE: impl/conn/crypto/cfb8.go ================================================ package crypto import ( "crypto/aes" "crypto/cipher" ) type cfb8 struct { b cipher.Block sr []byte srEnc []byte srPos int decrypt bool } func newEncrypt(block cipher.Block, iv []byte) cipher.Stream { if len(iv) != block.BlockSize() { panic("cfb8.newEncrypt: IV length must equal block size") } return newCFB8(block, iv, false) } func newDecrypt(block cipher.Block, iv []byte) cipher.Stream { if len(iv) != block.BlockSize() { panic("cfb8.newDecrypt: IV length must equal block size") } return newCFB8(block, iv, true) } func newCFB8(block cipher.Block, iv []byte, decrypt bool) cipher.Stream { blockSize := block.BlockSize() if len(iv) != blockSize { return nil } x := &cfb8{ b: block, sr: make([]byte, blockSize*4), srEnc: make([]byte, blockSize), srPos: 0, decrypt: decrypt, } copy(x.sr, iv) return x } func (x *cfb8) XORKeyStream(dst, src []byte) { blockSize := x.b.BlockSize() for i := 0; i < len(src); i++ { x.b.Encrypt(x.srEnc, x.sr[x.srPos:x.srPos+blockSize]) var c byte if x.decrypt { c = src[i] dst[i] = c ^ x.srEnc[0] } else { c = src[i] ^ x.srEnc[0] dst[i] = c } x.sr[x.srPos+blockSize] = c x.srPos++ if x.srPos+blockSize == len(x.sr) { copy(x.sr, x.sr[x.srPos:]) x.srPos = 0 } } } func NewEncryptAndDecrypt(secret []byte) (encrypt cipher.Stream, decrypt cipher.Stream, err error) { block, err := aes.NewCipher(secret) if err != nil { return nil, nil, err } encrypt = newEncrypt(block, secret) decrypt = newDecrypt(block, secret) return } ================================================ FILE: impl/conn/network.go ================================================ package conn import ( "fmt" "net" "reflect" "strconv" "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/logs" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/data/system" ) type network struct { host string port int logger *logs.Logging packets base.Packets join chan base.PlayerAndConnection quit chan base.PlayerAndConnection report chan system.Message } func NewNetwork(host string, port int, packet base.Packets, report chan system.Message, join chan base.PlayerAndConnection, quit chan base.PlayerAndConnection) base.Network { return &network{ host: host, port: port, join: join, quit: quit, report: report, logger: logs.NewLogging("network", logs.EveryLevel...), packets: packet, } } func (n *network) Load() { if err := n.startListening(); err != nil { n.report <- system.Make(system.FAIL, err) return } } func (n *network) Kill() { } func (n *network) startListening() error { ser, err := net.ResolveTCPAddr("tcp", n.host+":"+strconv.Itoa(n.port)) if err != nil { return fmt.Errorf("address resolution failed [%v]", err) } tcp, err := net.ListenTCP("tcp", ser) if err != nil { return fmt.Errorf("failed to bind [%v]", err) } n.logger.InfoF("listening on %s:%d", n.host, n.port) go func() { for { con, err := tcp.AcceptTCP() if err != nil { n.report <- system.Make(system.FAIL, err) break } _ = con.SetNoDelay(true) _ = con.SetKeepAlive(true) go handleConnect(n, NewConnection(con)) } }() return nil } func handleConnect(network *network, conn base.Connection) { network.logger.DataF("New Connection from &6%v", conn.Address()) var inf []byte for { inf = make([]byte, 1024) sze, err := conn.Pull(inf) if err != nil && err.Error() == "EOF" { network.quit <- base.PlayerAndConnection{ Player: nil, Connection: conn, } break } if err != nil || sze == 0 { _ = conn.Stop() network.quit <- base.PlayerAndConnection{ Player: nil, Connection: conn, } break } buf := NewBufferWith(conn.Decrypt(inf[:sze])) // decompression // decryption if buf.UAS()[0] == 0xFE { // LEGACY PING continue } packetLen := buf.PullVrI() bufI := NewBufferWith(buf.UAS()[buf.InI() : buf.InI()+packetLen]) bufO := NewBuffer() handleReceive(network, conn, bufI, bufO) if bufO.Len() > 1 { temp := NewBuffer() temp.PushVrI(bufO.Len()) comp := NewBuffer() comp.PushUAS(conn.Deflate(bufO.UAS()), false) temp.PushUAS(comp.UAS(), false) _, err := conn.Push(conn.Encrypt(temp.UAS())) if err != nil { network.logger.Fail("Failed to push client bound packet: %v", err) } } } } func handleReceive(network *network, conn base.Connection, bufI buff.Buffer, bufO buff.Buffer) { uuid := bufI.PullVrI() packetI := network.packets.GetPacketI(uuid, conn.GetState()) if packetI == nil { network.logger.DataF("unable to decode %v packet with uuid: %d", conn.GetState(), uuid) return } network.logger.DataF("GET packet: %d | %v | %v", packetI.UUID(), reflect.TypeOf(packetI), conn.GetState()) // populate incoming packet packetI.Pull(bufI, conn) network.packets.PubAs(packetI) network.packets.PubAs(packetI, conn) } ================================================ FILE: impl/cons/console.go ================================================ package cons import ( "bufio" "io" "os" "github.com/golangmc/minecraft-server/apis/base" "github.com/golangmc/minecraft-server/apis/logs" "github.com/golangmc/minecraft-server/impl/data/system" ) type Console struct { i io.Reader o io.Writer logger *logs.Logging IChannel chan string OChannel chan string report chan system.Message } func NewConsole(report chan system.Message) *Console { console := &Console{ IChannel: make(chan string), OChannel: make(chan string), report: report, } console.i = io.MultiReader(os.Stdin) console.o = io.MultiWriter(os.Stdout, console.newLogFile("latest.log")) console.logger = logs.NewLoggingWith("console", console.o, logs.EveryLevel...) return console } func (c *Console) Load() { // handle i channel go func() { scanner := bufio.NewScanner(c.i) for scanner.Scan() { err := base.Attempt(func() { c.IChannel <- scanner.Text() }) if err != nil { c.report <- system.Make(system.FAIL, err) } } }() // handle o channel go func() { for line := range c.OChannel { c.logger.Info(line) } }() } func (c *Console) Kill() { defer func() { _ = recover() // ignore panic with closing closed channel }() // save the log file as YYYY-MM-DD-{index}.log{.gz optionally compressed} close(c.IChannel) close(c.OChannel) } func (c *Console) Name() string { return "ConsoleSender" } func (c *Console) SendMessage(message ...interface{}) { defer func() { if err := recover(); err != nil { c.report <- system.Make(system.FAIL, err) } }() c.OChannel <- base.ConvertToString(message...) } type logFileWriter struct { file *os.File } func (c *Console) newLogFile(name string) io.Writer { file, err := os.Create(name) if err != nil { c.report <- system.Make(system.FAIL, err) return nil } return &logFileWriter{file: file} } func (l *logFileWriter) Write(p []byte) (n int, err error) { // this is going to be messy, but this should convert to string, strip colors, and then write to file. Don't @ me. return l.file.Write(p) } ================================================ FILE: impl/data/client/abilities.go ================================================ package client import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/impl/mask" ) type PlayerAbilities struct { mask.Masking Invulnerable bool Flying bool AllowFlight bool InstantBuild bool // creative?? } func (p *PlayerAbilities) Push(writer buff.Buffer) { flags := byte(0) p.Set(&flags, 0x01, p.Invulnerable) p.Set(&flags, 0x02, p.Flying) p.Set(&flags, 0x04, p.AllowFlight) p.Set(&flags, 0x08, p.InstantBuild) writer.PushByt(flags) } func (p *PlayerAbilities) Pull(reader buff.Buffer) { flags := reader.PullByt() p.Invulnerable = p.Has(flags, 0x01) p.Flying = p.Has(flags, 0x02) p.AllowFlight = p.Has(flags, 0x04) p.InstantBuild = p.Has(flags, 0x08) } ================================================ FILE: impl/data/client/chat.go ================================================ package client type ChatMode int const ( Full ChatMode = iota Cmds None ) ================================================ FILE: impl/data/client/hand.go ================================================ package client type MainHand int const ( Hand_L MainHand = iota Hand_R ) ================================================ FILE: impl/data/client/playerinfo.go ================================================ package client import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/ents" ) type PlayerInfoAction int32 const ( AddPlayer PlayerInfoAction = iota UpdateGameMode UpdateLatency UpdateDisplayName RemovePlayer ) type PlayerInfo interface { buff.BufferPush } type PlayerInfoAddPlayer struct { Player ents.Player } func (p *PlayerInfoAddPlayer) Push(writer buff.Buffer) { prof := p.Player.GetProfile() writer.PushUID(prof.UUID) writer.PushTxt(prof.Name) writer.PushVrI(int32(len(prof.Properties))) for _, prop := range prof.Properties { writer.PushTxt(prop.Name) writer.PushTxt(prop.Value) if prop.Signature == nil { writer.PushBit(false) } else { writer.PushBit(true) writer.PushTxt(*prop.Signature) } } writer.PushVrI(int32(p.Player.GetGameMode())) writer.PushVrI(0) // update this to the player's actual ping writer.PushBit(false) // update this to be whether the player has a custom display name or not, write that name as json if they do } type PlayerInfoUpdateLatency struct { } ================================================ FILE: impl/data/client/position.go ================================================ package client import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/impl/mask" ) type Relativity struct { mask.Masking X bool Y bool Z bool AxisX bool AxisY bool } func (r *Relativity) Push(writer buff.Buffer) { flags := byte(0) r.Set(&flags, 0x01, r.X) r.Set(&flags, 0x02, r.Y) r.Set(&flags, 0x04, r.Z) // the fact that these are flipped deeply bothers me. r.Set(&flags, 0x08, r.AxisY) r.Set(&flags, 0x10, r.AxisX) writer.PushByt(flags) } ================================================ FILE: impl/data/client/skin.go ================================================ package client import ( "fmt" "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/impl/mask" ) type SkinParts struct { mask.Masking Cape bool Head bool Body bool ArmL bool ArmR bool LegL bool LegR bool } func (d *SkinParts) String() string { return fmt.Sprintf("Cape:%t Head:%t Body:%t ArmL:%t ArmR:%t LegL:%t LegR:%t", d.Cape, d.Head, d.Body, d.ArmL, d.ArmR, d.LegL, d.LegR) } func (d *SkinParts) Push(writer buff.Buffer) { flags := byte(0) d.Set(&flags, 0x01, d.Cape) d.Set(&flags, 0x02, d.Body) d.Set(&flags, 0x04, d.ArmL) d.Set(&flags, 0x08, d.ArmR) d.Set(&flags, 0x10, d.LegL) d.Set(&flags, 0x20, d.LegR) d.Set(&flags, 0x40, d.Head) writer.PushByt(flags) } func (d *SkinParts) Pull(reader buff.Buffer) { flags := reader.PullByt() d.Cape = d.Has(flags, 0x01) d.Body = d.Has(flags, 0x02) d.ArmL = d.Has(flags, 0x04) d.ArmR = d.Has(flags, 0x08) d.LegL = d.Has(flags, 0x10) d.LegR = d.Has(flags, 0x20) d.Head = d.Has(flags, 0x40) } ================================================ FILE: impl/data/client/slot.go ================================================ package client type HotBarSlot byte const ( SLOT_0 HotBarSlot = iota SLOT_1 SLOT_2 SLOT_3 SLOT_4 SLOT_5 SLOT_6 SLOT_7 SLOT_8 ) ================================================ FILE: impl/data/client/status.go ================================================ package client type StatusAction int const ( Respawn StatusAction = iota Request ) ================================================ FILE: impl/data/plugin/message.go ================================================ package plugin import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/data" ) type Message interface { Chan() string buff.BufferPush buff.BufferPull } var registry = createMessageRegistry() type MessageRegistry struct { channels map[string]func() Message } func createMessageRegistry() MessageRegistry { registry := MessageRegistry{make(map[string]func() Message)} registry.channels["minecraft:brand"] = func() Message { return &Brand{} } registry.channels["minecraft:debug/paths"] = func() Message { return &DebugPaths{} } registry.channels["minecraft:debug/neighbors_update"] = func() Message { return &DebugNeighbors{} } return registry } func GetMessageForChannel(channel string) Message { creator := registry.channels[channel] if creator == nil { return nil } return creator() } const ( CHANNEL_BRAND = "minecraft:brand" CHANNEL_DEBUG_PATHS = "minecraft:debug/paths" CHANNEL_DEBUG_NEIGHBORS = "minecraft:debug/neighbors_update" ) // look, they're like cute little packets :D type Brand struct { Name string } func (b *Brand) Chan() string { return CHANNEL_BRAND } func (b *Brand) Push(writer buff.Buffer) { writer.PushTxt(b.Name) } func (b *Brand) Pull(reader buff.Buffer) { b.Name = reader.PullTxt() } type DebugPaths struct { // unused? honestly why did I do this UnknownValue1 int32 UnknownValue2 float32 PathEntity PathEntity } type PathEntity struct { Index int Target PathPoint PSetLen int PSet []PathPoint OSetLen int OSet []PathPoint CSetLen int CSet []PathPoint } func (p *PathEntity) Push(writer buff.Buffer) { writer.PushI32(int32(p.Index)) p.Target.Push(writer) writer.PushI32(int32(p.PSetLen)) for _, point := range p.PSet { point.Push(writer) } writer.PushI32(int32(p.OSetLen)) for _, point := range p.OSet { point.Push(writer) } writer.PushI32(int32(p.CSetLen)) for _, point := range p.CSet { point.Push(writer) } } func (p *PathEntity) Pull(reader buff.Buffer) { p.Index = int(reader.PullI32()) target := PathPoint{} target.Pull(reader) p.Target = target p.PSet = make([]PathPoint, 0) p.PSetLen = int(reader.PullI32()) for i := 0; i < p.PSetLen; i++ { point := PathPoint{} point.Pull(reader) p.PSet = append(p.PSet, point) } p.OSet = make([]PathPoint, 0) p.OSetLen = int(reader.PullI32()) for i := 0; i < p.OSetLen; i++ { point := PathPoint{} point.Pull(reader) p.OSet = append(p.OSet, point) } p.CSet = make([]PathPoint, 0) p.CSetLen = int(reader.PullI32()) for i := 0; i < p.CSetLen; i++ { point := PathPoint{} point.Pull(reader) p.CSet = append(p.CSet, point) } } type PathPoint struct { X int32 Y int32 Z int32 DistanceOrigin float32 Cost float32 CostMalus float32 Visited bool NodeType NodeType DistanceTarget float32 } func (p *PathPoint) Push(writer buff.Buffer) { writer.PushI32(p.X) writer.PushI32(p.Y) writer.PushI32(p.Z) writer.PushF32(p.DistanceOrigin) writer.PushF32(p.Cost) writer.PushF32(p.CostMalus) writer.PushBit(p.Visited) writer.PushI32(int32(p.NodeType)) writer.PushF32(p.DistanceTarget) } func (p *PathPoint) Pull(reader buff.Buffer) { p.X = reader.PullI32() p.Y = reader.PullI32() p.Z = reader.PullI32() p.DistanceOrigin = reader.PullF32() p.Cost = reader.PullF32() p.CostMalus = reader.PullF32() p.Visited = reader.PullBit() p.NodeType = NodeType(reader.PullI32()) p.DistanceTarget = reader.PullF32() } type NodeType int const ( BLOCKED NodeType = iota OPEN WALKABLE TRAPDOOR FENCE LAVA WATER RAIL DANGER_FIRE DAMAGE_FIRE DANGER_CACTUS DAMAGE_CACTUS DANGER_OTHER DAMAGE_OTHER DOOR_OPEN DOOR_WOOD_CLOSED DOOR_IRON_CLOSED ) func (d *DebugPaths) Chan() string { return CHANNEL_DEBUG_PATHS } func (d *DebugPaths) Push(writer buff.Buffer) { writer.PushI32(d.UnknownValue1) writer.PushF32(d.UnknownValue2) d.PathEntity.Push(writer) } func (d *DebugPaths) Pull(reader buff.Buffer) { d.UnknownValue1 = reader.PullI32() d.UnknownValue2 = reader.PullF32() entity := PathEntity{} entity.Pull(reader) d.PathEntity = entity } type DebugNeighbors struct { Time int64 Location data.PositionI } func (d *DebugNeighbors) Chan() string { return CHANNEL_DEBUG_NEIGHBORS } func (d *DebugNeighbors) Push(writer buff.Buffer) { writer.PushVrL(d.Time) writer.PushPos(d.Location) } func (d *DebugNeighbors) Pull(reader buff.Buffer) { d.Time = reader.PullVrL() d.Location = reader.PullPos() } ================================================ FILE: impl/data/status/response.go ================================================ package status import ( "github.com/golangmc/minecraft-server/apis/data" "github.com/golangmc/minecraft-server/apis/data/chat" ) const ( SxtannaName = "Sxtanna" SxtannaUUID = "41d1fed5-aa44-432c-ab1b-2810001f3270" ServerMotd = " &bA GoLang Server" ServerIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAACXBIWXMAAAMTAAADEwE9ZoPHAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAEqNaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjYtYzA2NyA3OS4xNTc3NDcsIDIwMTUvMDMvMzAtMjM6NDA6NDIgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgICAgICAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgICAgICAgICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgICAgICAgICAgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiCiAgICAgICAgICAgIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIj4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoV2luZG93cyk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgICAgPHhtcDpDcmVhdGVEYXRlPjIwMTktMDctMjJUMjM6NTA6MTUtMDQ6MDA8L3htcDpDcmVhdGVEYXRlPgogICAgICAgICA8eG1wOk1ldGFkYXRhRGF0ZT4yMDE5LTA4LTEzVDE3OjQ2OjIzLTA0OjAwPC94bXA6TWV0YWRhdGFEYXRlPgogICAgICAgICA8eG1wOk1vZGlmeURhdGU+MjAxOS0wOC0xM1QxNzo0NjoyMy0wNDowMDwveG1wOk1vZGlmeURhdGU+CiAgICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2UvcG5nPC9kYzpmb3JtYXQ+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6NzNlNTdkYTYtMDFiOC1mNDQ1LTlmMTEtYjIxODc1NmJjZWZlPC94bXBNTTpJbnN0YW5jZUlEPgogICAgICAgICA8eG1wTU06RG9jdW1lbnRJRD5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NWJiYWE4Y2ItYmUxMy0xMWU5LTljNWUtY2IzZWQ2NWEzZDZlPC94bXBNTTpEb2N1bWVudElEPgogICAgICAgICA8eG1wTU06T3JpZ2luYWxEb2N1bWVudElEPnhtcC5kaWQ6ZDI0NTZmNTUtOTlmYi05OTRlLThhNDItODA5N2RjYjhhYmQ4PC94bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpIaXN0b3J5PgogICAgICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y3JlYXRlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOmQyNDU2ZjU1LTk5ZmItOTk0ZS04YTQyLTgwOTdkY2I4YWJkODwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxOS0wNy0yMlQyMzo1MDoxNS0wNDowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo5NzZlZmVlMC1kOTZmLWQ1NDYtOTIxMC0wNjA5NGFiN2RlNTE8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTktMDctMjJUMjM6NTA6NDYtMDQ6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6MmU5Yjg4ZjItNzJhZS0zMjQ5LTg5OGUtN2IxNDI0ZjY2ZTI4PC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE5LTA3LTI2VDE4OjEyOjUxLTA0OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5jb252ZXJ0ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnBhcmFtZXRlcnM+ZnJvbSBhcHBsaWNhdGlvbi92bmQuYWRvYmUucGhvdG9zaG9wIHRvIGltYWdlL2pwZWc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5kZXJpdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpwYXJhbWV0ZXJzPmNvbnZlcnRlZCBmcm9tIGFwcGxpY2F0aW9uL3ZuZC5hZG9iZS5waG90b3Nob3AgdG8gaW1hZ2UvanBlZzwvc3RFdnQ6cGFyYW1ldGVycz4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6M2Q5ZTkzMmYtZTRmNC05MDRlLWE2MTQtY2YwMjBkYzYzZGNmPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE5LTA3LTI2VDE4OjEyOjUxLTA0OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5zYXZlZDwvc3RFdnQ6YWN0aW9uPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6aW5zdGFuY2VJRD54bXAuaWlkOmQ2YzUzY2FhLWRhMzktNzI0Zi1iNGZmLTY4ZWRkOWRiYWZkNjwvc3RFdnQ6aW5zdGFuY2VJRD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OndoZW4+MjAxOS0wOC0xM1QxNzo0MzozMC0wNDowMDwvc3RFdnQ6d2hlbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OnNvZnR3YXJlQWdlbnQ+QWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpPC9zdEV2dDpzb2Z0d2FyZUFnZW50PgogICAgICAgICAgICAgICAgICA8c3RFdnQ6Y2hhbmdlZD4vPC9zdEV2dDpjaGFuZ2VkPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+Y29udmVydGVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpwYXJhbWV0ZXJzPmZyb20gaW1hZ2UvanBlZyB0byBpbWFnZS9wbmc8L3N0RXZ0OnBhcmFtZXRlcnM+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICAgICA8cmRmOmxpIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmFjdGlvbj5kZXJpdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpwYXJhbWV0ZXJzPmNvbnZlcnRlZCBmcm9tIGltYWdlL2pwZWcgdG8gaW1hZ2UvcG5nPC9zdEV2dDpwYXJhbWV0ZXJzPgogICAgICAgICAgICAgICA8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDphY3Rpb24+c2F2ZWQ8L3N0RXZ0OmFjdGlvbj4KICAgICAgICAgICAgICAgICAgPHN0RXZ0Omluc3RhbmNlSUQ+eG1wLmlpZDo0MmIzNjk2NS04ZjI5LWQ0NDEtYjJmOS05NTNiZTJhNWJhZTY8L3N0RXZ0Omluc3RhbmNlSUQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDp3aGVuPjIwMTktMDgtMTNUMTc6NDM6MzAtMDQ6MDA8L3N0RXZ0OndoZW4+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpzb2Z0d2FyZUFnZW50PkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE1IChXaW5kb3dzKTwvc3RFdnQ6c29mdHdhcmVBZ2VudD4KICAgICAgICAgICAgICAgICAgPHN0RXZ0OmNoYW5nZWQ+Lzwvc3RFdnQ6Y2hhbmdlZD4KICAgICAgICAgICAgICAgPC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0iUmVzb3VyY2UiPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6YWN0aW9uPnNhdmVkPC9zdEV2dDphY3Rpb24+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDppbnN0YW5jZUlEPnhtcC5paWQ6NzNlNTdkYTYtMDFiOC1mNDQ1LTlmMTEtYjIxODc1NmJjZWZlPC9zdEV2dDppbnN0YW5jZUlEPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6d2hlbj4yMDE5LTA4LTEzVDE3OjQ2OjIzLTA0OjAwPC9zdEV2dDp3aGVuPgogICAgICAgICAgICAgICAgICA8c3RFdnQ6c29mdHdhcmVBZ2VudD5BZG9iZSBQaG90b3Nob3AgQ0MgMjAxNSAoV2luZG93cyk8L3N0RXZ0OnNvZnR3YXJlQWdlbnQ+CiAgICAgICAgICAgICAgICAgIDxzdEV2dDpjaGFuZ2VkPi88L3N0RXZ0OmNoYW5nZWQ+CiAgICAgICAgICAgICAgIDwvcmRmOmxpPgogICAgICAgICAgICA8L3JkZjpTZXE+CiAgICAgICAgIDwveG1wTU06SGlzdG9yeT4KICAgICAgICAgPHhtcE1NOkRlcml2ZWRGcm9tIHJkZjpwYXJzZVR5cGU9IlJlc291cmNlIj4KICAgICAgICAgICAgPHN0UmVmOmluc3RhbmNlSUQ+eG1wLmlpZDpkNmM1M2NhYS1kYTM5LTcyNGYtYjRmZi02OGVkZDlkYmFmZDY8L3N0UmVmOmluc3RhbmNlSUQ+CiAgICAgICAgICAgIDxzdFJlZjpkb2N1bWVudElEPmFkb2JlOmRvY2lkOnBob3Rvc2hvcDo3ZDBjYmQ1Mi1hZmYyLTExZTktOGY4ZS05YzQ4OTgwOWNiOTU8L3N0UmVmOmRvY3VtZW50SUQ+CiAgICAgICAgICAgIDxzdFJlZjpvcmlnaW5hbERvY3VtZW50SUQ+eG1wLmRpZDpkMjQ1NmY1NS05OWZiLTk5NGUtOGE0Mi04MDk3ZGNiOGFiZDg8L3N0UmVmOm9yaWdpbmFsRG9jdW1lbnRJRD4KICAgICAgICAgPC94bXBNTTpEZXJpdmVkRnJvbT4KICAgICAgICAgPHBob3Rvc2hvcDpMZWdhY3lJUFRDRGlnZXN0PjI3QUUyMTVBODcyNUI0NDlFQ0Q4QzNGNEQxQkI2QkFDPC9waG90b3Nob3A6TGVnYWN5SVBUQ0RpZ2VzdD4KICAgICAgICAgPHBob3Rvc2hvcDpDb2xvck1vZGU+MzwvcGhvdG9zaG9wOkNvbG9yTW9kZT4KICAgICAgICAgPHBob3Rvc2hvcDpJQ0NQcm9maWxlPnNSR0IgSUVDNjE5NjYtMi4xPC9waG90b3Nob3A6SUNDUHJvZmlsZT4KICAgICAgICAgPHRpZmY6SW1hZ2VXaWR0aD4xMDAwPC90aWZmOkltYWdlV2lkdGg+CiAgICAgICAgIDx0aWZmOkltYWdlTGVuZ3RoPjEwMDA8L3RpZmY6SW1hZ2VMZW5ndGg+CiAgICAgICAgIDx0aWZmOkJpdHNQZXJTYW1wbGU+CiAgICAgICAgICAgIDxyZGY6U2VxPgogICAgICAgICAgICAgICA8cmRmOmxpPjg8L3JkZjpsaT4KICAgICAgICAgICAgICAgPHJkZjpsaT44PC9yZGY6bGk+CiAgICAgICAgICAgICAgIDxyZGY6bGk+ODwvcmRmOmxpPgogICAgICAgICAgICA8L3JkZjpTZXE+CiAgICAgICAgIDwvdGlmZjpCaXRzUGVyU2FtcGxlPgogICAgICAgICA8dGlmZjpQaG90b21ldHJpY0ludGVycHJldGF0aW9uPjI8L3RpZmY6UGhvdG9tZXRyaWNJbnRlcnByZXRhdGlvbj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPHRpZmY6U2FtcGxlc1BlclBpeGVsPjM8L3RpZmY6U2FtcGxlc1BlclBpeGVsPgogICAgICAgICA8dGlmZjpYUmVzb2x1dGlvbj4yMDAwMDAvMTAwMDA8L3RpZmY6WFJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOllSZXNvbHV0aW9uPjIwMDAwMC8xMDAwMDwvdGlmZjpZUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPGV4aWY6RXhpZlZlcnNpb24+MDIyMTwvZXhpZjpFeGlmVmVyc2lvbj4KICAgICAgICAgPGV4aWY6Q29sb3JTcGFjZT4xPC9leGlmOkNvbG9yU3BhY2U+CiAgICAgICAgIDxleGlmOlBpeGVsWERpbWVuc2lvbj42NDwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgICAgICAgIDxleGlmOlBpeGVsWURpbWVuc2lvbj42NDwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+NSu5mgAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAHiklEQVR42uxZS49cVxGuqnPOffS7ZzwziWN7DIkBkYcEYsOG30AkNlkRARILxGOBWPAX2ASxCBtASLADxBokxGPDgqdFFMdREnsmnmHePdPd93EeVSx6xunusdvd80Cy1LXse7t0vlP1VX1VF59fvQlPsxE85TYHMAcwBzAHMAcwBzAHMAcwB/AUm57+VUTFbH2xBcDjyIWBtElWABUIT/RCIMEXW8IOUI0+YwAyyTNA+glOzgZAuFAqbXzsq8o0hMuROKrUFVu9rd+LMI4fa8yLQ8DWjddM8iyHbBRaEuxed+t3whZRXzAARCzz/aj5maVb30IkDvnwU2XqvtzPD/5hs3vKtCY4cWUXo2cXP/51kywHdzR6C4kIl913soO/63jp4gFYV5InwhD8kbAbfsjiQQKqZHLoEcB5C4QIwr4jIxFA5gJ1jXRFJFwKB0jXbL7R2fqL0tWRCAiQqYdi02X3AIRDBsKoEqToGA+SsJVQACkQLvv3D7f/nFRXvTscdoIqhdCz2Zoy9RmYOeVMTETOc6+3QxTpqDUcAQFRyiRmcFQDIgDAIUdUgHTySkBKkNAHzrIuACiVyFC4BESpOKIcIZBuiPgLjgAzo/QardXlW99Wps6hGCYmAqDSRAmACICJVzoPfr3/wc90fAUAQ7nbvvl669qXXLmFgALAPhMJo6VMlK6y7+zc/aEvd0lXL4EDxWGSvthY/nzw2XAEEEAAQATwxGlyNW28jIOSipiXdqn6ibh2E1AhIh5f+Tg9ADXpFw+rvy37H1wKAB/E2j5RTHE8GoFTTqO2iBd2iMgi1joRjpI2+6OJWZoEDuyzJxTiM5NYR+2iv7b+1hs6bnPIEU1wR77cRRpxwiJxsmA7f/UuR9xm5uBg486bxeGdEdYe9xav40VlGsyedEqhY7M1FbUunsSIyAxZ0XXZ7iDkwmCSetp8SdgNZ4SIGI1JukBmASQAgvNcZNvsjpDMuFeKss5tV/SQQClIk8Qky4CX0IlFhAhrlbqkg+zEvHtv6YXXr730/TJbH8tmEWF4yAhAJAABkKHfji2qXN+684Ptu29EleuIKHIiTC4jhUQEj08DCGAdANWY8+C6p092mqaPdOldT5kWkkEEETiD6ZneHlSbATUDg803OZQAIMKAAMIgAoiTrwEQAQkEAFGCdfkGyOByzmJ4tuUuIvb63SAqqV47vjqUYA+E/UnzeqwkRFQqWgBABknjBP1uCPn06u1cERi2Slovbdd1bwMSggRv29dfrbReDq4z4V/KtPLD/+yv/YaURlS2VCZuEaUz6Z8LADDgdCVtQFoHwOD7ztMzt75Wbb/iyt2JtXih6N7x3X9KyElXmBkAznz6c0VAREQEAAjBOV84BAARDxNPI+yYRQBYGJjh3HZ2AIAkwXLoASrhwuX9nXu/qi1+1pf7k1IoarveWz7fRIoDewAmVUUVz1Q6L4DEDzUc6SYSBZa8yLw9RMAJVUhEtFJpHJOpAahBcwjuEESeQP3LiIDLN1s3Xlt64Ruu2MLj3hxEwiN7wlD5AgACVHgs+1Z23/vxwdovTHL1/woAEfLCtszVKF1m3wfEKcu4jEZEx4tRdXXQXyYjv0gAiCgizoOETEdNE7cFztJFEVCZJvts+vHlfACQgL0rNgEYgVBg892fZodvcyjGb5adjto6viLsB2OaL3e93R8TcwKQxHXX/ZcEdvmGSEDUs65VZiGxeFJRZfELZBrAlgWLfM8XO6c0JpCu9ff/lnXWlAEAYA9p80Z14XPse6O5JEZhXFkh3RRxRIm3+/2dPwnI9CPB1AMNgC0PosYry5/8LpCRkCPi4zI/Sq/t3//lg9vfM/EyIOa9D5/79HdWnv9Kma2dqkv8UMORSoXtev/9vPNvHV+5aABE1hYYNIAL5QFMzlqMgAxSDCDCXDoRIVfu+HJv0tjtj0g3lWnORImpOSCCKnH5f/Ojd5WujS3VQARQIUWD1EDVLXvvCVsAEmAOIe+9H9yRd4MUQmELEsY6BqoK5xs2W1dTD8SzrVWs5153W+uKjtosbqyeMFukCCkS5iRONPQDWyIjAv2sGxhNsjiY8YGdsEUyI7VLRKnYUB/Fkm5Mr45mWKsoyRvNa+3VL2tTGxvqlanb7MPtuz9iLkglXCLrGqpEhBGxktad6wW7BqBQSsFo5dY34+r14LqjTiriu/v3fs6ugyq9hN1ocZC0P7W0+iqHYnS1CKRTDrbY+0PRfVtHi8IsJ5PhQLcmSR2khohlsYfx6uKNLypdYZ+NBBE1UNrb+WNv5765DAAuBHI5klaYcqCx7XQILgQrAnxKY36kWwGc9wI5EildGdOtqGIRFHY4iy6agcQ6ahX99Qd33tSmObwbFQClq+B3g90jXX/CJwZdy8vdzXd+EleeC6NtgVRKfOSy+xP22+cgMaJnyLIjV+yNaRYR0AoraarjpckfOE52M/0y38ZTo7IiqKSJiZeQoulJPEMnHkggedT5TjoaTtMQZbAEOIeTc6xVEB/Z5GXqlYgAPM7JDF7OvlY52+7mEpx8lNvwlNscwBzAHMAcwBzAHMAcwBzAOex/AwBoET5bwRr9gwAAAABJRU5ErkJggg==" ) type Response struct { Version Version `json:"version,string"` Players Players `json:"players,string"` Description Message `json:"description"` Favicon string `json:"favicon"` } type Version struct { Name string `json:"name"` Protocol int `json:"protocol"` } type Players struct { Max int `json:"max"` Online int `json:"online"` Sample []SamplePlayer `json:"sample"` } type SamplePlayer struct { Name string `json:"name"` ID string `json:"id"` } type Message struct { Text string `json:"text"` } func DefaultResponse() Response { return Response{ Version: Version{ Name: "GoLang Server", Protocol: data.CurrentProtocol.Protocol(), }, Players: Players{ Max: 10, Online: 1, Sample: []SamplePlayer{ { Name: SxtannaName, ID: SxtannaUUID, }, }, }, Description: Message{ Text: chat.Translate(ServerMotd), }, Favicon: ServerIcon, } } ================================================ FILE: impl/data/system/command.go ================================================ package system type Command int type Message struct { Command Message interface{} } const ( // stops the server entirely STOP Command = iota FAIL ) func Make(command Command, message interface{}) Message { return struct { Command Message interface{} }{Command: command, Message: message} } ================================================ FILE: impl/data/values/constants.go ================================================ package values import ( "encoding/binary" "github.com/golangmc/minecraft-server/apis/base" ) const ( // ticks per second TPS = 20 // milliseconds per tick MPT = 1_000 / TPS ) var DefaultWorldHashedSeed = int64(binary.LittleEndian.Uint64(base.JavaSHA256HashLong(int64(base.JavaStringHashCode("North Carolina"))))) ================================================ FILE: impl/game/auth/authenticate.go ================================================ package auth import ( "crypto/sha1" "encoding/json" "fmt" "strings" "github.com/golangmc/minecraft-server/apis/urls" ) const url = "https://sessionserver.mojang.com/session/minecraft/hasJoined" type Auth struct { UUID string `json:"id"` Name string `json:"name"` Prop []Prop `json:"properties"` } type Prop struct { Name string `json:"name"` Data string `json:"value"` Sign *string `json:"signature"` } func RunAuthGet(secret []byte, name string, callback func(auth *Auth, err error)) { go execute(generateAuthURL(name, generateAuthSHA(secret)), callback) } func execute(url string, callback func(auth *Auth, err error)) { get, err := urls.GetByte(url) if err != nil { callback(nil, err) return } var auth Auth err = json.Unmarshal(get, &auth) if err != nil { callback(nil, err) } else { callback(&auth, nil) } } func generateAuthURL(name, hash string) string { return fmt.Sprintf("%s?username=%s&serverId=%s", url, name, hash) } func generateAuthSHA(secret []byte) string { sha := sha1.New() // update with encoded secret, and encoded public _, public := NewCrypt() sha.Write(secret) sha.Write(public) hash := sha.Sum(nil) // Check for negative hashes negative := (hash[0] & 0x80) == 0x80 if negative { carry := true for i := len(hash) - 1; i >= 0; i-- { hash[i] = ^hash[i] if carry { carry = hash[i] == 0xff hash[i]++ } } } // Trim away zeroes res := strings.TrimLeft(fmt.Sprintf("%x", hash), "0") if negative { res = "-" + res } return res } ================================================ FILE: impl/game/auth/cryptography.go ================================================ package auth import ( "crypto/rand" "crypto/rsa" "crypto/x509" ) var secretKey *rsa.PrivateKey var publicKey *rsa.PublicKey var secretArr []byte var publicArr []byte func NewCrypt() (secret []byte, public []byte) { // initialize keys if secretKey == nil || publicKey == nil { secretKey, publicKey = generate() x509Secret := x509.MarshalPKCS1PrivateKey(secretKey) x509Public, _ := x509.MarshalPKIXPublicKey(publicKey) secretArr = x509Secret publicArr = x509Public } return secretArr, publicArr } func Encrypt(data []byte) ([]byte, error) { return rsa.EncryptPKCS1v15(rand.Reader, publicKey, data) } func Decrypt(data []byte) ([]byte, error) { return rsa.DecryptPKCS1v15(rand.Reader, secretKey, data) } func generate() (secretKey *rsa.PrivateKey, publicKey *rsa.PublicKey) { key, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { panic(err) } secretKey = key publicKey = &key.PublicKey secretKey.Precompute() if err := secretKey.Validate(); err != nil { panic(err) } return } ================================================ FILE: impl/game/ents/entity.go ================================================ package ents import "github.com/golangmc/minecraft-server/apis/uuid" var entityCounter = int64(0) type entity struct { name string uuid uuid.UUID entityID int64 } func newEntity() entity { id := entityCounter entityCounter++ return entity{entityID: id} } func (e *entity) Name() string { return e.name } func (e *entity) UUID() uuid.UUID { return e.uuid } func (e *entity) SetName(name string) { e.name = name } func (e *entity) SetUUID(uuid uuid.UUID) { e.uuid = uuid } func (e *entity) SendMessage(message ...interface{}) { // nothing } func (e *entity) EntityUUID() int64 { return e.entityID } ================================================ FILE: impl/game/ents/living.go ================================================ package ents type entityLiving struct { entity health float64 } func newEntityLiving() entityLiving { return entityLiving{entity: newEntity()} } func (e *entityLiving) GetHealth() float64 { return e.health } func (e *entityLiving) SetHealth(health float64) { e.health = health } ================================================ FILE: impl/game/ents/player.go ================================================ package ents import ( "github.com/golangmc/minecraft-server/apis/data/msgs" "github.com/golangmc/minecraft-server/apis/ents" "github.com/golangmc/minecraft-server/apis/game" "github.com/golangmc/minecraft-server/impl/prot/client" apis_base "github.com/golangmc/minecraft-server/apis/base" impl_base "github.com/golangmc/minecraft-server/impl/base" ) type player struct { entityLiving prof *game.Profile online bool conn impl_base.Connection mode game.GameMode } func NewPlayer(prof *game.Profile, conn impl_base.Connection) ents.Player { player := &player{ prof: prof, entityLiving: newEntityLiving(), } player.SetName(prof.Name) player.SetUUID(prof.UUID) player.SetConn(conn) return player } func (p *player) SendMessage(message ...interface{}) { packet := client.PacketOChatMessage{ Message: *msgs.New(apis_base.ConvertToString(message...)), MessagePosition: msgs.NormalChat, } p.conn.SendPacket(&packet) } func (p *player) GetGameMode() game.GameMode { return p.mode } func (p *player) SetGameMode(mode game.GameMode) { p.mode = mode } func (p *player) GetIsOnline() bool { return p.online } func (p *player) SetIsOnline(state bool) { p.online = state } func (p *player) GetProfile() *game.Profile { return p.prof } func (p *player) SetConn(conn impl_base.Connection) { p.conn = conn } ================================================ FILE: impl/game/event/events.go ================================================ package event import ( "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/data/plugin" ) type PlayerConnJoinEvent struct { Conn base.PlayerAndConnection } type PlayerConnQuitEvent struct { Conn base.PlayerAndConnection } type PlayerPluginMessagePullEvent struct { Conn base.PlayerAndConnection Channel string Message plugin.Message } ================================================ FILE: impl/game/level/block.go ================================================ package level import ( apis_level "github.com/golangmc/minecraft-server/apis/game/level" ) type block struct { // these should always be level coordinates x int y int z int slice *slice } func (b *block) X() int { return b.x } func (b *block) Y() int { return b.y } func (b *block) Z() int { return b.z } func (b *block) Chunk() apis_level.Chunk { return b.slice.chunk } func (b *block) Level() apis_level.Level { return b.slice.chunk.level } func (b *block) GetBlockType() (value int) { value = b.slice.sliceBlockGet(sliceIndex(blockLevelToSlice(b.x, b.y, b.z))) return } func (b *block) SetBlockType(value int) { value = b.slice.sliceBlockSet(sliceIndex(blockLevelToSlice(b.x, b.y, b.z)), value) return } ================================================ FILE: impl/game/level/chunk.go ================================================ package level import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/data/tags" apis_level "github.com/golangmc/minecraft-server/apis/game/level" "github.com/golangmc/minecraft-server/impl/base" ) type chunk struct { x int z int level *level slices []*slice heightMap map[heightMapType]*heightMap } func newChunk(level *level, x, z int) *chunk { chunk := &chunk{ x: x, z: z, level: level, slices: make([]*slice, apis_level.SliceC, apis_level.SliceC), heightMap: make(map[heightMapType]*heightMap), } for _, mapType := range heightMapTypes { chunk.heightMap[mapType] = &heightMap{ chunk: chunk, heightMapType: mapType, heightMapData: base.NewCompacter(9, 256), } } return chunk } func (c *chunk) ChunkX() int { return c.x } func (c *chunk) ChunkZ() int { return c.z } func (c *chunk) Level() apis_level.Level { return c.level } func (c *chunk) Slices() []apis_level.Slice { slices := make([]apis_level.Slice, apis_level.SliceC, apis_level.SliceC) for index, slice := range c.slices { slices[index] = slice } return slices } func (c *chunk) GetSlice(y int) apis_level.Slice { if y < 0 || y > 15 { panic("index out of range [0:15]") } slc := c.slices[y] if slc != nil { return slc } gen := newSlice(c, y) c.slices[y] = gen return gen } func (c *chunk) GetBlock(x, y, z int) apis_level.Block { if x < 0 || x > 15 { panic("invalid x value for chunk get block") } if y < 0 || y > 255 { panic("invalid y value for chunk get block") } if z < 0 || z > 15 { panic("invalid z value for chunk get block") } return &block{ x: (c.x << 0x04) | x, y: y, z: (c.z << 0x04) | z, slice: c.GetSlice(y >> 0x04).(*slice), } } func (c *chunk) Push(writer buff.Buffer) { mask := int32(0) for i := 0; i < apis_level.SliceC; i++ { if len(c.slices) < i { break } mask |= 1 << i slice := c.slices[i] slice.Push(writer) } writer.PushVrI(mask) } func (c *chunk) HeightMapNbtCompound() *tags.NbtCompound { compound := tags.NbtCompound{Value: make(map[string]tags.Nbt)} motionBlocking := c.heightMap[MotionBlocking] compound.Set(string(motionBlocking.heightMapType), &tags.NbtArrI64{Value: motionBlocking.heightMapData.Values}) return &compound } type heightMapType string const ( WorldSurfaceWg heightMapType = "WORLD_SURFACE_WG" WorldSurface heightMapType = "WORLD_SURFACE" OceanFloorWg heightMapType = "OCEAN_FLOOR_WG" OceanFloor heightMapType = "OCEAN_FLOOR" MotionBlocking heightMapType = "MOTION_BLOCKING" MotionBlockingNoLeaves heightMapType = "MOTION_BLOCKING_NO_LEAVES" ) var heightMapTypes = []heightMapType{ WorldSurfaceWg, WorldSurface, OceanFloorWg, OceanFloor, MotionBlocking, MotionBlockingNoLeaves, } type heightMap struct { chunk *chunk check func(b *block) bool heightMapType heightMapType heightMapData *base.Compacter } ================================================ FILE: impl/game/level/level.go ================================================ package level import ( apis_level "github.com/golangmc/minecraft-server/apis/game/level" "github.com/golangmc/minecraft-server/apis/uuid" ) type level struct { name string uuid uuid.UUID chunks map[int64]*chunk } func NewLevel(name string) apis_level.Level { level := &level{ name: name, uuid: uuid.NewUUID(), chunks: make(map[int64]*chunk), } return level } func (l *level) Name() string { return l.name } func (l *level) UUID() uuid.UUID { return l.uuid } func (l *level) Chunks() []apis_level.Chunk { chunks := make([]apis_level.Chunk, len(l.chunks), len(l.chunks)) index := 0 for _, chunk := range l.chunks { chunks[index] = chunk index++ } return chunks } func (l *level) GetChunk(x, z int) apis_level.Chunk { return l.getChunk(x, z, true) } func (l *level) GetChunkIfLoaded(x, z int) apis_level.Chunk { return l.getChunk(x, z, false) } func (l *level) GetBlock(x, y, z int) apis_level.Block { return &block{ x: x, y: y, z: z, slice: l.GetChunk(blockXZToChunkXZ(x, z)).GetSlice(blockYToSliceY(y)).(*slice), } } func (l *level) getChunk(x, z int, generate bool) apis_level.Chunk { idx := chunkIndex(x, z) cnk, con := l.chunks[idx] if con { return cnk } if generate { gen := newChunk(l, x, z) l.chunks[idx] = gen return gen } return nil } // generates chunks with the normal super-flat style func GenSuperFlat(level apis_level.Level, size int) { id := 210 for x := -size; x < size; x++ { for z := -size; z < size; z++ { chunk := level.GetChunk(x, z) for sliceY := 0; sliceY < apis_level.SliceC; sliceY++ { chunk.GetSlice(sliceY) } chunk.GetSlice(0).(*slice).fill(id) chunk.GetSlice(1).(*slice).fill(id) chunk.GetSlice(2).(*slice).fill(id) id++ } } for _, c := range level.Chunks() { _ = c.(*chunk).heightMap[MotionBlocking] for x := 0; x < apis_level.ChunkW; x++ { for z := 0; z < apis_level.ChunkL; z++ { // height.heightMapData.Set(x + z * 16, 16 * 3) } } } } ================================================ FILE: impl/game/level/slice.go ================================================ package level import ( "github.com/golangmc/minecraft-server/apis/buff" apis_level "github.com/golangmc/minecraft-server/apis/game/level" "github.com/golangmc/minecraft-server/impl/base" ) type slice struct { index int chunk *chunk values *base.Compacter } func newSlice(chunk *chunk, index int) *slice { slice := &slice{ index: index, chunk: chunk, values: base.NewCompacter(apis_level.BitsPerBlock, apis_level.SliceS), } return slice } func (s *slice) Index() int { return s.index } func (s *slice) Chunk() apis_level.Chunk { return s.chunk } func (s *slice) Level() apis_level.Level { return s.chunk.level } func (s *slice) GetBlock(x, y, z int) apis_level.Block { if x < 0 || x > 15 { panic("invalid x value for slice get block") } if y < 0 || y > 15 { panic("invalid y value for slice get block") } if z < 0 || z > 15 { panic("invalid z value for slice get block") } return &block{ x: (s.chunk.x << 0x04) | x, y: (apis_level.SliceH * s.index) + y, z: (s.chunk.z << 0x04) | z, slice: s, } } func (s *slice) Push(writer buff.Buffer) { writer.PushI16(apis_level.SliceS) // full slice writer.PushByt(apis_level.BitsPerBlock) // the server is using the direct palette writer.PushVrI(int32(len(s.values.Values))) for _, value := range s.values.Values { writer.PushI64(value) } } func (s *slice) sliceBlockGet(index int) int { return s.values.Get(index) } func (s *slice) sliceBlockSet(index int, value int) int { return s.values.Set(index, value) } func (s *slice) fill(value int) { for y := 0; y < apis_level.SliceH; y++ { s.layer(y, value) } } func (s *slice) layer(index int, value int) { for x := 0; x < apis_level.ChunkW; x++ { for z := 0; z < apis_level.ChunkL; z++ { s.sliceBlockSet(sliceIndex(x, index, z), value) } } } ================================================ FILE: impl/game/level/value.go ================================================ package level func chunkIndex(x, z int) int64 { return (int64(z) << 0x20) | (int64(x) & 0xFFFFFFFF) } func sliceIndex(x, y, z int) int { return y<<0x08 | z<<0x04 | x } func blockYToSliceY(blockY int) (sliceY int) { sliceY = blockY >> 0x04 return } func blockXZToChunkXZ(blockX, blockZ int) (chunkX, chunkZ int) { chunkX = blockX >> 0x04 chunkZ = blockZ >> 0x04 return } func blockLevelToSlice(levelBlockX, levelBlockY, levelBlockZ int) (sliceBlockX, sliceBlockY, sliceBlockZ int) { sliceBlockX = levelBlockX & 0xF sliceBlockY = levelBlockY & 0xF sliceBlockZ = levelBlockZ & 0xF return } ================================================ FILE: impl/game/mode/mode_state0.go ================================================ package mode import ( "github.com/golangmc/minecraft-server/apis/util" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/prot/server" ) /** * handshake */ func HandleState0(watcher util.Watcher) { watcher.SubAs(func(packet *server.PacketIHandshake, conn base.Connection) { conn.SetState(packet.State) }) } ================================================ FILE: impl/game/mode/mode_state1.go ================================================ package mode import ( "github.com/golangmc/minecraft-server/apis/util" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/data/status" "github.com/golangmc/minecraft-server/impl/prot/client" "github.com/golangmc/minecraft-server/impl/prot/server" ) /** * status */ func HandleState1(watcher util.Watcher) { watcher.SubAs(func(packet *server.PacketIRequest, conn base.Connection) { response := client.PacketOResponse{Status: status.DefaultResponse()} conn.SendPacket(&response) }) watcher.SubAs(func(packet *server.PacketIPing, conn base.Connection) { response := client.PacketOPong{Ping: packet.Ping} conn.SendPacket(&response) }) } ================================================ FILE: impl/game/mode/mode_state2.go ================================================ package mode import ( "bytes" "fmt" "github.com/golangmc/minecraft-server/apis/data/chat" "github.com/golangmc/minecraft-server/apis/data/msgs" "github.com/golangmc/minecraft-server/apis/game" "github.com/golangmc/minecraft-server/apis/util" "github.com/golangmc/minecraft-server/apis/uuid" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/game/auth" "github.com/golangmc/minecraft-server/impl/game/ents" "github.com/golangmc/minecraft-server/impl/prot/client" "github.com/golangmc/minecraft-server/impl/prot/server" ) /** * login */ func HandleState2(watcher util.Watcher, join chan base.PlayerAndConnection) { watcher.SubAs(func(packet *server.PacketILoginStart, conn base.Connection) { conn.CertifyValues(packet.PlayerName) _, public := auth.NewCrypt() response := client.PacketOEncryptionRequest{ Server: "", Public: public, Verify: conn.CertifyData(), } conn.SendPacket(&response) }) watcher.SubAs(func(packet *server.PacketIEncryptionResponse, conn base.Connection) { defer func() { if err := recover(); err != nil { conn.SendPacket(&client.PacketODisconnect{ Reason: *msgs.New(fmt.Sprintf("Authentication failed: %v", err)).SetColor(chat.Red), }) } }() ver, err := auth.Decrypt(packet.Verify) if err != nil { panic(fmt.Errorf("failed to decrypt token: %s\n%v\n", conn.CertifyName(), err)) } if !bytes.Equal(ver, conn.CertifyData()) { panic(fmt.Errorf("encryption failed, tokens are different: %s\n%v | %v", conn.CertifyName(), ver, conn.CertifyData())) } sec, err := auth.Decrypt(packet.Secret) if err != nil { panic(fmt.Errorf("failed to decrypt secret: %s\n%v\n", conn.CertifyName(), err)) } conn.CertifyUpdate(sec) // enable encryption on the connection auth.RunAuthGet(sec, conn.CertifyName(), func(auth *auth.Auth, err error) { defer func() { if err := recover(); err != nil { conn.SendPacket(&client.PacketODisconnect{ Reason: *msgs.New(fmt.Sprintf("Authentication failed: %v", err)).SetColor(chat.Red), }) } }() if err != nil { panic(fmt.Errorf("failed to authenticate: %s\n%v\n", conn.CertifyName(), err)) } uuid, err := uuid.TextToUUID(auth.UUID) if err != nil { panic(fmt.Errorf("failed to decode uuid for %s: %s\n%v\n", conn.CertifyName(), auth.UUID, err)) } prof := game.Profile{ UUID: uuid, Name: auth.Name, } for _, prop := range auth.Prop { prof.Properties = append(prof.Properties, &game.ProfileProperty{ Name: prop.Name, Value: prop.Data, Signature: prop.Sign, }) } player := ents.NewPlayer(&prof, conn) conn.SendPacket(&client.PacketOLoginSuccess{ PlayerName: player.Name(), PlayerUUID: player.UUID().String(), }) conn.SetState(base.PLAY) join <- base.PlayerAndConnection{ Player: player, Connection: conn, } }) }) } ================================================ FILE: impl/game/mode/mode_state3.go ================================================ package mode import ( "fmt" "time" "github.com/golangmc/minecraft-server/apis" "github.com/golangmc/minecraft-server/apis/data" "github.com/golangmc/minecraft-server/apis/data/chat" "github.com/golangmc/minecraft-server/apis/data/msgs" "github.com/golangmc/minecraft-server/apis/game" "github.com/golangmc/minecraft-server/apis/logs" "github.com/golangmc/minecraft-server/apis/task" "github.com/golangmc/minecraft-server/apis/util" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/data/client" "github.com/golangmc/minecraft-server/impl/data/plugin" "github.com/golangmc/minecraft-server/impl/data/values" impl_level "github.com/golangmc/minecraft-server/impl/game/level" impl_event "github.com/golangmc/minecraft-server/impl/game/event" client_packet "github.com/golangmc/minecraft-server/impl/prot/client" server_packet "github.com/golangmc/minecraft-server/impl/prot/server" ) func HandleState3(watcher util.Watcher, logger *logs.Logging, tasking *task.Tasking, join chan base.PlayerAndConnection, quit chan base.PlayerAndConnection) { tasking.EveryTime(10, time.Second, func(task *task.Task) { api := apis.MinecraftServer() // I hate this, add a functional method for player iterating for _, player := range api.Players() { // also probably add one that returns both the player and their connection conn := api.ConnByUUID(player.UUID()) // keep player connection alive via keep alive conn.SendPacket(&client_packet.PacketOKeepAlive{KeepAliveID: time.Now().UnixNano() / 1e6}) } }) watcher.SubAs(func(packet *server_packet.PacketIKeepAlive, conn base.Connection) { logger.DataF("player %s is being kept alive", conn.Address()) }) watcher.SubAs(func(packet *server_packet.PacketIPluginMessage, conn base.Connection) { api := apis.MinecraftServer() player := api.PlayerByConn(conn) if player == nil { return // log no player found? } api.Watcher().PubAs(impl_event.PlayerPluginMessagePullEvent{ Conn: base.PlayerAndConnection{ Connection: conn, Player: player, }, Channel: packet.Message.Chan(), Message: packet.Message, }) }) watcher.SubAs(func(packet *server_packet.PacketIChatMessage, conn base.Connection) { api := apis.MinecraftServer() who := api.PlayerByConn(conn) out := msgs. New(who.Name()).SetColor(chat.White). Add(":").SetColor(chat.Gray). Add(" "). Add(chat.Translate(packet.Message)).SetColor(chat.White). AsText() // why not just use translate? api.Broadcast(out) }) go func() { for conn := range join { apis.MinecraftServer().Watcher().PubAs(impl_event.PlayerConnJoinEvent{Conn: conn}) conn.SendPacket(&client_packet.PacketOJoinGame{ EntityID: int32(conn.EntityUUID()), Hardcore: false, GameMode: game.CREATIVE, Dimension: game.OVERWORLD, HashedSeed: values.DefaultWorldHashedSeed, MaxPlayers: 10, LevelType: game.DEFAULT, ViewDistance: 12, ReduceDebug: false, RespawnScreen: false, }) conn.SendPacket(&client_packet.PacketOPluginMessage{ Message: &plugin.Brand{ Name: chat.Translate(fmt.Sprintf("&b%s&r &a%s&r", "GoLangMc", apis.MinecraftServer().ServerVersion())), }, }) conn.SendPacket(&client_packet.PacketOServerDifficulty{ Difficulty: game.PEACEFUL, Locked: true, }) conn.SendPacket(&client_packet.PacketOPlayerAbilities{ Abilities: client.PlayerAbilities{ Invulnerable: true, Flying: true, AllowFlight: true, InstantBuild: false, }, FlyingSpeed: 0.05, // default value FieldOfView: 0.1, // default value }) conn.SendPacket(&client_packet.PacketOHeldItemChange{ Slot: client.SLOT_0, }) conn.SendPacket(&client_packet.PacketODeclareRecipes{}) conn.SendPacket(&client_packet.PacketOPlayerLocation{ ID: 0, Location: data.Location{ PositionF: data.PositionF{ X: 0, Y: 10, Z: 0, }, RotationF: data.RotationF{ AxisX: 0, AxisY: 0, }, }, Relative: client.Relativity{}, }) conn.SendPacket(&client_packet.PacketOPlayerInfo{ Action: client.AddPlayer, Values: []client.PlayerInfo{ &client.PlayerInfoAddPlayer{Player: conn.Player}, }, }) conn.SendPacket(&client_packet.PacketOEntityMetadata{Entity: conn.Player}) level := impl_level.NewLevel("test") impl_level.GenSuperFlat(level, 6) for _, chunk := range level.Chunks() { conn.SendPacket(&client_packet.PacketOChunkData{Chunk: chunk}) } logger.DataF("chunks sent to player: %s", conn.Player.Name()) conn.SendPacket(&client_packet.PacketOPlayerLocation{ ID: 1, Location: data.Location{ PositionF: data.PositionF{ X: 0, Y: 10, Z: 0, }, RotationF: data.RotationF{ AxisX: 0, AxisY: 0, }, }, Relative: client.Relativity{}, }) } }() go func() { for conn := range quit { apis.MinecraftServer().Watcher().PubAs(impl_event.PlayerConnQuitEvent{Conn: conn}) } }() } ================================================ FILE: impl/mask/masking.go ================================================ package mask type Masking struct{} func (m *Masking) Has(mask, field byte) bool { return mask&field != 0 } func (m *Masking) Set(mask *byte, field byte, when bool) { if when { *mask |= field } } ================================================ FILE: impl/mask/masking_test.go ================================================ package mask import ( "fmt" "testing" ) type Data struct { Masking Head bool Body bool } func TestMasking(t *testing.T) { mask := byte(0) data := Data{ Head: true, } data.Set(&mask, 0x01, data.Head) data.Set(&mask, 0x02, data.Body) fmt.Println(data.Has(mask, 0x01)) fmt.Println(data.Has(mask, 0x02)) } ================================================ FILE: impl/prot/client/to_client_state0.go ================================================ package client // done ================================================ FILE: impl/prot/client/to_client_state1.go ================================================ package client import ( "encoding/json" "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/data/status" ) // done type PacketOResponse struct { Status status.Response } func (p *PacketOResponse) UUID() int32 { return 0x00 } func (p *PacketOResponse) Push(writer buff.Buffer, conn base.Connection) { if text, err := json.Marshal(p.Status); err != nil { panic(err) } else { writer.PushTxt(string(text)) } } type PacketOPong struct { Ping int64 } func (p *PacketOPong) UUID() int32 { return 0x01 } func (p *PacketOPong) Push(writer buff.Buffer, conn base.Connection) { writer.PushI64(p.Ping) } ================================================ FILE: impl/prot/client/to_client_state2.go ================================================ package client import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/data/msgs" "github.com/golangmc/minecraft-server/impl/base" ) // done type PacketODisconnect struct { Reason msgs.Message } func (p *PacketODisconnect) UUID() int32 { return 0x00 } func (p *PacketODisconnect) Push(writer buff.Buffer, conn base.Connection) { message := p.Reason writer.PushTxt(message.AsJson()) } type PacketOEncryptionRequest struct { Server string // unused? Public []byte Verify []byte } func (p *PacketOEncryptionRequest) UUID() int32 { return 0x01 } func (p *PacketOEncryptionRequest) Push(writer buff.Buffer, conn base.Connection) { writer.PushTxt(p.Server) writer.PushUAS(p.Public, true) writer.PushUAS(p.Verify, true) } type PacketOLoginSuccess struct { PlayerUUID string PlayerName string } func (p *PacketOLoginSuccess) UUID() int32 { return 0x02 } func (p *PacketOLoginSuccess) Push(writer buff.Buffer, conn base.Connection) { writer.PushTxt(p.PlayerUUID) writer.PushTxt(p.PlayerName) } type PacketOSetCompression struct { Threshold int32 } func (p *PacketOSetCompression) UUID() int32 { return 0x03 } func (p *PacketOSetCompression) Push(writer buff.Buffer, conn base.Connection) { writer.PushVrI(p.Threshold) } type PacketOLoginPluginRequest struct { MessageID int32 Channel string OptData []byte } func (p *PacketOLoginPluginRequest) UUID() int32 { return 0x04 } func (p *PacketOLoginPluginRequest) Push(writer buff.Buffer, conn base.Connection) { writer.PushVrI(p.MessageID) writer.PushTxt(p.Channel) writer.PushUAS(p.OptData, false) } ================================================ FILE: impl/prot/client/to_client_state3.go ================================================ package client import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/data" "github.com/golangmc/minecraft-server/apis/data/msgs" "github.com/golangmc/minecraft-server/apis/ents" "github.com/golangmc/minecraft-server/apis/game" "github.com/golangmc/minecraft-server/apis/game/level" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/data/client" "github.com/golangmc/minecraft-server/impl/data/plugin" apis_conn "github.com/golangmc/minecraft-server/impl/conn" ) type PacketOChatMessage struct { Message msgs.Message MessagePosition msgs.MessagePosition } func (p *PacketOChatMessage) UUID() int32 { return 0x0F } func (p *PacketOChatMessage) Push(writer buff.Buffer, conn base.Connection) { message := p.Message if p.MessagePosition == msgs.HotBarText { message = *msgs.New(message.AsText()) } writer.PushTxt(message.AsJson()) writer.PushByt(byte(p.MessagePosition)) } type PacketOJoinGame struct { EntityID int32 Hardcore bool GameMode game.GameMode Dimension game.Dimension HashedSeed int64 MaxPlayers int LevelType game.LevelType ViewDistance int32 ReduceDebug bool RespawnScreen bool } func (p *PacketOJoinGame) UUID() int32 { return 0x26 } func (p *PacketOJoinGame) Push(writer buff.Buffer, conn base.Connection) { writer.PushI32(p.EntityID) writer.PushByt(p.GameMode.Encoded(p.Hardcore /* pull this value from somewhere */)) writer.PushI32(int32(p.Dimension)) writer.PushI64(p.HashedSeed) writer.PushByt(uint8(p.MaxPlayers)) writer.PushTxt(p.LevelType.String()) writer.PushVrI(p.ViewDistance) writer.PushBit(p.ReduceDebug) writer.PushBit(p.RespawnScreen) } type PacketOPluginMessage struct { Message plugin.Message } func (p *PacketOPluginMessage) UUID() int32 { return 0x19 } func (p *PacketOPluginMessage) Push(writer buff.Buffer, conn base.Connection) { writer.PushTxt(p.Message.Chan()) p.Message.Push(writer) } type PacketOPlayerLocation struct { Location data.Location Relative client.Relativity ID int32 } func (p *PacketOPlayerLocation) UUID() int32 { return 0x36 } func (p *PacketOPlayerLocation) Push(writer buff.Buffer, conn base.Connection) { writer.PushF64(p.Location.X) writer.PushF64(p.Location.Y) writer.PushF64(p.Location.Z) writer.PushF32(p.Location.AxisX) writer.PushF32(p.Location.AxisY) p.Relative.Push(writer) writer.PushVrI(p.ID) } type PacketOKeepAlive struct { KeepAliveID int64 } func (p *PacketOKeepAlive) UUID() int32 { return 0x21 } func (p *PacketOKeepAlive) Push(writer buff.Buffer, conn base.Connection) { writer.PushI64(p.KeepAliveID) } type PacketOServerDifficulty struct { Difficulty game.Difficulty Locked bool // should probably always be true } func (p *PacketOServerDifficulty) UUID() int32 { return 0x0E } func (p *PacketOServerDifficulty) Push(writer buff.Buffer, conn base.Connection) { writer.PushByt(byte(p.Difficulty)) writer.PushBit(p.Locked) } type PacketOPlayerAbilities struct { Abilities client.PlayerAbilities FlyingSpeed float32 FieldOfView float32 } func (p *PacketOPlayerAbilities) UUID() int32 { return 0x32 } func (p *PacketOPlayerAbilities) Push(writer buff.Buffer, conn base.Connection) { p.Abilities.Push(writer) writer.PushF32(p.FlyingSpeed) writer.PushF32(p.FieldOfView) } type PacketOHeldItemChange struct { Slot client.HotBarSlot } func (p *PacketOHeldItemChange) UUID() int32 { return 0x40 } func (p *PacketOHeldItemChange) Push(writer buff.Buffer, conn base.Connection) { writer.PushByt(byte(p.Slot)) } type PacketODeclareRecipes struct { // Recipes []*Recipe // this doesn't exist yet ;( RecipeCount int32 } func (p *PacketODeclareRecipes) UUID() int32 { return 0x5B } func (p *PacketODeclareRecipes) Push(writer buff.Buffer, conn base.Connection) { writer.PushVrI(p.RecipeCount) // when recipes are implemented, instead of holding a recipe count, simply write the size of the slice, Recipe will implement BufferPush } type PacketOChunkData struct { Chunk level.Chunk } func (p *PacketOChunkData) UUID() int32 { return 0x22 } func (p *PacketOChunkData) Push(writer buff.Buffer, conn base.Connection) { writer.PushI32(int32(p.Chunk.ChunkX())) writer.PushI32(int32(p.Chunk.ChunkZ())) // full chunk (for now >:D) writer.PushBit(true) chunkData := apis_conn.NewBuffer() p.Chunk.Push(chunkData) // write chunk data and primary bit mask // pull primary bit mask and push to writer writer.PushVrI(chunkData.PullVrI()) // write height-maps writer.PushNbt(p.Chunk.HeightMapNbtCompound()) biomes := make([]int32, 1024, 1024) for i := range biomes { biomes[i] = 0 // void biome } for _, biome := range biomes { writer.PushI32(biome) } // data, prefixed with len writer.PushUAS(chunkData.UAS(), true) // write block entities writer.PushVrI(0) } type PacketOPlayerInfo struct { Action client.PlayerInfoAction Values []client.PlayerInfo } func (p *PacketOPlayerInfo) UUID() int32 { return 0x34 } func (p *PacketOPlayerInfo) Push(writer buff.Buffer, conn base.Connection) { writer.PushVrI(int32(p.Action)) writer.PushVrI(int32(len(p.Values))) for _, value := range p.Values { value.Push(writer) } } type PacketOEntityMetadata struct { Entity ents.Entity } func (p *PacketOEntityMetadata) UUID() int32 { return 0x44 } func (p *PacketOEntityMetadata) Push(writer buff.Buffer, conn base.Connection) { writer.PushVrI(int32(p.Entity.EntityUUID())) // questionable... // only supporting player metadata for now _, ok := p.Entity.(ents.Player) if ok { writer.PushByt(16) // index | displayed skin parts writer.PushVrI(0) // type | byte skin := client.SkinParts{ Cape: true, Head: true, Body: true, ArmL: true, ArmR: true, LegL: true, LegR: true, } skin.Push(writer) } writer.PushByt(0xFF) } ================================================ FILE: impl/prot/packets.go ================================================ package prot import ( "github.com/golangmc/minecraft-server/apis/logs" "github.com/golangmc/minecraft-server/apis/task" "github.com/golangmc/minecraft-server/apis/util" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/game/mode" "github.com/golangmc/minecraft-server/impl/prot/server" ) type packets struct { util.Watcher logger *logs.Logging packetI map[base.PacketState]map[int32]func() base.PacketI // UUID to I server_data join chan base.PlayerAndConnection quit chan base.PlayerAndConnection } func NewPackets(tasking *task.Tasking, join chan base.PlayerAndConnection, quit chan base.PlayerAndConnection) base.Packets { packets := &packets{ Watcher: util.NewWatcher(), logger: logs.NewLogging("protocol", logs.EveryLevel...), packetI: createPacketI(), } mode.HandleState0(packets) mode.HandleState1(packets) mode.HandleState2(packets, join) mode.HandleState3(packets, packets.logger, tasking, join, quit) return packets } func (p *packets) GetPacketI(uuid int32, state base.PacketState) base.PacketI { creator := p.packetI[state][uuid] if creator == nil { return nil } return creator() } func createPacketI() map[base.PacketState]map[int32]func() base.PacketI { return map[base.PacketState]map[int32]func() base.PacketI{ base.SHAKE: { 0x00: func() base.PacketI { return &server.PacketIHandshake{} }, }, base.STATUS: { 0x00: func() base.PacketI { return &server.PacketIRequest{} }, 0x01: func() base.PacketI { return &server.PacketIPing{} }, }, base.LOGIN: { 0x00: func() base.PacketI { return &server.PacketILoginStart{} }, 0x01: func() base.PacketI { return &server.PacketIEncryptionResponse{} }, 0x02: func() base.PacketI { return &server.PacketILoginPluginResponse{} }, }, base.PLAY: { 0x00: func() base.PacketI { return &server.PacketITeleportConfirm{} }, 0x01: func() base.PacketI { return &server.PacketIQueryBlockNBT{} }, 0x02: func() base.PacketI { return &server.PacketISetDifficulty{} }, 0x03: func() base.PacketI { return &server.PacketIChatMessage{} }, 0x04: func() base.PacketI { return &server.PacketIClientStatus{} }, 0x05: func() base.PacketI { return &server.PacketIClientSettings{} }, 0x0B: func() base.PacketI { return &server.PacketIPluginMessage{} }, 0x0F: func() base.PacketI { return &server.PacketIKeepAlive{} }, 0x11: func() base.PacketI { return &server.PacketIPlayerPosition{} }, 0x12: func() base.PacketI { return &server.PacketIPlayerLocation{} }, 0x13: func() base.PacketI { return &server.PacketIPlayerRotation{} }, 0x19: func() base.PacketI { return &server.PacketIPlayerAbilities{} }, }, } } ================================================ FILE: impl/prot/server/to_server_state0.go ================================================ package server import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/impl/base" ) // done type PacketIHandshake struct { version int32 host string port uint16 State base.PacketState } func (p *PacketIHandshake) UUID() int32 { return 0x00 } func (p *PacketIHandshake) Pull(reader buff.Buffer, conn base.Connection) { p.version = reader.PullVrI() p.host = reader.PullTxt() p.port = reader.PullU16() state := reader.PullVrI() p.State = base.PacketStateValueOf(int(state)) } ================================================ FILE: impl/prot/server/to_server_state1.go ================================================ package server import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/impl/base" ) // done type PacketIRequest struct { } func (p *PacketIRequest) UUID() int32 { return 0x00 } func (p *PacketIRequest) Pull(reader buff.Buffer, conn base.Connection) { // no fields } type PacketIPing struct { Ping int64 } func (p *PacketIPing) UUID() int32 { return 0x01 } func (p *PacketIPing) Pull(reader buff.Buffer, conn base.Connection) { p.Ping = reader.PullI64() } ================================================ FILE: impl/prot/server/to_server_state2.go ================================================ package server import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/impl/base" ) // done type PacketILoginStart struct { PlayerName string } func (p *PacketILoginStart) UUID() int32 { return 0x00 } func (p *PacketILoginStart) Pull(reader buff.Buffer, conn base.Connection) { p.PlayerName = reader.PullTxt() } type PacketIEncryptionResponse struct { Secret []byte Verify []byte } func (p *PacketIEncryptionResponse) UUID() int32 { return 0x01 } func (p *PacketIEncryptionResponse) Pull(reader buff.Buffer, conn base.Connection) { p.Secret = reader.PullUAS() p.Verify = reader.PullUAS() } type PacketILoginPluginResponse struct { Message int32 Success bool OptData []byte } func (p *PacketILoginPluginResponse) UUID() int32 { return 0x02 } func (p *PacketILoginPluginResponse) Pull(reader buff.Buffer, conn base.Connection) { p.Message = reader.PullVrI() p.Success = reader.PullBit() p.OptData = reader.UAS()[reader.InI():reader.Len()] } ================================================ FILE: impl/prot/server/to_server_state3.go ================================================ package server import ( "github.com/golangmc/minecraft-server/apis/buff" "github.com/golangmc/minecraft-server/apis/data" "github.com/golangmc/minecraft-server/apis/game" "github.com/golangmc/minecraft-server/impl/base" "github.com/golangmc/minecraft-server/impl/data/client" "github.com/golangmc/minecraft-server/impl/data/plugin" ) type PacketIKeepAlive struct { KeepAliveID int64 } func (p *PacketIKeepAlive) UUID() int32 { return 0x0F } func (p *PacketIKeepAlive) Pull(reader buff.Buffer, conn base.Connection) { p.KeepAliveID = reader.PullI64() } type PacketIChatMessage struct { Message string } func (p *PacketIChatMessage) UUID() int32 { return 0x03 } func (p *PacketIChatMessage) Pull(reader buff.Buffer, conn base.Connection) { p.Message = reader.PullTxt() } type PacketITeleportConfirm struct { TeleportID int32 } func (p *PacketITeleportConfirm) UUID() int32 { return 0x00 } func (p *PacketITeleportConfirm) Pull(reader buff.Buffer, conn base.Connection) { p.TeleportID = reader.PullVrI() } type PacketIQueryBlockNBT struct { TransactionID int32 Position data.PositionI } func (p *PacketIQueryBlockNBT) UUID() int32 { return 0x01 } func (p *PacketIQueryBlockNBT) Pull(reader buff.Buffer, conn base.Connection) { p.TransactionID = reader.PullVrI() p.Position = reader.PullPos() } type PacketISetDifficulty struct { Difficult game.Difficulty } func (p *PacketISetDifficulty) UUID() int32 { return 0x02 } func (p *PacketISetDifficulty) Pull(reader buff.Buffer, conn base.Connection) { p.Difficult = game.DifficultyValueOf(reader.PullByt()) } type PacketIPluginMessage struct { Message plugin.Message } func (p *PacketIPluginMessage) UUID() int32 { return 0x0B } func (p *PacketIPluginMessage) Pull(reader buff.Buffer, conn base.Connection) { channel := reader.PullTxt() message := plugin.GetMessageForChannel(channel) if message == nil { return // log unregistered channel? } message.Pull(reader) p.Message = message } type PacketIClientStatus struct { Action client.StatusAction } func (p *PacketIClientStatus) UUID() int32 { return 0x04 } func (p *PacketIClientStatus) Pull(reader buff.Buffer, conn base.Connection) { p.Action = client.StatusAction(reader.PullVrI()) } type PacketIClientSettings struct { Locale string ViewDistance byte ChatMode client.ChatMode ChatColors bool // if false, strip messages of colors before sending SkinParts client.SkinParts MainHand client.MainHand } func (p *PacketIClientSettings) UUID() int32 { return 0x05 } func (p *PacketIClientSettings) Pull(reader buff.Buffer, conn base.Connection) { p.Locale = reader.PullTxt() p.ViewDistance = reader.PullByt() p.ChatMode = client.ChatMode(reader.PullVrI()) p.ChatColors = reader.PullBit() parts := client.SkinParts{} parts.Pull(reader) p.SkinParts = parts p.MainHand = client.MainHand(reader.PullVrI()) } type PacketIPlayerAbilities struct { Abilities client.PlayerAbilities FlightSpeed float32 GroundSpeed float32 } func (p *PacketIPlayerAbilities) UUID() int32 { return 0x19 } func (p *PacketIPlayerAbilities) Pull(reader buff.Buffer, conn base.Connection) { abilities := client.PlayerAbilities{} abilities.Pull(reader) p.Abilities = abilities p.FlightSpeed = reader.PullF32() p.GroundSpeed = reader.PullF32() } type PacketIPlayerPosition struct { Position data.PositionF OnGround bool } func (p *PacketIPlayerPosition) UUID() int32 { return 0x11 } func (p *PacketIPlayerPosition) Pull(reader buff.Buffer, conn base.Connection) { p.Position = data.PositionF{ X: reader.PullF64(), Y: reader.PullF64(), Z: reader.PullF64(), } p.OnGround = reader.PullBit() } type PacketIPlayerLocation struct { Location data.Location OnGround bool } func (p *PacketIPlayerLocation) UUID() int32 { return 0x12 } func (p *PacketIPlayerLocation) Pull(reader buff.Buffer, conn base.Connection) { p.Location = data.Location{ PositionF: data.PositionF{ X: reader.PullF64(), Y: reader.PullF64(), Z: reader.PullF64(), }, RotationF: data.RotationF{ AxisX: reader.PullF32(), AxisY: reader.PullF32(), }, } p.OnGround = reader.PullBit() } type PacketIPlayerRotation struct { Rotation data.RotationF OnGround bool } func (p *PacketIPlayerRotation) UUID() int32 { return 0x13 } func (p *PacketIPlayerRotation) Pull(reader buff.Buffer, conn base.Connection) { p.Rotation = data.RotationF{ AxisX: reader.PullF32(), AxisY: reader.PullF32(), } p.OnGround = reader.PullBit() } ================================================ FILE: impl/server.go ================================================ package impl import ( "fmt" "strconv" "strings" "time" "github.com/golangmc/minecraft-server/apis" "github.com/golangmc/minecraft-server/apis/cmds" "github.com/golangmc/minecraft-server/apis/data/chat" "github.com/golangmc/minecraft-server/apis/ents" "github.com/golangmc/minecraft-server/apis/logs" "github.com/golangmc/minecraft-server/apis/task" "github.com/golangmc/minecraft-server/apis/util" "github.com/golangmc/minecraft-server/apis/uuid" "github.com/golangmc/minecraft-server/impl/conf" "github.com/golangmc/minecraft-server/impl/data/plugin" "github.com/golangmc/minecraft-server/impl/conn" "github.com/golangmc/minecraft-server/impl/cons" "github.com/golangmc/minecraft-server/impl/data/system" "github.com/golangmc/minecraft-server/impl/data/values" "github.com/golangmc/minecraft-server/impl/prot" apis_base "github.com/golangmc/minecraft-server/apis/base" impl_base "github.com/golangmc/minecraft-server/impl/base" apis_event "github.com/golangmc/minecraft-server/apis/game/event" impl_event "github.com/golangmc/minecraft-server/impl/game/event" ) type server struct { message chan system.Message console *cons.Console logging *logs.Logging tasking *task.Tasking watcher util.Watcher command *cmds.CommandManager network impl_base.Network packets impl_base.Packets players *playerAssociation } // ==== new ==== func NewServer(conf conf.ServerConfig) apis.Server { message := make(chan system.Message) console := cons.NewConsole(message) logging := logs.NewLogging("server", logs.EveryLevel...) tasking := task.NewTasking(values.MPT) watcher := util.NewWatcher() join := make(chan impl_base.PlayerAndConnection) quit := make(chan impl_base.PlayerAndConnection) packets := prot.NewPackets(tasking, join, quit) network := conn.NewNetwork(conf.Network.Host, conf.Network.Port, packets, message, join, quit) command := cmds.NewCommandManager() return &server{ message: message, console: console, logging: logging, tasking: tasking, watcher: watcher, command: command, packets: packets, network: network, players: &playerAssociation{ uuidToData: make(map[uuid.UUID]ents.Player), connToUUID: make(map[impl_base.Connection]uuid.UUID), uuidToConn: make(map[uuid.UUID]impl_base.Connection), }, } } // ==== State ==== func (s *server) Load() { apis.SetMinecraftServer(s) go s.loadServer() go s.readInputs() s.wait() } func (s *server) Kill() { s.console.Kill() s.command.Kill() s.tasking.Kill() s.network.Kill() // push the stop message to the server exit channel s.message <- system.Make(system.STOP, "normal stop") close(s.message) s.logging.Info(chat.DarkRed, "server stopped") } // ==== Server ==== func (s *server) Logging() *logs.Logging { return s.logging } func (s *server) Command() *cmds.CommandManager { return s.command } func (s *server) Tasking() *task.Tasking { return s.tasking } func (s *server) Watcher() util.Watcher { return s.watcher } func (s *server) Players() []ents.Player { players := make([]ents.Player, 0) for _, player := range s.players.uuidToData { players = append(players, player) } return players } func (s *server) ConnByUUID(uuid uuid.UUID) impl_base.Connection { return s.players.uuidToConn[uuid] } func (s *server) PlayerByUUID(uuid uuid.UUID) ents.Player { return s.players.uuidToData[uuid] } func (s *server) PlayerByConn(conn impl_base.Connection) ents.Player { uuid, con := s.players.connToUUID[conn] if !con { return nil } return s.PlayerByUUID(uuid) } func (s *server) ServerVersion() string { return "0.0.1-SNAPSHOT" } func (s *server) Broadcast(message string) { s.console.SendMessage(message) for _, player := range s.Players() { player.SendMessage(message) } } // ==== server commands ==== func (s *server) broadcastCommand(sender ents.Sender, params []string) { message := strings.Join(params, " ") for _, player := range s.Players() { player.SendMessage(message) } } func (s *server) stopServerCommand(sender ents.Sender, params []string) { if _, ok := sender.(*cons.Console); !ok { s.logging.FailF("non console sender %s tried to stop the server", sender.Name()) return } var after int64 = 0 if len(params) > 0 { param, err := strconv.Atoi(params[0]) if err != nil { panic(err) } if param <= 0 { panic(fmt.Errorf("value must be a positive whole number. [1..]")) } after = int64(param) } if after == 0 { s.Kill() } else { // inform future shutdown s.logging.Warn(chat.Gold, "stopping server in ", chat.Green, util.FormatTime(after)) // schedule shutdown {after} seconds later s.tasking.AfterTime(after, time.Second, func(task *task.Task) { s.Kill() }) } } func (s *server) versionCommand(sender ents.Sender, params []string) { sender.SendMessage(s.ServerVersion()) } // ==== internal ==== func (s *server) loadServer() { s.console.Load() s.command.Load() s.tasking.Load() s.network.Load() s.command.Register("vers", s.versionCommand) s.command.Register("send", s.broadcastCommand) s.command.Register("stop", s.stopServerCommand) s.watcher.SubAs(func(event apis_event.PlayerJoinEvent) { s.logging.InfoF("player %s logged in with uuid:%v", event.Player.Name(), event.Player.UUID()) s.Broadcast(chat.Translate(fmt.Sprintf("%s%s has joined!", chat.Yellow, event.Player.Name()))) }) s.watcher.SubAs(func(event apis_event.PlayerQuitEvent) { s.logging.InfoF("%s disconnected!", event.Player.Name()) s.Broadcast(chat.Translate(fmt.Sprintf("%s%s has left!", chat.Yellow, event.Player.Name()))) }) s.watcher.SubAs(func(event impl_event.PlayerConnJoinEvent) { s.players.addData(event.Conn) s.watcher.PubAs(apis_event.PlayerJoinEvent{PlayerEvent: apis_event.PlayerEvent{Player: event.Conn.Player}}) }) s.watcher.SubAs(func(event impl_event.PlayerConnQuitEvent) { player := s.players.playerByConn(event.Conn.Connection) if player != nil { s.watcher.PubAs(apis_event.PlayerQuitEvent{PlayerEvent: apis_event.PlayerEvent{Player: player}}) } s.players.delData(event.Conn) }) s.watcher.SubAs(func(event impl_event.PlayerPluginMessagePullEvent) { s.logging.DataF("received message on channel '%s' from player %s:%s", event.Channel, event.Conn.Name(), event.Conn.UUID()) switch event.Channel { case plugin.CHANNEL_BRAND: s.logging.DataF("their client's brand is '%s'", event.Message.(*plugin.Brand).Name) } }) } func (s *server) readInputs() { for { // read input from console text := strings.Trim(<-s.console.IChannel, " ") if len(text) == 0 { continue } args := strings.Split(text, " ") if len(args) == 0 { continue } if command := s.command.Search(args[0]); command != nil { err := apis_base.Attempt(func() { (*command).Evaluate(s.console, args[1:]) }) if err != nil { s.logging.Fail( chat.Red, "failed to evaluate ", chat.DarkGray, "`", chat.White, (*command).Name(), chat.DarkGray, "`", chat.Red, ": ", err.Error()[8:]) } continue } s.console.SendMessage(text) } } func (s *server) wait() { // select over server commands channel select { case command := <-s.message: switch command.Command { // stop selecting when stop is received case system.STOP: return case system.FAIL: s.logging.Fail("internal server error: ", command.Message) s.logging.Fail("stopping server") return } } s.wait() } // ==== players ==== type playerAssociation struct { uuidToData map[uuid.UUID]ents.Player connToUUID map[impl_base.Connection]uuid.UUID uuidToConn map[uuid.UUID]impl_base.Connection } func (p *playerAssociation) addData(data impl_base.PlayerAndConnection) { p.uuidToData[data.Player.UUID()] = data.Player p.connToUUID[data.Connection] = data.Player.UUID() p.uuidToConn[data.Player.UUID()] = data.Connection } func (p *playerAssociation) delData(data impl_base.PlayerAndConnection) { player := p.playerByConn(data.Connection) uuid := p.connToUUID[data.Connection] delete(p.connToUUID, data.Connection) delete(p.uuidToConn, uuid) if player != nil { delete(p.uuidToData, player.UUID()) } } func (p *playerAssociation) playerByUUID(uuid uuid.UUID) ents.Player { return p.uuidToData[uuid] } func (p *playerAssociation) playerByConn(conn impl_base.Connection) ents.Player { uuid, con := p.connToUUID[conn] if !con { return nil } data, con := p.uuidToData[uuid] if !con { return nil } return data } ================================================ FILE: main.go ================================================ package main import ( "flag" "github.com/fatih/color" "github.com/golangmc/minecraft-server/impl" "github.com/golangmc/minecraft-server/impl/conf" ) func main() { color.NoColor = false server := impl.NewServer(mergeWithFlags(conf.DefaultServerConfig)) server.Load() } func mergeWithFlags(c conf.ServerConfig) conf.ServerConfig { host := flag.String("host", conf.DefaultServerConfig.Network.Host, "the address this server will bind to") port := flag.Int("port", conf.DefaultServerConfig.Network.Port, "the port this server will bind to") flag.Parse() if *host != conf.DefaultServerConfig.Network.Host { c.Network.Host = *host } if *port != conf.DefaultServerConfig.Network.Port { c.Network.Port = *port } return c }