Refine Go runtime behavior and log handling
Some checks failed
build-windows-exe / build (push) Failing after 48s
Some checks failed
build-windows-exe / build (push) Failing after 48s
This commit is contained in:
141
VRWT_Tool/VRC_OSC/internal/osc/server.go
Normal file
141
VRWT_Tool/VRC_OSC/internal/osc/server.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package osc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Value struct {
|
||||
Type byte
|
||||
Float float32
|
||||
Int int32
|
||||
Bool bool
|
||||
Str string
|
||||
}
|
||||
|
||||
type Handler func(address string, args []Value) error
|
||||
|
||||
type Server struct {
|
||||
addr string
|
||||
conn *net.UDPConn
|
||||
handlers map[string]Handler
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewServer(host string, port int) *Server {
|
||||
return &Server{
|
||||
addr: fmt.Sprintf("%s:%d", host, port),
|
||||
handlers: map[string]Handler{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) Map(param string, h Handler) { s.handlers["/avatar/parameters/"+param] = h }
|
||||
func (s *Server) Close() error {
|
||||
if s.conn != nil {
|
||||
return s.conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Serve() error {
|
||||
addr, err := net.ResolveUDPAddr("udp", s.addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.conn = conn
|
||||
buf := make([]byte, 2048)
|
||||
for {
|
||||
n, _, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a, v, err := parseMessage(buf[:n])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
s.mu.RLock()
|
||||
h := s.handlers[a]
|
||||
s.mu.RUnlock()
|
||||
if h != nil {
|
||||
_ = h(a, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseMessage(b []byte) (string, []Value, error) {
|
||||
address, offset, err := parsePaddedString(b, 0)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if !strings.HasPrefix(address, "/") {
|
||||
return "", nil, fmt.Errorf("invalid address")
|
||||
}
|
||||
if offset >= len(b) {
|
||||
return address, nil, nil
|
||||
}
|
||||
typetags, offset, err := parsePaddedString(b, offset)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if !strings.HasPrefix(typetags, ",") {
|
||||
return address, nil, nil
|
||||
}
|
||||
var values []Value
|
||||
for _, tag := range typetags[1:] {
|
||||
switch tag {
|
||||
case 'i':
|
||||
if offset+4 > len(b) {
|
||||
return "", nil, fmt.Errorf("invalid int")
|
||||
}
|
||||
values = append(values, Value{Type: 'i', Int: int32(binary.BigEndian.Uint32(b[offset : offset+4]))})
|
||||
offset += 4
|
||||
case 'f':
|
||||
if offset+4 > len(b) {
|
||||
return "", nil, fmt.Errorf("invalid float")
|
||||
}
|
||||
bits := binary.BigEndian.Uint32(b[offset : offset+4])
|
||||
values = append(values, Value{Type: 'f', Float: math.Float32frombits(bits)})
|
||||
offset += 4
|
||||
case 's':
|
||||
s, next, err := parsePaddedString(b, offset)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
values = append(values, Value{Type: 's', Str: s})
|
||||
offset = next
|
||||
case 'T':
|
||||
values = append(values, Value{Type: 'T', Bool: true})
|
||||
case 'F':
|
||||
values = append(values, Value{Type: 'F', Bool: false})
|
||||
}
|
||||
}
|
||||
return address, values, nil
|
||||
}
|
||||
|
||||
func parsePaddedString(b []byte, offset int) (string, int, error) {
|
||||
if offset >= len(b) {
|
||||
return "", offset, fmt.Errorf("invalid osc string")
|
||||
}
|
||||
end := bytes.IndexByte(b[offset:], 0)
|
||||
if end < 0 {
|
||||
return "", offset, fmt.Errorf("invalid osc string")
|
||||
}
|
||||
s := string(b[offset : offset+end])
|
||||
next := offset + end + 1
|
||||
for next%4 != 0 {
|
||||
next++
|
||||
}
|
||||
if next > len(b) {
|
||||
next = len(b)
|
||||
}
|
||||
return s, next, nil
|
||||
}
|
||||
Reference in New Issue
Block a user