realms support

This commit is contained in:
Tnze
2019-08-09 23:50:53 +08:00
parent b0e6a70e0a
commit ad5dfeffd7
7 changed files with 316 additions and 1 deletions

View File

@ -1,11 +1,13 @@
<component name="ProjectDictionaryState">
<dictionary name="cjd00">
<words>
<w>astk</w>
<w>cooldown</w>
<w>craftable</w>
<w>ender</w>
<w>minecraft</w>
<w>mojang</w>
<w>motd</w>
<w>rcon</w>
<w>teleport</w>
</words>

View File

@ -10,10 +10,11 @@ There's some library in Go support you to create your Minecraft client or server
- [x] Chat
- [x] Parse NBT
- [x] Simple MC robot lib
- [x] Mojang authenticate
- [x] Yggdrasil
- [x] Minecraft network protocol
- [x] RCON protocol
- [x] Saves decoding /encoding
- [ ] Realms Server
bot:
- [x] Swing arm

13
realms/invite.go Normal file
View File

@ -0,0 +1,13 @@
package realms
import "fmt"
// Invite invite player to Realm
func (r *Realms) Invite(s Server, name, uuid string) error {
pl := struct {
Name string `json:"name"`
UUID string `json:"uuid"`
}{Name: name, UUID: uuid}
return r.post(fmt.Sprintf("/invites/%d", s.ID), pl, struct{}{})
}

36
realms/mco.go Normal file
View File

@ -0,0 +1,36 @@
package realms
import "io/ioutil"
// Available returns whether the user can access the Minecraft Realms service
func (r *Realms) Available() (ok bool, err error) {
err = r.get("/mco/available", &ok)
return
}
// Compatible returns whether the clients version is up to date with Realms.
// if the client is outdated, it returns OUTDATED,
// if the client is running a snapshot, it returns OTHER,
// else it returns COMPATIBLE.
func (r *Realms) Compatible() (string, error) {
resp, err := r.c.Get(Domain + "/mco/client/compatible")
if err != nil {
return "", err
}
defer resp.Body.Close()
rp, err := ioutil.ReadAll(resp.Body)
return string(rp), err
}
// TOS is what to join Realms servers you must agree to.
// Call this function will set this flag.
func (r *Realms) TOS()error{
resp,err:=r.c.Post(Domain+"/mco/tos/agreed","application/json",nil)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}

88
realms/realms.go Normal file
View File

@ -0,0 +1,88 @@
package realms
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/cookiejar"
"net/url"
)
type Realms struct {
c http.Client
}
type Error struct {
ErrorCode int
ErrorMsg string
}
func (e *Error) Error() string {
return fmt.Sprintf("[%d] %s", e.ErrorCode, e.ErrorMsg)
}
// Domain is the URL of Realms API server
// Panic if it cannot be parse by url.Parse().
var Domain = "https://pc.realms.minecraft.net"
// New create a new Realms c with version, username, accessToken and UUID without dashes.
func New(version, user, astk, uuid string) *Realms {
r := &Realms{
c: http.Client{},
}
var err error
r.c.Jar, err = cookiejar.New(nil)
if err != nil {
panic(err)
}
d, err := url.Parse(Domain)
if err != nil {
panic("cannot parse realms.Domain as url: " + err.Error())
}
r.c.Jar.SetCookies(d, []*http.Cookie{
{Name: "user", Value: user},
{Name: "version", Value: version},
{Name: "sid", Value: "token:" + astk + ":" + uuid},
})
return r
}
func (r *Realms) get(endpoint string, resp interface{}) error {
rawResp, err := r.c.Get(Domain + endpoint)
if err != nil {
return err
}
defer rawResp.Body.Close()
err = json.NewDecoder(rawResp.Body).Decode(resp)
if err != nil {
return err
}
return nil
}
func (r *Realms) post(endpoint string, payload, resp interface{}) error {
data, err := json.Marshal(payload)
if err != nil {
return err
}
rawResp, err := r.c.Post(Domain+endpoint, "application/json", bytes.NewReader(data))
if err != nil {
return err
}
err = json.NewDecoder(rawResp.Body).Decode(resp)
if err != nil {
return err
}
return nil
}

