Something went wrong. Try again.
A native-speed rewrite of the roblox-ts compiler in golang
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281package api
import ( "bufio" "encoding/binary" "fmt" "io"
"rotor/tsgo/json" "rotor/tsgo/jsonrpc")
// MessageType represents the type of message in the msgpack protocol.type MessageType uint8
const ( MessageTypeUnknown MessageType = iota MessageTypeRequest MessageTypeCallResponse MessageTypeCallError MessageTypeResponse MessageTypeError MessageTypeCall)
func (m MessageType) IsValid() bool { return m >= MessageTypeRequest && m <= MessageTypeCall}
// MessagePack format constantsconst ( msgpackFixedArray3 byte = 0x93 msgpackBin8 byte = 0xC4 msgpackBin16 byte = 0xC5 msgpackBin32 byte = 0xC6 msgpackU8 byte = 0xCC)
// MessagePackProtocol implements the Protocol interface using a custom// msgpack-based tuple format: [MessageType, method, payload].type MessagePackProtocol struct { r *bufio.Reader w *bufio.Writer}
var _ Protocol = (*MessagePackProtocol)(nil)
// NewMessagePackProtocol creates a new msgpack protocol handler.func NewMessagePackProtocol(rw io.ReadWriter) *MessagePackProtocol { return &MessagePackProtocol{ r: bufio.NewReader(rw), w: bufio.NewWriter(rw), }}
// ReadMessage implements Protocol.func (p *MessagePackProtocol) ReadMessage() (*Message, error) { msgType, method, payload, err := p.readTuple() if err != nil { return nil, err }
// Convert msgpack message type to JSON-RPC message msg := &Message{}
switch msgType { case MessageTypeRequest: // Client request - needs an ID for response // We use the method as a pseudo-ID since this protocol doesn't have explicit IDs id := jsonrpc.NewIDString(method) msg.ID = id msg.Method = method msg.Params = payload case MessageTypeCallResponse: // Response to our Call - use method as ID // Note: Method must be empty for IsResponse() to return true id := jsonrpc.NewIDString(method) msg.ID = id msg.Result = payload case MessageTypeCallError: // Error response to our Call // Note: Method must be empty for IsResponse() to return true id := jsonrpc.NewIDString(method) msg.ID = id msg.Error = &jsonrpc.ResponseError{ Code: jsonrpc.CodeInternalError, Message: string(payload), } default: return nil, fmt.Errorf("unexpected message type: %d", msgType) }
return msg, nil}
func (p *MessagePackProtocol) readTuple() (MessageType, string, []byte, error) { // Read fixed array marker (0x93 = 3-element array) t, err := p.r.ReadByte() if err != nil { return 0, "", nil, err } if t != msgpackFixedArray3 { return 0, "", nil, fmt.Errorf("%w: expected fixed 3-element array (0x93), received: 0x%02x", ErrInvalidRequest, t) }
// Read message type - can be positive fixint (0x00-0x7F) or uint8 (0xCC + value) t, err = p.r.ReadByte() if err != nil { return 0, "", nil, err } var rawType byte if t <= 0x7F { // Positive fixint - the byte IS the value rawType = t } else if t == msgpackU8 { // uint8 marker - next byte is the value rawType, err = p.r.ReadByte() if err != nil { return 0, "", nil, err } } else { return 0, "", nil, fmt.Errorf("%w: expected positive fixint or uint8 marker, received: 0x%02x", ErrInvalidRequest, t) } msgType := MessageType(rawType) if !msgType.IsValid() { return 0, "", nil, fmt.Errorf("%w: unknown message type: %d", ErrInvalidRequest, msgType) }
// Read method (binary) methodBytes, err := p.readBin() if err != nil { return 0, "", nil, err } method := string(methodBytes)
// Read payload (binary) payload, err := p.readBin() if err != nil { return 0, "", nil, err }
return msgType, method, payload, nil}
func (p *MessagePackProtocol) readBin() ([]byte, error) { t, err := p.r.ReadByte() if err != nil { return nil, err }
var size uint switch t { case msgpackBin8: var size8 uint8 if err = binary.Read(p.r, binary.BigEndian, &size8); err != nil { return nil, err } size = uint(size8) case msgpackBin16: var size16 uint16 if err = binary.Read(p.r, binary.BigEndian, &size16); err != nil { return nil, err } size = uint(size16) case msgpackBin32: var size32 uint32 if err = binary.Read(p.r, binary.BigEndian, &size32); err != nil { return nil, err } size = uint(size32) default: return nil, fmt.Errorf("%w: expected binary data (0xc4-0xc6), received: 0x%02x", ErrInvalidRequest, t) }
payload := make([]byte, size) if _, err := io.ReadFull(p.r, payload); err != nil { return nil, err } return payload, nil}
// WriteRequest implements Protocol.func (p *MessagePackProtocol) WriteRequest(id *jsonrpc.ID, method string, params any) error { // For msgpack protocol, requests from server are "Call" type payload, err := json.Marshal(params) if err != nil { return err } return p.writeTuple(MessageTypeCall, method, payload)}
// WriteNotification implements Protocol.func (p *MessagePackProtocol) WriteNotification(method string, params any) error { // Msgpack protocol doesn't distinguish notifications from calls return p.WriteRequest(nil, method, params)}
// WriteResponse implements Protocol.func (p *MessagePackProtocol) WriteResponse(id *jsonrpc.ID, result any) error { method := "" if id != nil { method = id.String() }
var payload []byte var err error
// Check if result is raw binary (for efficient binary transport) if raw, ok := result.(RawBinary); ok { payload = []byte(raw) } else { payload, err = json.Marshal(result) if err != nil { return err } }
return p.writeTuple(MessageTypeResponse, method, payload)}
// WriteError implements Protocol.func (p *MessagePackProtocol) WriteError(id *jsonrpc.ID, respErr *jsonrpc.ResponseError) error { method := "" if id != nil { method = id.String() } return p.writeTuple(MessageTypeError, method, []byte(respErr.Message))}
func (p *MessagePackProtocol) writeTuple(msgType MessageType, method string, payload []byte) error { // Write fixed array marker if err := p.w.WriteByte(msgpackFixedArray3); err != nil { return err } // Write message type as positive fixint (values 0-127 are written directly) if err := p.w.WriteByte(byte(msgType)); err != nil { return err } // Write method if err := p.writeBin([]byte(method)); err != nil { return err } // Write payload if err := p.writeBin(payload); err != nil { return err } return p.w.Flush()}
func (p *MessagePackProtocol) writeBin(data []byte) error { length := len(data) if length < 256 { if err := p.w.WriteByte(msgpackBin8); err != nil { return err } if err := p.w.WriteByte(byte(length)); err != nil { return err } } else if length < 1<<16 { if err := p.w.WriteByte(msgpackBin16); err != nil { return err } if err := binary.Write(p.w, binary.BigEndian, uint16(length)); err != nil { return err } } else { if err := p.w.WriteByte(msgpackBin32); err != nil { return err } if err := binary.Write(p.w, binary.BigEndian, uint32(length)); err != nil { return err } } _, err := p.w.Write(data) return err}
// RawBinary is a marker type for binary data that should be written// directly by MessagePackProtocol instead of being JSON-encoded.type RawBinary []byte