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
================================================
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
}