36
realms/realms_test.go Normal file
View File

@ -0,0 +1,36 @@
package realms
import (
"fmt"
"time"
)
func ExampleRealms() {
var r *Realms
r = New(
"1.14.4",
"Name",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
)
fmt.Println(r.Available())
fmt.Println(r.Compatible())
servers, err := r.Worlds()
if err != nil {
panic(err)
}
for _, v := range servers {
fmt.Println(v.Name, v.ID)
}
time.Sleep(time.Second * 5)
if err := r.TOS(); err != nil {
panic(err)
}
time.Sleep(time.Second * 5)
fmt.Println(r.Address(servers[0]))
}

139
realms/server.go Normal file
View File

@ -0,0 +1,139 @@
package realms
import (
"errors"
"fmt"
)
type Server struct {
ID int
RemoteSubscriptionID string
Owner string
OwnerUUID string
Name string
MOTD string
State string
DaysLeft int
Expired bool
ExpiredTrial bool
WorldType string
Players []string
MaxPlayers int
MiniGameName *string
MiniGameID *int
MinigameImage *string
ActiveSlot int
//Slots interface{}
Member bool
}
// Worlds return a list of servers that the user is invited to or owns.
func (r *Realms) Worlds() ([]Server, error) {
var resp struct {
Servers []Server
*Error
}
err := r.get("/worlds", &resp)
if err != nil {
return nil, err
}
if resp.Error != nil {
err = resp.Error
}
return resp.Servers, err
}
// Server returns a single server listing about a server.
// you must be the owner of the server.
func (r *Realms) Server(ID int) (s Server, err error) {
var resp = struct {
*Server
*Error
}{Server: &s}
err = r.get(fmt.Sprintf("/worlds/%d", ID), &resp)
if err != nil {
return
}
if resp.Error != nil {
err = resp.Error
}
return
}
// Address used to get the IP address for a server.
// Call TOS before you call this function.
func (r *Realms) Address(s Server) (string, error) {
var resp struct {
Address string
PendingUpdate bool
ResourcePackUrl *string
ResourcePackHash *string
*Error
}
err := r.get(fmt.Sprintf("/worlds/v1/%d/join/pc", s.ID), &resp)
if err != nil {
return "", err
}
if resp.Error != nil {
err = resp.Error
return "", err
}
if resp.PendingUpdate {
return "", errors.New("pending update")
}
return resp.Address, err
}
// Backups returns a list of backups for the world.
func (r *Realms) Backups(s Server) ([]int, error) {
var bs []int
err := r.get(fmt.Sprintf("/worlds/%d/backups", s.ID), &bs)
return bs, err
}
//func (r *Realms) Download() (link, resURL, resHash string) {
// var resp struct {
// DownloadLink string
// ResourcePackURL *string
// ResourcePackHash *string
// *Error
// }
// TODO: What's the $WORLD(1-4) means?
// err := r.get(fmt.Sprintf("/worlds/$ID/slot/$WORLD(1-4)/download", s.ID), &resp)
//
//}
// Ops returns a list of operators for this server.
// You must own this server to view this.
func (r *Realms) Ops(s Server) (ops []string, err error) {
err = r.get(fmt.Sprintf("/ops/%d", s.ID), &ops)
return
}
// SubscriptionLife returns the current life of a server subscription.
func (r *Realms) SubscriptionLife(s Server) (startDate int64, daysLeft int, Type string, err error) {
var resp = struct {
StartDate *int64
DaysLeft *int
SubscriptionType *string
}{
StartDate: &startDate,
DaysLeft: &daysLeft,
SubscriptionType: &Type,
}
err = r.get(fmt.Sprintf("/subscriptions/%d", s.ID), &resp)
return
}