realms support
This commit is contained in:
2
.idea/dictionaries/cjd00.xml
generated
2
.idea/dictionaries/cjd00.xml
generated
@ -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>
|
||||
|
@ -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
13
realms/invite.go
Normal 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
36
realms/mco.go
Normal 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
88
realms/realms.go
Normal 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
36
realms/realms_test.go
Normal 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
139
realms/server.go
Normal 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
|
||||
}
|
Reference in New Issue
Block a user