442 lines
8.3 KiB
Go
442 lines
8.3 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/anon55555/mt/rudp"
|
|
)
|
|
|
|
const BytesPerBunch = 5000
|
|
|
|
var media map[string]*mediaFile
|
|
var nodedefs map[string][]byte
|
|
var itemdefs map[string][]byte
|
|
var detachedinvs map[string][][]byte
|
|
|
|
type mediaFile struct {
|
|
digest []byte
|
|
data []byte
|
|
noCache bool
|
|
}
|
|
|
|
func (c *Conn) fetchMedia() {
|
|
if !c.IsSrv() {
|
|
return
|
|
}
|
|
|
|
for {
|
|
pkt, err := c.Recv()
|
|
if err != nil {
|
|
if errors.Is(err, net.ErrClosed) {
|
|
return
|
|
}
|
|
|
|
go func() {
|
|
<-LogReady()
|
|
log.Print(err)
|
|
}()
|
|
continue
|
|
}
|
|
|
|
r := ByteReader(pkt)
|
|
|
|
switch cmd := ReadUint16(r); cmd {
|
|
case ToClientNodeDef:
|
|
servers := ConfKey("servers").(map[interface{}]interface{})
|
|
var srvname string
|
|
for server := range servers {
|
|
if ConfKey("servers:"+server.(string)+":address") == c.Addr().String() {
|
|
srvname = server.(string)
|
|
break
|
|
}
|
|
}
|
|
|
|
r.Seek(6, io.SeekStart)
|
|
|
|
nodedefs[srvname] = make([]byte, r.Len())
|
|
r.Read(nodedefs[srvname])
|
|
case ToClientItemDef:
|
|
servers := ConfKey("servers").(map[interface{}]interface{})
|
|
var srvname string
|
|
for server := range servers {
|
|
if ConfKey("servers:"+server.(string)+":address") == c.Addr().String() {
|
|
srvname = server.(string)
|
|
break
|
|
}
|
|
}
|
|
|
|
r.Seek(6, io.SeekStart)
|
|
|
|
itemdefs[srvname] = make([]byte, r.Len())
|
|
r.Read(itemdefs[srvname])
|
|
case ToClientDetachedInventory:
|
|
servers := ConfKey("servers").(map[interface{}]interface{})
|
|
var srvname string
|
|
for server := range servers {
|
|
if ConfKey("servers:"+server.(string)+":address") == c.Addr().String() {
|
|
srvname = server.(string)
|
|
break
|
|
}
|
|
}
|
|
|
|
inv := make([]byte, r.Len())
|
|
r.Read(inv)
|
|
|
|
detachedinvs[srvname] = append(detachedinvs[srvname], inv)
|
|
case ToClientAnnounceMedia:
|
|
var rq []string
|
|
|
|
count := ReadUint16(r)
|
|
|
|
for i := uint16(0); i < count; i++ {
|
|
name := string(ReadBytes16(r))
|
|
|
|
digest := ReadBytes16(r)
|
|
|
|
if media[name] == nil && !isCached(name, digest) {
|
|
rq = append(rq, name)
|
|
media[name] = &mediaFile{digest: digest}
|
|
}
|
|
}
|
|
|
|
// Request the media
|
|
pktlen := 0
|
|
for f := range rq {
|
|
pktlen += 2 + len(rq[f])
|
|
}
|
|
|
|
w := bytes.NewBuffer([]byte{0x00, ToServerRequestMedia})
|
|
WriteUint16(w, uint16(len(rq)))
|
|
for f := range rq {
|
|
WriteBytes16(w, []byte(rq[f]))
|
|
}
|
|
|
|
_, err := c.Send(rudp.Pkt{
|
|
Reader: w,
|
|
PktInfo: rudp.PktInfo{
|
|
Channel: 1,
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
go func() {
|
|
<-LogReady()
|
|
log.Print(err)
|
|
}()
|
|
continue
|
|
}
|
|
case ToClientMedia:
|
|
bunchCount := ReadUint16(r)
|
|
bunchID := ReadUint16(r)
|
|
fileCount := ReadUint32(r)
|
|
|
|
for i := uint32(0); i < fileCount; i++ {
|
|
name := string(ReadBytes16(r))
|
|
data := ReadBytes32(r)
|
|
|
|
if media[name] != nil && len(media[name].data) == 0 {
|
|
media[name].data = data
|
|
}
|
|
}
|
|
|
|
if bunchID >= bunchCount-1 {
|
|
c.Close()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Conn) updateDetachedInvs(srvname string) {
|
|
for i := range detachedinvs[srvname] {
|
|
w := bytes.NewBuffer([]byte{0x00, ToClientDetachedInventory})
|
|
w.Write(detachedinvs[srvname][i])
|
|
|
|
ack, err := c.Send(rudp.Pkt{Reader: w})
|
|
if err != nil {
|
|
log.Print(err)
|
|
continue
|
|
}
|
|
<-ack
|
|
}
|
|
}
|
|
|
|
func (c *Conn) announceMedia() {
|
|
srvname, ok := ConfKey("default_server").(string)
|
|
if !ok {
|
|
log.Print("Default server name not set or not a string")
|
|
return
|
|
}
|
|
|
|
data := make([]byte, 6+len(nodedef))
|
|
data[0] = uint8(0x00)
|
|
data[1] = uint8(ToClientNodeDef)
|
|
binary.BigEndian.PutUint32(data[2:6], uint32(len(nodedef)))
|
|
copy(data[6:], nodedef)
|
|
|
|
ack, err := c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
|
|
if err != nil {
|
|
log.Print(err)
|
|
}
|
|
<-ack
|
|
|
|
data = make([]byte, 6+len(itemdef))
|
|
data[0] = uint8(0x00)
|
|
data[1] = uint8(ToClientItemDef)
|
|
binary.BigEndian.PutUint32(data[2:6], uint32(len(itemdef)))
|
|
copy(data[6:], itemdef)
|
|
|
|
ack, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
|
|
if err != nil {
|
|
log.Print(err)
|
|
}
|
|
<-ack
|
|
|
|
c.updateDetachedInvs(srvname)
|
|
|
|
csmrf, ok := ConfKey("csm_restriction_flags").(int)
|
|
if !ok {
|
|
csmrf = 0
|
|
}
|
|
|
|
csmnr, ok := ConfKey("csm_restriction_noderange").(int)
|
|
if !ok {
|
|
csmnr = 8
|
|
}
|
|
|
|
data = make([]byte, 14)
|
|
data[0] = uint8(0x00)
|
|
data[1] = uint8(ToClientCSMRestrictionFlags)
|
|
binary.BigEndian.PutUint32(data[2:6], uint32(0))
|
|
binary.BigEndian.PutUint32(data[6:10], uint32(csmrf))
|
|
binary.BigEndian.PutUint32(data[10:], uint32(csmnr))
|
|
|
|
ack, err = c.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
|
|
if err != nil {
|
|
log.Print(err)
|
|
}
|
|
<-ack
|
|
|
|
w := bytes.NewBuffer([]byte{0x00, ToClientAnnounceMedia})
|
|
WriteUint16(w, uint16(len(media)))
|
|
for f := range media {
|
|
WriteBytes16(w, []byte(f))
|
|
WriteBytes16(w, media[f].digest)
|
|
}
|
|
|
|
remote, ok := ConfKey("remote_media_server").(string)
|
|
if !ok {
|
|
remote = ""
|
|
}
|
|
WriteBytes16(w, []byte(remote))
|
|
|
|
ack, err = c.Send(rudp.Pkt{Reader: w})
|
|
if err != nil {
|
|
log.Print(err)
|
|
return
|
|
}
|
|
<-ack
|
|
}
|
|
|
|
func (c *Conn) sendMedia(r *bytes.Reader) {
|
|
count := ReadUint16(r)
|
|
|
|
var rq []string
|
|
for i := uint16(0); i < count; i++ {
|
|
name := string(ReadBytes16(r))
|
|
rq = append(rq, name)
|
|
}
|
|
|
|
bunches := []map[string]*mediaFile{make(map[string]*mediaFile)}
|
|
var bunchlen int
|
|
for _, f := range rq {
|
|
bunches[len(bunches)-1][f] = media[f]
|
|
bunchlen += len(media[f].data)
|
|
|
|
if bunchlen >= BytesPerBunch {
|
|
bunches = append(bunches, make(map[string]*mediaFile))
|
|
bunchlen = 0
|
|
}
|
|
}
|
|
|
|
for i, bunch := range bunches {
|
|
w := bytes.NewBuffer([]byte{0x00, ToClientMedia})
|
|
WriteUint16(w, uint16(len(bunches)))
|
|
WriteUint16(w, uint16(i))
|
|
WriteUint32(w, uint32(len(bunch)))
|
|
for f, m := range bunch {
|
|
WriteBytes16(w, []byte(f))
|
|
WriteBytes32(w, m.data)
|
|
}
|
|
|
|
ack, err := c.Send(rudp.Pkt{
|
|
Reader: w,
|
|
PktInfo: rudp.PktInfo{
|
|
Channel: 2,
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
log.Print(err)
|
|
return
|
|
}
|
|
<-ack
|
|
}
|
|
}
|
|
|
|
func loadMediaCache() error {
|
|
os.Mkdir("cache", 0777)
|
|
|
|
files, err := os.ReadDir("cache")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, file := range files {
|
|
if !file.IsDir() {
|
|
meta := strings.Split(file.Name(), "#")
|
|
if len(meta) != 2 {
|
|
os.Remove("cache/" + file.Name())
|
|
continue
|
|
}
|
|
|
|
data, err := os.ReadFile("cache/" + file.Name())
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
media[meta[0]] = &mediaFile{digest: stringToDigest(meta[1]), data: data}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func isCached(name string, digest []byte) bool {
|
|
os.Mkdir("cache", 0777)
|
|
|
|
_, err := os.Stat("cache/" + name + "#" + digestToString(digest))
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func updateMediaCache() {
|
|
os.Mkdir("cache", 0777)
|
|
|
|
for mfname, mfile := range media {
|
|
if mfile.noCache {
|
|
continue
|
|
}
|
|
|
|
cfname := "cache/" + mfname + "#" + digestToString(mfile.digest)
|
|
_, err := os.Stat(cfname)
|
|
if os.IsNotExist(err) {
|
|
os.WriteFile(cfname, mfile.data, 0666)
|
|
}
|
|
}
|
|
}
|
|
|
|
func digestToString(d []byte) string {
|
|
return hex.EncodeToString(d)
|
|
}
|
|
|
|
func stringToDigest(s string) []byte {
|
|
r, err := hex.DecodeString(s)
|
|
if err != nil {
|
|
return []byte{}
|
|
}
|
|
return r
|
|
}
|
|
|
|
func loadMedia(servers map[string]struct{}) {
|
|
log.Print("Fetching media")
|
|
|
|
media = make(map[string]*mediaFile)
|
|
detachedinvs = make(map[string][][]byte)
|
|
|
|
loadMediaCache()
|
|
|
|
for server := range servers {
|
|
straddr := ConfKey("servers:" + server + ":address")
|
|
|
|
srvaddr, err := net.ResolveUDPAddr("udp", straddr.(string))
|
|
if err != nil {
|
|
go func() {
|
|
<-LogReady()
|
|
log.Fatal(err)
|
|
}()
|
|
}
|
|
|
|
conn, err := net.DialUDP("udp", nil, srvaddr)
|
|
if err != nil {
|
|
go func() {
|
|
<-LogReady()
|
|
log.Fatal(err)
|
|
}()
|
|
}
|
|
|
|
srv, err := Connect(conn)
|
|
if err != nil {
|
|
go func() {
|
|
<-LogReady()
|
|
log.Print(err)
|
|
}()
|
|
continue
|
|
}
|
|
|
|
clt := &Conn{username: "media"}
|
|
|
|
fin := make(chan *Conn) // close-only
|
|
go Init(clt, srv, false, true, fin)
|
|
<-fin
|
|
|
|
srv.fetchMedia()
|
|
}
|
|
|
|
if err := mergeNodedefs(nodedefs); err != nil {
|
|
go func() {
|
|
<-LogReady()
|
|
log.Fatal(err)
|
|
}()
|
|
}
|
|
|
|
if err := mergeItemdefs(itemdefs); err != nil {
|
|
go func() {
|
|
<-LogReady()
|
|
log.Fatal(err)
|
|
}()
|
|
}
|
|
|
|
updateMediaCache()
|
|
}
|
|
|
|
func init() {
|
|
nodedefs = make(map[string][]byte)
|
|
itemdefs = make(map[string][]byte)
|
|
|
|
servers, ok := ConfKey("servers").(map[interface{}]interface{})
|
|
if !ok {
|
|
go func() {
|
|
<-LogReady()
|
|
log.Fatal("Server list inexistent or not a dictionary")
|
|
}()
|
|
}
|
|
|
|
srvs := make(map[string]struct{})
|
|
for server := range servers {
|
|
srvs[server.(string)] = struct{}{}
|
|
}
|
|
|
|
loadMedia(srvs)
|
|
}
|