Rename cmd -> examples

This commit is contained in:
Tnze
2021-02-27 15:00:25 +08:00
parent 8e7ac43bf5
commit a7bf3b683f
12 changed files with 28 additions and 17 deletions

View File

@ -0,0 +1,115 @@
package main
import (
"github.com/Tnze/go-mc/bot/basic"
"github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet"
"log"
"time"
"github.com/google/uuid"
"github.com/Tnze/go-mc/bot"
"github.com/Tnze/go-mc/chat"
_ "github.com/Tnze/go-mc/data/lang/en-us"
"github.com/mattn/go-colorable"
)
const timeout = 45
var (
c *bot.Client
p *basic.Player
watch chan time.Time
)
func main() {
log.SetOutput(colorable.NewColorableStdout())
c = bot.NewClient()
p = basic.NewPlayer(c, basic.DefaultSettings)
//Register event handlers
basic.EventsListener{
GameStart: onGameStart,
ChatMsg: onChatMsg,
Disconnect: onDisconnect,
Death: onDeath,
}.Attach(c)
c.Events.AddListener(bot.PacketHandler{
ID: packetid.NamedSoundEffect,
Priority: 0,
F: func(p pk.Packet) error {
return onSound()
},
})
//Login
err := c.JoinServer("127.0.0.1")
if err != nil {
log.Fatal(err)
}
log.Println("Login success")
//JoinGame
err = c.HandleGame()
if err != nil {
log.Fatal(err)
}
}
func onDeath() error {
log.Println("Died and Respawned")
// If we exclude Respawn(...) then the player won't press the "Respawn" button upon death
return p.Respawn()
}
func onGameStart() error {
log.Println("Game start")
watch = make(chan time.Time)
go watchDog()
return c.UseItem(0)
}
//goland:noinspection SpellCheckingInspection
func onSound(name string, category int, x, y, z float64, volume, pitch float32) error {
if name == "entity.fishing_bobber.splash" {
if err := c.UseItem(0); err != nil { //retrieve
return err
}
log.Println("gra~")
time.Sleep(time.Millisecond * 300)
if err := c.UseItem(0); err != nil { //throw
return err
}
watch <- time.Now()
}
return nil
}
func onChatMsg(c chat.Message, pos byte, uuid uuid.UUID) error {
log.Println("Chat:", c)
return nil
}
func onDisconnect(c chat.Message) error {
log.Println("Disconnect:", c)
return nil
}
func watchDog() {
to := time.NewTimer(time.Second * timeout)
for {
select {
case <-watch:
case <-to.C:
log.Println("rethrow")
if err := c.UseItem(0); err != nil {
panic(err)
}
}
to.Reset(time.Second * timeout)
}
}

98
examples/daze/daze.go Normal file
View File

@ -0,0 +1,98 @@
// Daze could join an offline-mode server as client.
// Just standing there and do nothing. Automatically reborn after five seconds of death.
//
// BUG(Tnze): Kick by Disconnect: Time Out
package main
import (
"errors"
"flag"
"log"
"time"
"github.com/google/uuid"
"github.com/Tnze/go-mc/bot"
"github.com/Tnze/go-mc/bot/basic"
"github.com/Tnze/go-mc/chat"
_ "github.com/Tnze/go-mc/data/lang/zh-cn"
)
var address = flag.String("address", "127.0.0.1", "The server address")
var client *bot.Client
var player *basic.Player
func main() {
flag.Parse()
client = bot.NewClient()
client.Auth.Name = "Daze"
player = basic.NewPlayer(client, basic.DefaultSettings)
basic.EventsListener{
GameStart: onGameStart,
ChatMsg: onChatMsg,
Disconnect: onDisconnect,
Death: onDeath,
}.Attach(client)
//Login
err := client.JoinServer(*address)
if err != nil {
log.Fatal(err)
}
log.Println("Login success")
//JoinGame
for {
if err = client.HandleGame(); err == nil {
panic("HandleGame never return nil")
}
if err2 := new(bot.PacketHandlerError); errors.As(err, err2) {
if err := new(DisconnectErr); errors.As(err2, err) {
log.Print("Disconnect: ", err.Reason)
return
} else {
// print and ignore the error
log.Print(err2)
}
} else {
log.Fatal(err)
}
}
}
func onDeath() error {
log.Println("Died and Respawned")
// If we exclude Respawn(...) then the player won't press the "Respawn" button upon death
go func() {
time.Sleep(time.Second * 5)
err := player.Respawn()
if err != nil {
log.Print(err)
}
}()
return nil
}
func onGameStart() error {
log.Println("Game start")
return nil //if err isn't nil, HandleGame() will return it.
}
func onChatMsg(c chat.Message, _ byte, _ uuid.UUID) error {
log.Println("Chat:", c.ClearString()) // output chat message without any format code (like color or bold)
return nil
}
type DisconnectErr struct {
Reason chat.Message
}
func (d DisconnectErr) Error() string {
return "disconnect: " + d.Reason.String()
}
func onDisconnect(reason chat.Message) error {
// return a error value so that we can stop main loop
return DisconnectErr{Reason: reason}
}

