Calls & objects
How a method invocation is assembled and how the Core’s replies and pushes are decoded.
Ported in src/proto/{remoting,serializer,objects}.ts.
Method calls (CALL, cmd 3)
Section titled “Method calls (CALL, cmd 3)”CALL body = flexLong(objectId) + flexInt(methodId) + args-
objectId— the server-assigned object you’re calling on (from aPUSHOBJ, or a service resolved viaGETSVC). -
methodId— a client-assigned integer. The first time you use one, the client auto-sends aDEFMETHOD(cmd 6) declaring it:DEFMETHOD = flexInt(methodId) + string(signature)The Core maps the id to a real method by the signature string — so you choose the ids; the server resolves them by name.
A response body is string(status) + returnValue. The status "Success" (and "") means
OK — isSuccessStatus() encapsulates that. A method with no callback parameter is
fire-and-forget: no request id, no response (callMethodNoReply).
Method signature strings
Section titled “Method signature strings”Sooloos.Broker.Api.{Interface}::{Method}({fullyQualifiedArgTypes})Type-name mapping (matched the 67 signatures I had captures to check against):
- Primitives keep the C# keyword:
string,bool,double,long,int. Sooid→System.Sooid;byte[]→System.Byte[].ResultCallback→Base.ResultCallback(keeps its generic, e.g.ResultCallback<PlayFeedback>).- Collections →
System.Collections.Generic.*. - Everything else →
Sooloos.Broker.Api.{T}.
Built by formatMethodSignature() in src/catalog/signature.ts.
Argument encoding (serializer.ts)
Section titled “Argument encoding (serializer.ts)”Arguments are positional; each non-callback parameter is written in order by its kind:
| Param kind | Encoding |
|---|---|
Sooid | sooid(bytes) |
| primitive | its primitive writer |
enum | flexInt(value) |
by-ref object (interfaces: Zone, Album, Track…) | flexLong(objectId) |
by-val struct ([ByVal] classes: PlayParameters, *QueryCriteria…) | inline value object (below) |
IEnumerable<ref> | flexInt(count) + flexLong(oid)* |
ResultCallback | omitted from the wire |
By-val struct arguments
Section titled “By-val struct arguments”A by-val struct is sent as an inline value object:
flexLong(1) + flexInt(clientTypeId) + flexInt(len) + sparseFieldsYou must declare the type once with DEFTYPE (cmd 5):
DEFTYPE = flexInt(typeId) + string(name) + flexInt(count) + [ string(memberName) + flexInt(propType) ] * countThe server matches members by name, so sending a subset (or none) is legal — you
only declare the fields you set. RoonClient.structArg(typeName, fields) does this for you;
inlineStruct() builds the bytes.
The object graph (responses & pushes)
Section titled “The object graph (responses & pushes)”The Core describes types, then streams objects. Decoded by ObjectGraph in
src/proto/objects.ts.
Type definitions (DEFTYPE, cmd 7 server-side):
flexInt(typeId) + string(name) + flexInt(count) + [ string(member) + flexInt(PropertyType) ] * countObject pushes (PUSHOBJ / UPDATEOBJ / PUSHSTUB):
flexLong(oid) + flexInt(typeId) + fieldsFields are sparse:
( flexInt memberIndex (1-based), value )* then flexInt 0Each value is read per that member’s PropertyType. The Object property type is a
flexLong: 0 = null, 1 = an inline value object (typeId, len, sparse fields),
anything else = an object-id reference.
Collections (DataList<T> / Query<T>): no declared members; the body is (1, count, 0) followed by count items, each using the Object encoding above.
Summary
Section titled “Summary”| Concept | Who assigns | How referenced |
|---|---|---|
| Method id | Client | declared once via DEFMETHOD, matched by signature |
| By-val type id | Client | declared once via DEFTYPE, matched by member name |
| Object id | Server | handed out via PUSHOBJ; ephemeral per session |
With these three encodings — calls, by-val structs, and the sparse object graph — the entire 1550-method surface is reachable. The generated client builds all of it from the catalog automatically.