132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
// ABOUTME: Message dispatch for incoming client control frames
|
|
// ABOUTME: Routes client/time, client/state, client/goodbye, client/command to handlers
|
|
package sendspin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
|
|
"github.com/Sendspin/sendspin-go/pkg/protocol"
|
|
)
|
|
|
|
func (s *Server) handleClientMessage(c *ServerClient, data []byte) {
|
|
var msg protocol.Message
|
|
if err := json.Unmarshal(data, &msg); err != nil {
|
|
log.Printf("Error unmarshaling message: %v", err)
|
|
return
|
|
}
|
|
|
|
switch msg.Type {
|
|
case "client/time":
|
|
s.handleTimeSync(c, msg.Payload)
|
|
case "client/state":
|
|
s.handleClientState(c, msg.Payload)
|
|
case "client/goodbye":
|
|
s.handleClientGoodbye(c, msg.Payload)
|
|
case "client/command":
|
|
s.handleClientCommand(c, msg.Payload)
|
|
default:
|
|
if s.config.Debug {
|
|
log.Printf("Unknown message type: %s", msg.Type)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleTimeSync(c *ServerClient, payload interface{}) {
|
|
serverRecv := s.getClockMicros()
|
|
|
|
timeData, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var clientTime protocol.ClientTime
|
|
if err := json.Unmarshal(timeData, &clientTime); err != nil {
|
|
return
|
|
}
|
|
|
|
serverSend := s.getClockMicros()
|
|
|
|
response := protocol.ServerTime{
|
|
ClientTransmitted: clientTime.ClientTransmitted,
|
|
ServerReceived: serverRecv,
|
|
ServerTransmitted: serverSend,
|
|
}
|
|
|
|
if err := c.Send("server/time", response); err != nil {
|
|
if s.config.Debug {
|
|
log.Printf("Error sending server/time to %s: %v", c.name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// handleClientState applies a client's player state update per spec.
|
|
func (s *Server) handleClientState(c *ServerClient, payload interface{}) {
|
|
stateData, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var stateMsg protocol.ClientStateMessage
|
|
if err := json.Unmarshal(stateData, &stateMsg); err != nil {
|
|
return
|
|
}
|
|
|
|
if stateMsg.Player != nil {
|
|
c.mu.Lock()
|
|
c.state = stateMsg.Player.State
|
|
c.volume = stateMsg.Player.Volume
|
|
c.muted = stateMsg.Player.Muted
|
|
c.mu.Unlock()
|
|
|
|
if s.config.Debug {
|
|
log.Printf("Client %s state: %s (vol: %d, muted: %v)", c.name, stateMsg.Player.State, stateMsg.Player.Volume, stateMsg.Player.Muted)
|
|
}
|
|
|
|
s.defaultGroup.publish(ClientStateChangedEvent{
|
|
Client: c,
|
|
State: stateMsg.Player.State,
|
|
Volume: stateMsg.Player.Volume,
|
|
Muted: stateMsg.Player.Muted,
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleClientCommand(c *ServerClient, payload interface{}) {
|
|
data, err := json.Marshal(payload)
|
|
if err != nil {
|
|
log.Printf("Error marshaling client/command payload: %v", err)
|
|
return
|
|
}
|
|
|
|
// client/command payloads are role-keyed: {"controller": {...}, "player": {...}}
|
|
var rolePayloads map[string]json.RawMessage
|
|
if err := json.Unmarshal(data, &rolePayloads); err != nil {
|
|
log.Printf("Error unmarshaling client/command: %v", err)
|
|
return
|
|
}
|
|
|
|
for roleFamily, roleData := range rolePayloads {
|
|
if err := s.defaultGroup.RouteMessage(c, roleFamily, roleData); err != nil {
|
|
if s.config.Debug {
|
|
log.Printf("client/command routing error for role %s: %v", roleFamily, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleClientGoodbye(c *ServerClient, payload interface{}) {
|
|
goodbyeData, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var goodbye protocol.ClientGoodbye
|
|
if err := json.Unmarshal(goodbyeData, &goodbye); err != nil {
|
|
return
|
|
}
|
|
|
|
log.Printf("Client %s goodbye: %s", c.name, goodbye.Reason)
|
|
// Connection close happens in handleConnection's read loop once this returns.
|
|
}
|