View File

@ -0,0 +1,26 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/Tnze/go-mc/yggdrasil"
)
var user = flag.String("user", "", "Can be an email address or player name for unmigrated accounts")
var pswd = flag.String("password", "", "Your password")
func main() {
flag.Parse()
resp, err := yggdrasil.Authenticate(*user, *pswd)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
id, name := resp.SelectedProfile()
fmt.Println("user:", name)
fmt.Println("uuid:", id)
fmt.Println("astk:", resp.AccessToken())
}

125
examples/mcadump/mcadump.go Normal file
View File

@ -0,0 +1,125 @@
package main
import (
"bytes"
"compress/gzip"
"compress/zlib"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"github.com/Tnze/go-mc/save/region"
)
var decomp = flag.Bool("x", false, "decompress each chunk to NBT format")
var repack = flag.Bool("p", false, "repack .mcc file to .mca")
func main() {
flag.Usage = usage
flag.Parse()
args := flag.Args()
var o string
o = "." // output dir
if len(args) < 2 {
usage()
}
for _, f := range args[1:] {
fs, err := filepath.Glob(f)
checkerr(err)
if *repack {
for _, f := range fs {
pack(f, o)
}
} else {
for _, f := range fs {
unpack(f, o)
}
}
}
}
func usage() {
_, _ = fmt.Fprintf(os.Stderr, "usage: %s [-x] [-r] r.<X>.<Z>.mc{a,c}\n", os.Args[0])
os.Exit(1)
}
// we use this function to check for laziness. Don't scold me >_<
func checkerr(err error) {
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func unpack(f, o string) {
var x, z int
rn := filepath.Base(f)
_, err := fmt.Sscanf(rn, "r.%d.%d.mca", &x, &z)
if err != nil {
checkerr(fmt.Errorf("cannot use %s as mca file name: %v", rn, err))
}
r, err := region.Open(f)
checkerr(err)
defer r.Close()
for i := 0; i < 32; i++ {
for j := 0; j < 32; j++ {
if !r.ExistSector(i, j) {
continue
}
data, err := r.ReadSector(i, j)
checkerr(err)
var r io.Reader = bytes.NewReader(data[1:])
fn := fmt.Sprintf("c.%d.%d.mcc", x*32+i, z*32+j)
if *decomp {
fn += ".nbt" //解压后就是一个标准的NBT文件可以加个.nbt后缀
switch data[0] {
default:
err = fmt.Errorf("unknown compression type 0x%02x", data[0])
case 1:
r, err = gzip.NewReader(r)
case 2:
r, err = zlib.NewReader(r)
}
checkerr(err)
}
cf, err := os.OpenFile(filepath.Join(o, fn), os.O_CREATE|os.O_RDWR|os.O_EXCL, 0666)
checkerr(err)
_, err = io.Copy(cf, r)
checkerr(err)
}
}
}
func pack(f, o string) {
var x, z int
rn := filepath.Base(f)
_, err := fmt.Sscanf(rn, "c.%d.%d.mcc", &x, &z)
if err != nil {
checkerr(fmt.Errorf("cannot use %s as mcc file name: %v", rn, err))
}
fn := fmt.Sprintf("r.%d.%d.mca", x>>5, z>>5)
r, err := region.Open(fn)
if err != nil && os.IsNotExist(err) {
r, err = region.Create(filepath.Join(o, fn))
}
checkerr(err)
defer r.Close()
mcc, err := os.ReadFile(f)
checkerr(err)
rx, rz := region.In(x, z)
err = r.WriteSector(rx, rz, mcc)
checkerr(err)
}

17
examples/mcping/README.md Normal file
View File

@ -0,0 +1,17 @@
# mcping
Ping tool for Minecraft: Java Edition.
Just for example. Not recommended for daily use. Use [github.com/go-mc/mcping](github.com/go-mc/mcping) instead, which including SRV parse.
适用于Minecraft: Java Edition的ping工具。
只起示例作用,日常使用建议使用完整版[github.com/go-mc/mcping](github.com/go-mc/mcping)包含SRV解析等功能。
Install with go tools:
```go get -u github.com/Tnze/go-mc/cmd/mcping```
`$GOPATH/bin` should in your `$PATH`.
Install with Homebrew:
```brew tap Tnze/tap && brew install mcping```
Useage:
```mcping <hostname>[:port]```

72
examples/mcping/mcping.go Normal file
View File

@ -0,0 +1,72 @@
// Usage: go run examples/ping/ping.go localhost
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/Tnze/go-mc/bot"
"github.com/Tnze/go-mc/chat"
"github.com/google/uuid"
)
type status struct {
Description chat.Message
Players struct {
Max int
Online int
Sample []struct {
ID uuid.UUID
Name string
}
}
Version struct {
Name string
Protocol int
}
//favicon ignored
}
func main() {
addr := getAddr()
fmt.Printf("MCPING (%s):\n", addr)
resp, delay, err := bot.PingAndList(addr)
if err != nil {
fmt.Printf("ping and list server fail: %v", err)
os.Exit(1)
}
var s status
err = json.Unmarshal(resp, &s)
if err != nil {
fmt.Print("unmarshal resp fail:", err)
os.Exit(1)
}
fmt.Print(s)
fmt.Println("Delay:", delay)
}
func getAddr() string {
const usage = "Usage: mcping <hostname>[:port]"
if len(os.Args) < 2 {
fmt.Println("no host name.", usage)
os.Exit(1)
}
return os.Args[1]
}
func (s status) String() string {
var sb strings.Builder
fmt.Fprintln(&sb, "Server:", s.Version.Name)
fmt.Fprintln(&sb, "Protocol:", s.Version.Protocol)
fmt.Fprintln(&sb, "Description:", s.Description)
fmt.Fprintf(&sb, "Players: %d/%d\n", s.Players.Online, s.Players.Max)
for _, v := range s.Players.Sample {
fmt.Fprintf(&sb, "- [%s] %v\n", v.Name, v.ID)
}
return sb.String()
}

