forked from Tnze/go-mc
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
163 lines (144 loc) · 3.76 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Example minecraft 1.15.2 server
package main
import (
"github.com/ForgottenWorld/go-mc/net"
pk "github.com/ForgottenWorld/go-mc/net/packet"
"github.com/ForgottenWorld/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 {
if _, err := conn.ReadPacket(); 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
p, err = conn.ReadPacket()
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 (
Protocol, Intention pk.VarInt
ServerAddress pk.String // ignored
ServerPort pk.UnsignedShort // ignored
)
// receive handshake packet
p, err := conn.ReadPacket()
if err != nil {
return 0, 0, err
}
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
))
}