Files
arpack/README.md
T
2026-03-19 15:16:04 +03:00

147 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<p align="center">
<img src="images/logo.png" alt="arpack logo" width="240">
</p>
# ArPack
Binary serialization code generator for Go and C#. Define messages once as Go structs — get zero-allocation `Marshal`/`Unmarshal` for Go and `unsafe` pointer-based `Serialize`/`Deserialize` for C#.
Built for game networking where every byte and allocation matters.
## Features
- **Single source of truth** — define messages in Go, generate both Go and C# code
- **Float quantization** — compress `float32`/`float64` to 8 or 16 bits with a `pack` struct tag
- **Boolean packing** — consecutive `bool` fields are packed into single bytes (up to 8 per byte)
- **Enums** — `type Opcode uint16` + `const` block becomes a C# `enum`
- **Nested types, fixed arrays, slices** — full support for complex message structures
- **Cross-language binary compatibility** — Go and C# produce identical wire formats
## Installation
```bash
go install edmand46/arpack/cmd/arpack@latest
```
## Usage
```bash
arpack -in messages.go -out-go ./gen -out-cs ../Unity/Assets/Scripts
```
| Flag | Description |
|---|---|
| `-in` | Input Go file with struct definitions (required) |
| `-out-go` | Output directory for generated Go code |
| `-out-cs` | Output directory for generated C# code |
| `-cs-namespace` | C# namespace (default: `Arpack.Messages`) |
At least one of `-out-go` or `-out-cs` is required.
**Output files:**
- Go: `{name}_gen.go`
- C#: `{Name}.gen.cs`
## Schema Definition
Messages are defined as Go structs in a single `.go` file:
```go
package messages
// Quantized 3D vector — 6 bytes instead of 12
type Vector3 struct {
X float32 `pack:"min=-500,max=500,bits=16"`
Y float32 `pack:"min=-500,max=500,bits=16"`
Z float32 `pack:"min=-500,max=500,bits=16"`
}
// Enum
type Opcode uint16
const (
OpcodeUnknown Opcode = iota
OpcodeAuthorize
OpcodeJoinRoom
)
type MoveMessage struct {
Position Vector3 // nested type
Velocity [3]float32 // fixed-length array
Waypoints []Vector3 // variable-length slice
PlayerID uint32
Active bool // 3 consecutive bools →
Visible bool // packed into 1 byte
Ghost bool
Name string
}
```
### Supported Types
| Type | Wire Size |
|---|---|
| `bool` (packed) | 1 bit (up to 8 per byte) |
| `int8`, `uint8` | 1 byte |
| `int16`, `uint16` | 2 bytes |
| `int32`, `uint32`, `float32` | 4 bytes |
| `int64`, `uint64`, `float64` | 8 bytes |
| `string` | 2-byte length prefix + UTF-8 |
| `[N]T` | N × sizeof(T) |
| `[]T` | 2-byte length prefix + N × sizeof(T) |
### Float Quantization
Use the `pack` struct tag to compress floats:
```go
X float32 `pack:"min=-500,max=500,bits=16"` // 2 bytes instead of 4
Y float32 `pack:"min=0,max=1,bits=8"` // 1 byte instead of 4
```
| Parameter | Description |
|---|---|
| `min` | Minimum expected value |
| `max` | Maximum expected value |
| `bits` | Target size: `8` (uint8) or `16` (uint16) |
Values are linearly mapped: `encoded = (value - min) / (max - min) * maxUint`.
## Generated Code
### Go
```go
func (m *MoveMessage) Marshal(buf []byte) []byte
func (m *MoveMessage) Unmarshal(data []byte) (int, error)
```
`Marshal` appends to the buffer and returns it. `Unmarshal` reads from the buffer and returns bytes consumed.
### C#
```csharp
public unsafe int Serialize(byte* buffer)
public static unsafe int Deserialize(byte* buffer, out MoveMessage msg)
```
Uses unsafe pointers for zero-copy serialization. Returns bytes written/consumed.
## Wire Format
- Little-endian byte order
- No message framing — fields are written in declaration order
- Variable-length fields (`string`, `[]T`) prefixed with `uint16` length
- Booleans packed as bitfields (LSB first, up to 8 per byte)
- Quantized floats stored as `uint8` or `uint16`
## Running Tests
```bash
# Unit tests (parser + generator)
go test ./parser/... ./generator/...
# End-to-end cross-language tests (requires dotnet SDK)
go test ./e2e/...
```