View File

@ -0,0 +1,164 @@
// Example minecraft 1.15.2 server
package main
import (
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/offline"
"github.com/google/uuid"
"log"
)
const ProtocolVersion = 578
const MaxPlayer = 200
// Packet IDs
const (
PlayerPositionAndLookClientbound = 0x36
JoinGame = 0x26
)
func main() {
l, err := net.ListenMC(":25565")
if err != nil {
log.Fatalf("Listen error: %v", err)
}
for {
conn, err := l.Accept()
if err != nil {
log.Fatalf("Accept error: %v", err)
}
go acceptConn(conn)
}
}
func acceptConn(conn net.Conn) {
defer conn.Close()
// handshake
protocol, intention, err := handshake(conn)
if err != nil {
log.Printf("Handshake error: %v", err)
return
}
switch intention {
default: //unknown error
log.Printf("Unknown handshake intention: %v", intention)
case 1: //for status
acceptListPing(conn)
case 2: //for login
handlePlaying(conn, protocol)
}
}
func handlePlaying(conn net.Conn, protocol int32) {
// login, get player info
info, err := acceptLogin(conn)
if err != nil {
log.Print("Login failed")
return
}
// Write LoginSuccess packet
if err = loginSuccess(conn, info.Name, info.UUID); err != nil {
log.Print("Login failed on success")
return
}
if err := joinGame(conn); err != nil {
log.Print("Login failed on joinGame")
return
}
if err := conn.WritePacket(pk.Marshal(PlayerPositionAndLookClientbound,
// https://wiki.vg/index.php?title=Protocol&oldid=16067#Player_Position_And_Look_.28clientbound.29
pk.Double(0), pk.Double(0), pk.Double(0), // XYZ
pk.Float(0), pk.Float(0), // Yaw Pitch
pk.Byte(0), // flag
pk.VarInt(0), // TP ID
)); err != nil {
log.Print("Login failed on sending PlayerPositionAndLookClientbound")
return
}
// Just for block this goroutine. Keep the connection
for {
var p pk.Packet
if err := conn.ReadPacket(&p); err != nil {
log.Printf("ReadPacket error: %v", err)
break
}
// KeepAlive packet is not handled, so client might
// exit because of "time out".
}
}
type PlayerInfo struct {
Name string
UUID uuid.UUID
OPLevel int
}
// acceptLogin check player's account
func acceptLogin(conn net.Conn) (info PlayerInfo, err error) {
//login start
var p pk.Packet
err = conn.ReadPacket(&p)
if err != nil {
return
}
err = p.Scan((*pk.String)(&info.Name)) //decode username as pk.String
if err != nil {
return
}
//auth
const OnlineMode = false
if OnlineMode {
log.Panic("Not Implement")
} else {
// offline-mode UUID
info.UUID = offline.NameToUUID(info.Name)
}
return
}
// handshake receive and parse Handshake packet
func handshake(conn net.Conn) (protocol, intention int32, err error) {
var (
p pk.Packet
Protocol, Intention pk.VarInt
ServerAddress pk.String // ignored
ServerPort pk.UnsignedShort // ignored
)
// receive handshake packet
if err = conn.ReadPacket(&p); err != nil {
return
}
err = p.Scan(&Protocol, &ServerAddress, &ServerPort, &Intention)
return int32(Protocol), int32(Intention), err
}
// loginSuccess send LoginSuccess packet to client
func loginSuccess(conn net.Conn, name string, uuid uuid.UUID) error {
return conn.WritePacket(pk.Marshal(0x02,
pk.String(uuid.String()), //uuid as string with hyphens
pk.String(name),
))
}
func joinGame(conn net.Conn) error {
return conn.WritePacket(pk.Marshal(JoinGame,
pk.Int(0), // EntityID
pk.UnsignedByte(1), // Gamemode
pk.Int(0), // Dimension
pk.Long(0), // HashedSeed
pk.UnsignedByte(MaxPlayer), // MaxPlayer
pk.String("default"), // LevelType
pk.VarInt(15), // View Distance
pk.Boolean(false), // Reduced Debug Info
pk.Boolean(true), // Enable respawn screen
))
}

