From ca90ebdfa8789654766c5d7969baa7afacd9ebd2 Mon Sep 17 00:00:00 2001 From: BanceDev Date: Mon, 16 Feb 2026 16:31:54 -0500 Subject: initial commit --- packet/clc/connectionless.go | 64 +++++++++++++++++++++++++++ packet/clc/gamedata.go | 101 +++++++++++++++++++++++++++++++++++++++++++ packet/clc/parse.go | 18 ++++++++ 3 files changed, 183 insertions(+) create mode 100644 packet/clc/connectionless.go create mode 100644 packet/clc/gamedata.go create mode 100644 packet/clc/parse.go (limited to 'packet/clc') diff --git a/packet/clc/connectionless.go b/packet/clc/connectionless.go new file mode 100644 index 0000000..96db112 --- /dev/null +++ b/packet/clc/connectionless.go @@ -0,0 +1,64 @@ +package clc + +import ( + "errors" + + "github.com/osm/quake/common/args" + "github.com/osm/quake/common/buffer" + "github.com/osm/quake/common/context" + "github.com/osm/quake/packet/command" + "github.com/osm/quake/packet/command/connect" + "github.com/osm/quake/packet/command/getchallenge" + "github.com/osm/quake/packet/command/passthrough" +) + +type Connectionless struct { + Command command.Command +} + +func (cmd *Connectionless) Bytes() []byte { + buf := buffer.New() + + buf.PutInt32(-1) + buf.PutBytes(cmd.Command.Bytes()) + + return buf.Bytes() +} + +func parseConnectionless(ctx *context.Context, buf *buffer.Buffer) (*Connectionless, error) { + var err error + var pkg Connectionless + + if err := buf.Skip(4); err != nil { + return nil, err + } + + var str string + if str, err = buf.GetString(); err != nil { + return nil, err + } + + args := args.Parse(str) + if len(args) != 1 { + return nil, errors.New("unexpected length of parsed arguments") + } + + arg := args[0] + + var cmd command.Command + switch arg.Cmd { + case "connect": + cmd, err = connect.Parse(ctx, buf, arg) + case "getchallenge": + cmd, err = getchallenge.Parse(ctx, buf) + default: + cmd, err = passthrough.Parse(ctx, buf, str) + } + + if err != nil { + return nil, err + } + pkg.Command = cmd + + return &pkg, nil +} diff --git a/packet/clc/gamedata.go b/packet/clc/gamedata.go new file mode 100644 index 0000000..7ee5890 --- /dev/null +++ b/packet/clc/gamedata.go @@ -0,0 +1,101 @@ +package clc + +import ( + "errors" + + "github.com/osm/quake/common/buffer" + "github.com/osm/quake/common/context" + "github.com/osm/quake/packet/command" + "github.com/osm/quake/packet/command/bad" + "github.com/osm/quake/packet/command/delta" + "github.com/osm/quake/packet/command/ftevoicechatc" + "github.com/osm/quake/packet/command/move" + "github.com/osm/quake/packet/command/mvdweapon" + "github.com/osm/quake/packet/command/nopc" + "github.com/osm/quake/packet/command/stringcmd" + "github.com/osm/quake/packet/command/tmove" + "github.com/osm/quake/packet/command/upload" + "github.com/osm/quake/protocol" + "github.com/osm/quake/protocol/fte" + "github.com/osm/quake/protocol/mvd" +) + +var ErrUnknownCommandType = errors.New("unknown command type") + +type GameData struct { + Seq uint32 + Ack uint32 + QPort uint16 + Commands []command.Command +} + +func (gd *GameData) Bytes() []byte { + buf := buffer.New() + + buf.PutUint32(gd.Seq) + buf.PutUint32(gd.Ack) + buf.PutUint16(gd.QPort) + + for _, c := range gd.Commands { + buf.PutBytes(c.Bytes()) + } + + return buf.Bytes() +} + +func parseGameData(ctx *context.Context, buf *buffer.Buffer) (*GameData, error) { + var err error + var pkg GameData + + if pkg.Seq, err = buf.GetUint32(); err != nil { + return nil, err + } + + if pkg.Ack, err = buf.GetUint32(); err != nil { + return nil, err + } + + if pkg.QPort, err = buf.GetUint16(); err != nil { + return nil, err + } + + var cmd command.Command + for buf.Off() < buf.Len() { + typ, err := buf.ReadByte() + if err != nil { + return nil, err + } + + switch protocol.CommandType(typ) { + case protocol.CLCBad: + cmd, err = bad.Parse(ctx, buf, protocol.CLCBad) + case protocol.CLCNOP: + cmd, err = nopc.Parse(ctx, buf) + case protocol.CLCDoubleMove: + case protocol.CLCMove: + cmd, err = move.Parse(ctx, buf) + case protocol.CLCStringCmd: + cmd, err = stringcmd.Parse(ctx, buf) + case protocol.CLCDelta: + cmd, err = delta.Parse(ctx, buf) + case protocol.CLCTMove: + cmd, err = tmove.Parse(ctx, buf) + case protocol.CLCUpload: + cmd, err = upload.Parse(ctx, buf) + case fte.CLCVoiceChat: + cmd, err = ftevoicechatc.Parse(ctx, buf) + case mvd.CLCWeapon: + cmd, err = mvdweapon.Parse(ctx, buf) + default: + return nil, ErrUnknownCommandType + } + + if err != nil { + return nil, err + } + + pkg.Commands = append(pkg.Commands, cmd) + } + + return &pkg, nil +} diff --git a/packet/clc/parse.go b/packet/clc/parse.go new file mode 100644 index 0000000..594a9dd --- /dev/null +++ b/packet/clc/parse.go @@ -0,0 +1,18 @@ +package clc + +import ( + "github.com/osm/quake/common/buffer" + "github.com/osm/quake/common/context" + "github.com/osm/quake/packet" +) + +func Parse(ctx *context.Context, data []byte) (packet.Packet, error) { + buf := buffer.New(buffer.WithData(data)) + + header, _ := buf.PeekInt32() + if header == -1 { + return parseConnectionless(ctx, buf) + } + + return parseGameData(ctx, buf) +} -- cgit v1.2.3-59-g8ed1b