From b1b57e067b4fe5f942b44be18f27831563232da3 Mon Sep 17 00:00:00 2001 From: Tnze Date: Tue, 23 Nov 2021 15:46:01 +0800 Subject: [PATCH] Document of RCON Connection --- net/rcon.go | 8 ++++++ net/rcon_test.go | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/net/rcon.go b/net/rcon.go index 651b7e8..316292f 100644 --- a/net/rcon.go +++ b/net/rcon.go @@ -11,6 +11,9 @@ import ( const MaxRCONPackageSize = 4096 +// DialRCON connect to a RCON server and return the connection after login. +// We promise the returned RCONClientConn is an RCONConn, so you can convert +// them by type assertions if you need call the ReadPacket() or WritePacket() methods. func DialRCON(addr string, password string) (client RCONClientConn, err error) { c := &RCONConn{ReqID: rand.Int31()} client = c @@ -180,6 +183,7 @@ type RCONClientConn interface { Close() error } +// RCONServerConn is the connection in the server side. type RCONServerConn interface { AcceptLogin(password string) error AcceptCmd() (cmd string, err error) @@ -187,6 +191,7 @@ type RCONServerConn interface { Close() error } +// ListenRCON announces on the local network address, accepting RCON clients. func ListenRCON(addr string) (*RCONListener, error) { l, err := net.Listen("tcp", addr) if err != nil { @@ -198,6 +203,9 @@ func ListenRCON(addr string) (*RCONListener, error) { type RCONListener struct{ net.Listener } +// Accept RCON connection for client. +// We promise the returned RCONServerConn is an RCONConn, so you can convert +// them by type assertions if you need call the ReadPacket() or WritePacket() methods. func (r *RCONListener) Accept() (RCONServerConn, error) { conn, err := r.Listener.Accept() if err != nil { diff --git a/net/rcon_test.go b/net/rcon_test.go index 4cf857c..91006f9 100644 --- a/net/rcon_test.go +++ b/net/rcon_test.go @@ -69,3 +69,69 @@ func client(t *testing.T) { } t.Logf("Server response: %q", resp) } + +func ExampleListenRCON() { + l, err := ListenRCON("localhost:25575") + if err != nil { + panic(err) + } + defer l.Close() + + for { + conn, err := l.Accept() + if err != nil { + fmt.Printf("Accept connection error: %v", err) + } + + go func(conn RCONServerConn) { + err = conn.AcceptLogin("CORRECT_PASSWORD") + if err != nil { + fmt.Printf("Login fail: %v", err) + } + defer conn.Close() + + // The client is login, we are accepting its command + for { + cmd, err := conn.AcceptCmd() + if err != nil { + fmt.Printf("Read command fail: %v", err) + break + } + + resp := handleCommand(cmd) + + // Return the result of command. + // It's allowed to call RespCmd multiple times for one command. + err = conn.RespCmd(resp) + if err != nil { + fmt.Printf("Response command fail: %v", err) + break + } + } + }(conn) + } +} + +func ExampleDialRCON() { + conn, err := DialRCON("localhost:25575", "CORRECT_PASSWORD") + if err != nil { + panic(err) + } + defer conn.Close() + + err = conn.Cmd("TEST COMMAND") + if err != nil { + panic(err) + } + + for { + // Server may send the result in more(or less) than one packet. + // See: https://wiki.vg/RCON#Fragmentation + resp, err := conn.Resp() + if err != nil { + fmt.Print(err) + } + fmt.Printf("Server response: %q", resp) + break + } +}