fill out readme
This commit is contained in:
parent
6d57815a14
commit
c5d5998c8a
333
README.md
333
README.md
@ -2,4 +2,335 @@
|
||||
|
||||
This library is meant to replicate EverQuest 2's UDP protocol. The work here is based on the immense
|
||||
work done by contributors to the EQ2Emu project. I am porting it to Go with the idea of a simpler, faster
|
||||
development pipeline - and as a personal experiment.
|
||||
development pipeline - and as a personal experiment.
|
||||
|
||||
## Opcodes
|
||||
|
||||
The opcodes package manages the mapping between internal emulator opcodes and client-specific opcodes across different client versions.
|
||||
|
||||
### OpcodeManager
|
||||
|
||||
The `OpcodeManager` handles opcode translation for specific client versions:
|
||||
|
||||
```go
|
||||
import "git.sharkk.net/EQ2/Protocol/opcodes"
|
||||
|
||||
// Create a manager for client version 60085
|
||||
om := opcodes.NewOpcodeManager(60085)
|
||||
|
||||
// Load opcode mappings from database
|
||||
err := om.LoadFromDatabase(db, "opcodes_table")
|
||||
|
||||
// Convert between emulator and client opcodes
|
||||
clientOpcode := om.EmuToEQ(opcodes.OP_LoginRequestMsg)
|
||||
emuOpcode := om.EQToEmu(0x0123)
|
||||
|
||||
// Get opcode names
|
||||
name := om.EmuToName(opcodes.OP_LoginRequestMsg)
|
||||
```
|
||||
|
||||
### Opcode Types
|
||||
|
||||
- **EmuOpcode**: Internal emulator opcodes (constants like `OP_LoginRequestMsg`)
|
||||
- **Protocol opcodes**: Low-level protocol opcodes (e.g., `OP_SessionRequest`, `OP_Packet`)
|
||||
- **Client opcodes**: Version-specific opcodes sent to/from the game client
|
||||
|
||||
### Global Manager Cache
|
||||
|
||||
```go
|
||||
// Get or create a manager for a specific version
|
||||
mgr := opcodes.GetManager(version, db, tableName)
|
||||
|
||||
// Get all loaded versions
|
||||
versions := opcodes.GetLoadedVersions()
|
||||
```
|
||||
|
||||
## Packet Definitions
|
||||
|
||||
The defs package provides structured packet definitions with automatic version selection based on client version.
|
||||
|
||||
### PacketManager
|
||||
|
||||
Manages packet structure lookups with version-aware selection:
|
||||
|
||||
```go
|
||||
import "git.sharkk.net/EQ2/Protocol/defs"
|
||||
|
||||
// Create a manager for client version 60085
|
||||
pm := defs.NewPacketManager(60085)
|
||||
|
||||
// Get the appropriate packet struct for this version
|
||||
ps := pm.GetStruct("CreateCharacter", 60085)
|
||||
// Returns CreateCharacterV60085 struct
|
||||
|
||||
// Get by exact version
|
||||
ps := pm.GetStructByVersion("CreateCharacter", 373)
|
||||
|
||||
// Get by opcode
|
||||
ps := pm.GetStructByOpcode(opcodes.OP_CreateCharacterRequestMsg)
|
||||
|
||||
// Create a new packet instance
|
||||
packet := pm.CreateInstance("CreateCharacter")
|
||||
```
|
||||
|
||||
### Version Selection
|
||||
|
||||
The manager automatically selects the highest packet version that doesn't exceed the client version:
|
||||
|
||||
- Client 1000 requesting "CreateCharacter" gets version 869
|
||||
- Client 60085 requesting "CreateCharacter" gets version 60085
|
||||
- Client 70000 requesting "CreateCharacter" gets version 65534
|
||||
|
||||
### Code Generation
|
||||
|
||||
Packet structures and registrations are generated from XML definitions:
|
||||
|
||||
```bash
|
||||
# Regenerate packet structures from XML
|
||||
cd defs && go generate
|
||||
|
||||
# XML definitions are in defs/xml/
|
||||
# Generated code goes to defs/generated/
|
||||
```
|
||||
|
||||
## Packets
|
||||
|
||||
The packets package provides base packet types and interfaces for all protocol packets.
|
||||
|
||||
### Packet Interface
|
||||
|
||||
All packets implement the `Packet` interface:
|
||||
|
||||
```go
|
||||
type Packet interface {
|
||||
GetOpcode() uint16
|
||||
SetOpcode(uint16)
|
||||
GetVersion() uint16
|
||||
SetVersion(uint16)
|
||||
GetSequence() uint16
|
||||
SetSequence(uint16)
|
||||
GetData() []byte
|
||||
GetSize() uint32
|
||||
}
|
||||
```
|
||||
|
||||
### Base Packet Types
|
||||
|
||||
```go
|
||||
// RawPacket - Basic packet with opcode and data
|
||||
packet := &packets.RawPacket{
|
||||
Opcode: 0x0123,
|
||||
Data: []byte{...},
|
||||
}
|
||||
|
||||
// EncodedPacket - Packet with protocol encoding
|
||||
encoded := &packets.EncodedPacket{
|
||||
Sequence: seq,
|
||||
Opcode: opcode,
|
||||
Data: data,
|
||||
Compressed: false,
|
||||
}
|
||||
```
|
||||
|
||||
### Packet Processing
|
||||
|
||||
```go
|
||||
// Check if packet needs processing
|
||||
if packet.ShouldCombine() {
|
||||
// Handle combined packets
|
||||
}
|
||||
|
||||
if packet.IsProtocolPacket() {
|
||||
// Handle protocol-level packets
|
||||
}
|
||||
|
||||
// Encode for transmission
|
||||
encoded := packet.Encode()
|
||||
|
||||
// Decode received data
|
||||
packet, err := packets.Decode(data)
|
||||
```
|
||||
|
||||
## Stream
|
||||
|
||||
The stream package handles bidirectional packet streaming with buffering, fragmentation, and reassembly.
|
||||
|
||||
### PacketStream
|
||||
|
||||
Manages packet flow between client and server:
|
||||
|
||||
```go
|
||||
import "git.sharkk.net/EQ2/Protocol/stream"
|
||||
|
||||
// Create a new packet stream
|
||||
stream := stream.NewPacketStream(conn, bufferSize)
|
||||
|
||||
// Set packet handlers
|
||||
stream.SetIncomingHandler(func(packet packets.Packet) {
|
||||
// Process incoming packets
|
||||
})
|
||||
|
||||
stream.SetOutgoingHandler(func(packet packets.Packet) {
|
||||
// Process outgoing packets
|
||||
})
|
||||
|
||||
// Start processing
|
||||
stream.Start()
|
||||
|
||||
// Send a packet
|
||||
err := stream.Send(packet)
|
||||
|
||||
// Receive packets (handled by callback)
|
||||
// Packets are automatically decoded and reassembled
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
- **Automatic fragmentation**: Large packets are split into fragments
|
||||
- **Reassembly**: Fragments are automatically reassembled
|
||||
- **Buffering**: Configurable send/receive buffers
|
||||
- **Sequencing**: Automatic sequence number management
|
||||
- **Compression**: Optional packet compression support
|
||||
|
||||
### Stream Configuration
|
||||
|
||||
```go
|
||||
config := &stream.Config{
|
||||
MaxPacketSize: 512,
|
||||
BufferSize: 8192,
|
||||
EnableCompression: true,
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
stream := stream.NewPacketStreamWithConfig(conn, config)
|
||||
```
|
||||
|
||||
## Factory
|
||||
|
||||
The factory package provides centralized packet creation and registration.
|
||||
|
||||
### PacketFactory
|
||||
|
||||
Creates packets based on opcodes and manages packet type registration:
|
||||
|
||||
```go
|
||||
import "git.sharkk.net/EQ2/Protocol/factory"
|
||||
|
||||
// Get the global factory instance
|
||||
factory := factory.GetInstance()
|
||||
|
||||
// Register a packet type
|
||||
factory.RegisterPacket(opcodes.OP_LoginRequestMsg, func() packets.Packet {
|
||||
return &LoginRequestPacket{}
|
||||
})
|
||||
|
||||
// Create a packet by opcode
|
||||
packet := factory.CreatePacket(opcodes.OP_LoginRequestMsg)
|
||||
|
||||
// Create with data
|
||||
packet := factory.CreatePacketWithData(opcode, data)
|
||||
|
||||
// Create from raw bytes (auto-detects type)
|
||||
packet, err := factory.CreateFromBytes(rawData)
|
||||
```
|
||||
|
||||
### Packet Registration
|
||||
|
||||
Packets can be registered globally or per-version:
|
||||
|
||||
```go
|
||||
// Register for all versions
|
||||
factory.RegisterPacket(opcode, constructor)
|
||||
|
||||
// Register version-specific
|
||||
factory.RegisterVersionedPacket(opcode, version, constructor)
|
||||
|
||||
// Bulk registration
|
||||
factory.RegisterPackets(map[uint16]PacketConstructor{
|
||||
opcodes.OP_LoginRequestMsg: NewLoginRequest,
|
||||
opcodes.OP_CharacterCreate: NewCharacterCreate,
|
||||
})
|
||||
```
|
||||
|
||||
### Integration with PacketManager
|
||||
|
||||
The factory can work with the defs package for automatic struct-based packets:
|
||||
|
||||
```go
|
||||
// Set packet manager for automatic struct registration
|
||||
factory.SetPacketManager(defs.GetManager(version))
|
||||
|
||||
// Now factory can create packets from struct definitions
|
||||
packet := factory.CreatePacket(opcodes.OP_CreateCharacterRequestMsg)
|
||||
// Returns properly versioned CreateCharacter struct
|
||||
```
|
||||
|
||||
## Usage Example
|
||||
|
||||
Complete example showing protocol usage:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.sharkk.net/EQ2/Protocol/opcodes"
|
||||
"git.sharkk.net/EQ2/Protocol/defs"
|
||||
"git.sharkk.net/EQ2/Protocol/stream"
|
||||
"git.sharkk.net/EQ2/Protocol/factory"
|
||||
)
|
||||
|
||||
func main() {
|
||||
version := uint16(60085)
|
||||
|
||||
// Initialize managers
|
||||
opcodeManager := opcodes.GetManager(version, db, "opcodes")
|
||||
packetManager := defs.GetManager(version)
|
||||
|
||||
// Setup factory
|
||||
factory := factory.GetInstance()
|
||||
factory.SetPacketManager(packetManager)
|
||||
|
||||
// Create stream for connection
|
||||
stream := stream.NewPacketStream(conn, 8192)
|
||||
|
||||
// Handle incoming packets
|
||||
stream.SetIncomingHandler(func(packet packets.Packet) {
|
||||
// Translate opcode
|
||||
emuOpcode := opcodeManager.EQToEmu(packet.GetOpcode())
|
||||
|
||||
// Process based on opcode
|
||||
switch emuOpcode {
|
||||
case opcodes.OP_LoginRequestMsg:
|
||||
handleLogin(packet)
|
||||
case opcodes.OP_CreateCharacterRequestMsg:
|
||||
handleCharacterCreate(packet)
|
||||
}
|
||||
})
|
||||
|
||||
// Send a packet
|
||||
packet := packetManager.CreateInstance("LSWorldList")
|
||||
populateWorldList(packet)
|
||||
|
||||
// Convert to client opcode
|
||||
clientOpcode := opcodeManager.EmuToEQ(opcodes.OP_WorldListMsg)
|
||||
packet.SetOpcode(clientOpcode)
|
||||
|
||||
// Send to client
|
||||
stream.Send(packet)
|
||||
|
||||
// Start processing
|
||||
stream.Start()
|
||||
}
|
||||
```
|
||||
|
||||
## Protocol Architecture
|
||||
|
||||
The protocol library is organized in layers:
|
||||
|
||||
1. **Opcodes**: Manages opcode mappings and translations
|
||||
2. **Defs**: Defines packet structures with version management
|
||||
3. **Packets**: Base packet types and interfaces
|
||||
4. **Factory**: Creates packet instances from opcodes/data
|
||||
5. **Stream**: Handles network I/O, fragmentation, and reassembly
|
||||
|
||||
Each layer builds on the previous, providing a complete protocol implementation for EQ2 server emulation.
|
Loading…
x
Reference in New Issue
Block a user