View File

@ -0,0 +1,65 @@
package main
import (
"encoding/json"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/google/uuid"
"log"
)
func acceptListPing(conn net.Conn) {
var p pk.Packet
for i := 0; i < 2; i++ { // ping or list. Only accept twice
err := conn.ReadPacket(&p)
if err != nil {
return
}
switch p.ID {
case 0x00: //List
err = conn.WritePacket(pk.Marshal(0x00, pk.String(listResp())))
case 0x01: //Ping
err = conn.WritePacket(p)
}
if err != nil {
return
}
}
}
type player struct {
Name string `json:"name"`
ID uuid.UUID `json:"id"`
}
// listResp return server status as JSON string
func listResp() string {
var list struct {
Version struct {
Name string `json:"name"`
Protocol int `json:"protocol"`
} `json:"version"`
Players struct {
Max int `json:"max"`
Online int `json:"online"`
Sample []player `json:"sample"`
} `json:"players"`
Description chat.Message `json:"description"`
FavIcon string `json:"favicon,omitempty"`
}
list.Version.Name = "Chat Server"
list.Version.Protocol = ProtocolVersion
list.Players.Max = MaxPlayer
list.Players.Online = 123
list.Players.Sample = []player{} // must init. can't be nil
list.Description = chat.Message{Text: "Powered by go-mc", Color: "blue"}
data, err := json.Marshal(list)
if err != nil {
log.Panic("Marshal JSON for status checking fail")
}
return string(data)
}