forked from markus-wa/demoinfocs-golang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
demopacket.go
155 lines (126 loc) · 3.77 KB
/
demopacket.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package demoinfocs
import (
"fmt"
"sync"
proto "github.com/gogo/protobuf/proto"
events "github.com/markus-wa/demoinfocs-golang/events"
msg "github.com/markus-wa/demoinfocs-golang/msg"
)
// NetMessageCreator creates additional net-messages to be dispatched to net-message handlers.
//
// See also: ParserConfig.AdditionalNetMessageCreators & Parser.RegisterNetMessageHandler()
type NetMessageCreator func() proto.Message
var byteSlicePool = sync.Pool{
New: func() interface{} {
s := make([]byte, 0, 256)
return &s
},
}
func (p *Parser) parsePacket() {
// Booooring
// 152 bytes CommandInfo, 4 bytes SeqNrIn, 4 bytes SeqNrOut
// See at the bottom what the CommandInfo would contain if you are interested.
p.bitReader.Skip((152 + 4 + 4) << 3)
// Here we go
p.bitReader.BeginChunk(p.bitReader.ReadSignedInt(32) << 3)
for !p.bitReader.ChunkFinished() {
cmd := int(p.bitReader.ReadVarInt32())
size := int(p.bitReader.ReadVarInt32())
p.bitReader.BeginChunk(size << 3)
var m proto.Message
switch cmd {
case int(msg.SVC_Messages_svc_PacketEntities):
// We could pool CSVCMsg_PacketEntities as they take up A LOT of the allocations
// but unless we're on a system that's doing a lot of concurrent parsing there isn't really a point
// as handling packets is a lot slower than creating them and we can't pool until they are handled.
m = new(msg.CSVCMsg_PacketEntities)
case int(msg.SVC_Messages_svc_GameEventList):
m = new(msg.CSVCMsg_GameEventList)
case int(msg.SVC_Messages_svc_GameEvent):
m = new(msg.CSVCMsg_GameEvent)
case int(msg.SVC_Messages_svc_CreateStringTable):
m = new(msg.CSVCMsg_CreateStringTable)
case int(msg.SVC_Messages_svc_UpdateStringTable):
m = new(msg.CSVCMsg_UpdateStringTable)
case int(msg.SVC_Messages_svc_UserMessage):
m = new(msg.CSVCMsg_UserMessage)
default:
var name string
if cmd < 8 || cmd >= 100 {
name = msg.NET_Messages_name[int32(cmd)]
} else {
name = msg.SVC_Messages_name[int32(cmd)]
}
if isDebug {
debugUnhandledMessage(cmd, name)
}
if name != "" {
// Handle additional net-messages as defined by the user
creator := p.additionalNetMessageCreators[cmd]
if creator != nil {
m = creator()
break
}
} else {
// Send a warning if the command is unknown
// This might mean our proto files are out of date
p.eventDispatcher.Dispatch(events.ParserWarnEvent{Message: fmt.Sprintf("Unknown message command %q", cmd)})
}
// On to the next one
p.bitReader.EndChunk()
continue
}
b := byteSlicePool.Get().(*[]byte)
p.bitReader.ReadBytesInto(b, size)
if proto.Unmarshal(*b, m) != nil {
// TODO: Don't crash here, happens with demos that work in gotv
panic(fmt.Sprintf("Failed to unmarshal cmd %d", cmd))
}
p.msgQueue <- m
// Reset length to 0 and pool
*b = (*b)[:0]
byteSlicePool.Put(b)
p.bitReader.EndChunk()
}
p.bitReader.EndChunk()
}
/*
Format of 'CommandInfos' - I honestly have no clue what they are good for.
If you find a use for this please let me know!
Here is all i know:
CommandInfo [152 bytes]
- [2]Split
Split [76 bytes]
- flags [4 bytes]
- viewOrigin [12 bytes]
- viewAngles [12 bytes]
- localViewAngles [12 bytes]
- viewOrigin2 [12 bytes]
- viewAngles2 [12 bytes]
- localViewAngles2 [12 bytes]
Origin [12 bytes]
- X [4 bytes]
- Y [4 bytes]
- Z [4 bytes]
Angle [12 bytes]
- X [4 bytes]
- Y [4 bytes]
- Z [4 bytes]
They are parsed in the following order:
split1.flags
split1.viewOrigin.x
split1.viewOrigin.y
split1.viewOrigin.z
split1.viewAngles.x
split1.viewAngles.y
split1.viewAngles.z
split1.localViewAngles.x
split1.localViewAngles.y
split1.localViewAngles.z
split1.viewOrigin2...
split1.viewAngles2...
split1.localViewAngles2...
split2.flags
...
Or just check this file's history for an example on how to parse them
*/