aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pkg/messages/types/svcUserMessage.go955
1 files changed, 906 insertions, 49 deletions
diff --git a/pkg/messages/types/svcUserMessage.go b/pkg/messages/types/svcUserMessage.go
index e5b1943..35c9e2d 100644
--- a/pkg/messages/types/svcUserMessage.go
+++ b/pkg/messages/types/svcUserMessage.go
@@ -1,6 +1,8 @@
1package messages 1package messages
2 2
3import ( 3import (
4 "fmt"
5
4 "github.com/pektezol/bitreader" 6 "github.com/pektezol/bitreader"
5 "github.com/pektezol/demoparser/pkg/writer" 7 "github.com/pektezol/demoparser/pkg/writer"
6) 8)
@@ -8,50 +10,762 @@ import (
8type SvcUserMessage struct { 10type SvcUserMessage struct {
9 Type int8 11 Type int8
10 Length int16 12 Length int16
11 Data []byte 13 Data any
12} 14}
13 15
14type UserMessageType int 16func ParseSvcUserMessage(reader *bitreader.Reader) SvcUserMessage {
17 svcUserMessage := SvcUserMessage{
18 Type: int8(reader.TryReadBits(8)),
19 Length: int16(reader.TryReadBits(12)),
20 }
21 svcUserMessage.Data = reader.TryReadBitsToSlice(uint64(svcUserMessage.Length))
22 userMessageReader := bitreader.NewReaderFromBytes(svcUserMessage.Data.([]byte), true)
23 writer.TempAppendLine("\t\t%s (%d):", UserMessageType(svcUserMessage.Type).String(), svcUserMessage.Type)
24 switch UserMessageType(svcUserMessage.Type) {
25 case EUserMessageTypeGeiger:
26 svcUserMessage.parseGeiger(userMessageReader)
27 case EUserMessageTypeTrain:
28 svcUserMessage.parseTrain(userMessageReader)
29 case EUserMessageTypeHudText:
30 svcUserMessage.parseHUDText(userMessageReader)
31 case EUserMessageTypeSayText:
32 svcUserMessage.parseSayText(userMessageReader)
33 case EUserMessageTypeSayText2:
34 svcUserMessage.parseSayText2(userMessageReader)
35 case EUserMessageTypeTextMsg:
36 svcUserMessage.parseTextMsg(userMessageReader)
37 case EUserMessageTypeHUDMsg:
38 svcUserMessage.parseHUDMsg(userMessageReader)
39 case EUserMessageTypeResetHUD:
40 svcUserMessage.parseResetHUD(userMessageReader)
41 case EUserMessageTypeShake:
42 svcUserMessage.parseShake(userMessageReader)
43 case EUserMessageTypeFade:
44 svcUserMessage.parseFade(userMessageReader)
45 case EUserMessageTypeVGUIMenu:
46 svcUserMessage.parseVguiMenu(userMessageReader)
47 case EUserMessageTypeRumble:
48 svcUserMessage.parseRumble(userMessageReader)
49 case EUserMessageTypeBattery:
50 svcUserMessage.parseBattery(userMessageReader)
51 case EUserMessageTypeDamage:
52 svcUserMessage.parseDamage(userMessageReader)
53 case EUserMessageTypeVoiceMask:
54 svcUserMessage.parseVoiceMask(userMessageReader)
55 case EUserMessageTypeCloseCaption:
56 svcUserMessage.parseCloseCaption(userMessageReader)
57 case EUserMessageTypeKeyHintText:
58 svcUserMessage.parseKeyHintText(userMessageReader)
59 case EUserMessageTypeLogoTimeMsg:
60 svcUserMessage.parseLogoTimeMsg(userMessageReader)
61 case EUserMessageTypeAchievementEvent:
62 svcUserMessage.parseAchivementEvent(userMessageReader)
63 case EUserMessageTypeMPMapCompleted:
64 svcUserMessage.parseMpMapCompleted(userMessageReader)
65 case EUserMessageTypeMPMapIncomplete:
66 svcUserMessage.parseMpMapIncomplete(userMessageReader)
67 case EUserMessageTypeMPTauntEarned:
68 svcUserMessage.parseMpTauntEarned(userMessageReader)
69 case EUserMessageTypeMPTauntLocked:
70 svcUserMessage.parseMpTauntLocked(userMessageReader)
71 case EUserMessageTypePortalFX_Surface:
72 svcUserMessage.parsePortalFxSurface(userMessageReader)
73 case EUserMessageTypeScoreboardTempUpdate:
74 svcUserMessage.parseScoreboardTempUpdate(userMessageReader)
75 default:
76 writer.TempAppendLine("\t\t\tData: %v", svcUserMessage.Data)
77 }
78 return svcUserMessage
79}
80
81func (svcUserMessage *SvcUserMessage) parseGeiger(reader *bitreader.Reader) {
82 geiger := struct{ Range uint8 }{
83 Range: reader.TryReadUInt8(),
84 }
85 svcUserMessage.Data = geiger
86 writer.TempAppendLine("\t\t\tGeiger Range: %d", geiger.Range)
87}
88
89func (svcUserMessage *SvcUserMessage) parseTrain(reader *bitreader.Reader) {
90 train := struct{ Pos uint8 }{
91 Pos: reader.TryReadUInt8(),
92 }
93 svcUserMessage.Data = train
94 writer.TempAppendLine("\t\t\tPos: %d", train.Pos)
95}
96
97func (svcUserMessage *SvcUserMessage) parseHUDText(reader *bitreader.Reader) {
98 hudText := struct{ Text string }{
99 Text: reader.TryReadString(),
100 }
101 svcUserMessage.Data = hudText
102 writer.TempAppendLine("\t\t\tText: %s", hudText.Text)
103}
104
105func (svcUserMessage *SvcUserMessage) parseSayText(reader *bitreader.Reader) {
106 sayText := struct {
107 Client uint8
108 Message string
109 WantsToChat bool
110 }{
111 Client: reader.TryReadUInt8(),
112 Message: reader.TryReadString(),
113 WantsToChat: reader.TryReadUInt8() != 0,
114 }
115 svcUserMessage.Data = sayText
116 writer.TempAppendLine("\t\t\tClient: %d", sayText.Client)
117 writer.TempAppendLine("\t\t\tMessage: %s", sayText.Message)
118 writer.TempAppendLine("\t\t\tWants To Chat: %t", sayText.WantsToChat)
119}
120
121func (svcUserMessage *SvcUserMessage) parseSayText2(reader *bitreader.Reader) {
122 sayText2 := struct {
123 Client uint8
124 WantsToChat bool
125 MessageName string
126 Messages []string
127 }{
128 Client: reader.TryReadUInt8(),
129 WantsToChat: reader.TryReadUInt8() != 0,
130 MessageName: reader.TryReadString(),
131 Messages: []string{reader.TryReadString(), reader.TryReadString(), reader.TryReadString()},
132 }
133 svcUserMessage.Data = sayText2
134 writer.TempAppendLine("\t\t\tClient: %d", sayText2.Client)
135 writer.TempAppendLine("\t\t\tWants To Chat: %t", sayText2.WantsToChat)
136 writer.TempAppendLine("\t\t\tName: %s", sayText2.MessageName)
137 for index, message := range sayText2.Messages {
138 writer.TempAppendLine("\t\t\tMessage %d: %s", index, message)
139 }
140}
141
142func (svcUserMessage *SvcUserMessage) parseTextMsg(reader *bitreader.Reader) {
143 const MessageCount int = 5
144 textMsg := struct {
145 Destination uint8
146 Messages []string
147 }{
148 Destination: reader.TryReadUInt8(),
149 }
150 textMsg.Messages = make([]string, 5)
151 for i := 0; i < MessageCount; i++ {
152 textMsg.Messages[i] = reader.TryReadString()
153 }
154 svcUserMessage.Data = textMsg
155 writer.TempAppendLine("\t\t\tDestination: %d", textMsg.Destination)
156 for i := 0; i < MessageCount; i++ {
157 writer.TempAppendLine("\t\t\tMessage %d: %s", i+1, textMsg.Messages)
158 }
159}
160
161func (svcUserMessage *SvcUserMessage) parseHUDMsg(reader *bitreader.Reader) {
162 const MaxNetMessage uint8 = 6
163 hudMsg := struct {
164 Channel uint8
165 Info struct {
166 X, Y float32 // 0-1 & resolution independent, -1 means center in each dimension
167 R1, G1, B1, A1 uint8
168 R2, G2, B2, A2 uint8
169 Effect uint8
170 FadeIn, FadeOut, HoldTime, FxTime float32 // the fade times seem to be per character
171 Message string
172 }
173 }{
174 Channel: reader.TryReadUInt8() % MaxNetMessage,
175 }
176 svcUserMessage.Data = hudMsg
177 writer.TempAppendLine("\t\t\tChannel: %d", hudMsg.Channel)
178 if reader.TryReadRemainingBits() >= 148 {
179 hudMsg.Info = struct {
180 X float32
181 Y float32
182 R1 uint8
183 G1 uint8
184 B1 uint8
185 A1 uint8
186 R2 uint8
187 G2 uint8
188 B2 uint8
189 A2 uint8
190 Effect uint8
191 FadeIn float32
192 FadeOut float32
193 HoldTime float32
194 FxTime float32
195 Message string
196 }{
197 X: reader.TryReadFloat32(),
198 Y: reader.TryReadFloat32(),
199 R1: reader.TryReadUInt8(),
200 G1: reader.TryReadUInt8(),
201 B1: reader.TryReadUInt8(),
202 A1: reader.TryReadUInt8(),
203 R2: reader.TryReadUInt8(),
204 G2: reader.TryReadUInt8(),
205 B2: reader.TryReadUInt8(),
206 A2: reader.TryReadUInt8(),
207 Effect: reader.TryReadUInt8(),
208 FadeIn: reader.TryReadFloat32(),
209 FadeOut: reader.TryReadFloat32(),
210 HoldTime: reader.TryReadFloat32(),
211 FxTime: reader.TryReadFloat32(),
212 Message: reader.TryReadString(),
213 }
214 svcUserMessage.Data = hudMsg
215 writer.TempAppendLine("\t\t\tX: %f, Y: %f", hudMsg.Info.X, hudMsg.Info.Y)
216 writer.TempAppendLine("\t\t\tRGBA1: %3d %3d %3d %3d", hudMsg.Info.R1, hudMsg.Info.G1, hudMsg.Info.B1, hudMsg.Info.A1)
217 writer.TempAppendLine("\t\t\tRGBA2: %3d %3d %3d %3d", hudMsg.Info.R2, hudMsg.Info.G2, hudMsg.Info.B2, hudMsg.Info.A2)
218 writer.TempAppendLine("\t\t\tEffect: %d", hudMsg.Info.Effect)
219 writer.TempAppendLine("\t\t\tFade In: %f", hudMsg.Info.FadeIn)
220 writer.TempAppendLine("\t\t\tFade Out: %f", hudMsg.Info.FadeOut)
221 writer.TempAppendLine("\t\t\tHold Time: %f", hudMsg.Info.HoldTime)
222 writer.TempAppendLine("\t\t\tFX Time: %f", hudMsg.Info.FxTime)
223 writer.TempAppendLine("\t\t\tMessage: %s", hudMsg.Info.Message)
224 }
225}
226
227func (svcUserMessage *SvcUserMessage) parseResetHUD(reader *bitreader.Reader) {
228 resetHUD := struct{ Unknown uint8 }{
229 Unknown: reader.TryReadUInt8(),
230 }
231 svcUserMessage.Data = resetHUD
232 writer.TempAppendLine("\t\t\tUnknown: %d", resetHUD.Unknown)
233}
234
235func (svcUserMessage *SvcUserMessage) parseShake(reader *bitreader.Reader) {
236 type ShakeCommand uint8
237 const (
238 Start ShakeCommand = iota // Starts the screen shake for all players within the radius.
239 Stop // Stops the screen shake for all players within the radius.
240 Amplitude // Modifies the amplitude of an active screen shake for all players within the radius.
241 Frequency // Modifies the frequency of an active screen shake for all players within the radius.
242 RumbleOnly // Starts a shake effect that only rumbles the controller, no screen effect.
243 NoRumble // Starts a shake that does NOT rumble the controller.
244 )
245 shake := struct {
246 Command uint8
247 Amplitude float32
248 Frequency float32
249 Duration float32
250 }{
251 Command: reader.TryReadUInt8(),
252 Amplitude: reader.TryReadFloat32(),
253 Frequency: reader.TryReadFloat32(),
254 Duration: reader.TryReadFloat32(),
255 }
256 shakeCommandToString := func(cmd ShakeCommand) string {
257 switch cmd {
258 case Start:
259 return "Start"
260 case Stop:
261 return "Stop"
262 case Amplitude:
263 return "Amplitude"
264 case Frequency:
265 return "Frequency"
266 case RumbleOnly:
267 return "RumbleOnly"
268 case NoRumble:
269 return "NoRumble"
270 default:
271 return "Unknown"
272 }
273 }
274 svcUserMessage.Data = shake
275 writer.TempAppendLine("\t\t\tCommand: %v", shakeCommandToString(ShakeCommand(shake.Command)))
276 writer.TempAppendLine("\t\t\tAmplitude: %v", shake.Amplitude)
277 writer.TempAppendLine("\t\t\tFrequency: %v", shake.Frequency)
278 writer.TempAppendLine("\t\t\tDuration: %v", shake.Duration)
279}
280
281func (svcUserMessage *SvcUserMessage) parseFade(reader *bitreader.Reader) {
282 type FadeFlag uint16
283 const (
284 None FadeFlag = 0
285 FadeIn FadeFlag = 1
286 FadeOut FadeFlag = 1 << 1
287 Modulate FadeFlag = 1 << 2 // Modulate (don't blend)
288 StayOut FadeFlag = 1 << 3 // ignores the duration, stays faded out until new ScreenFade message received
289 Purge FadeFlag = 1 << 4 // Purges all other fades, replacing them with this one
290 )
291 fade := struct {
292 Duration float32
293 HoldTime uint16
294 Flags uint16
295 R uint8
296 G uint8
297 B uint8
298 A uint8
299 }{
300 Duration: float32(reader.TryReadUInt16()) / float32(1<<9), // might be useful: #define SCREENFADE_FRACBITS 9
301 HoldTime: reader.TryReadUInt16(),
302 Flags: reader.TryReadUInt16(),
303 R: reader.TryReadUInt8(),
304 G: reader.TryReadUInt8(),
305 B: reader.TryReadUInt8(),
306 A: reader.TryReadUInt8(),
307 }
308 getFlags := func(flags FadeFlag) []string {
309 var flagStrings []string
310 if flags&FadeIn != 0 {
311 flagStrings = append(flagStrings, "FadeIn")
312 }
313 if flags&FadeOut != 0 {
314 flagStrings = append(flagStrings, "FadeOut")
315 }
316 if flags&Modulate != 0 {
317 flagStrings = append(flagStrings, "Modulate")
318 }
319 if flags&StayOut != 0 {
320 flagStrings = append(flagStrings, "StayOut")
321 }
322 if flags&Purge != 0 {
323 flagStrings = append(flagStrings, "Purge")
324 }
325 return flagStrings
326 }
327 svcUserMessage.Data = fade
328 writer.TempAppendLine("\t\t\tDuration: %f", fade.Duration)
329 writer.TempAppendLine("\t\t\tHold Time: %d", fade.HoldTime)
330 writer.TempAppendLine("\t\t\tFlags: %v", getFlags(FadeFlag(fade.Flags)))
331 writer.TempAppendLine("\t\t\tRGBA: %3d %3d %3d %3d", fade.R, fade.G, fade.B, fade.A)
332}
333
334func (svcUserMessage *SvcUserMessage) parseVguiMenu(reader *bitreader.Reader) {
335 vguiMenu := struct {
336 Message string
337 Show bool
338 KeyValues []map[string]string
339 }{
340 Message: reader.TryReadString(),
341 Show: reader.TryReadUInt8() != 0,
342 }
343 count := reader.TryReadUInt8()
344 for i := 0; i < int(count); i++ {
345 vguiMenu.KeyValues = append(vguiMenu.KeyValues, map[string]string{"Key": reader.TryReadString(), "Value": reader.TryReadString()})
346 }
347 svcUserMessage.Data = vguiMenu
348 writer.TempAppendLine("\t\t\tMessage: %s", vguiMenu.Message)
349 writer.TempAppendLine("\t\t\tShow: %t", vguiMenu.Show)
350 if len(vguiMenu.KeyValues) > 0 {
351 writer.TempAppendLine("\t\t\t%d Key Value Pairs:", len(vguiMenu.KeyValues))
352 for _, kv := range vguiMenu.KeyValues {
353 writer.TempAppendLine("\t\t\t\t%s: %s", kv["Key"], kv["Value"])
354 }
355 } else {
356 writer.TempAppendLine("\t\t\tNo Key Value Pairs")
357 }
358}
359
360func (svcUserMessage *SvcUserMessage) parseRumble(reader *bitreader.Reader) {
361 type RumbleLookup int8
362 const (
363 RumbleInvalid RumbleLookup = -1
364 RumbleStopAll RumbleLookup = 0 // cease all current rumbling effects.
365 PhyscannonOpen RumbleLookup = 20
366 PhyscannonPunt RumbleLookup = 21
367 PhyscannonLow RumbleLookup = 22
368 PhyscannonMedium RumbleLookup = 23
369 PhyscannonHigh RumbleLookup = 24
370 PortalgunLeft RumbleLookup = 25
371 PortalgunRight RumbleLookup = 26
372 PortalPlacementFailure RumbleLookup = 27
373 )
374 getRumbleLookup := func(rumbleLookup RumbleLookup) string {
375 switch rumbleLookup {
376 case RumbleInvalid:
377 return "RumbleInvalid"
378 case RumbleStopAll:
379 return "RumbleStopAll"
380 case PhyscannonOpen:
381 return "PhyscannonOpen"
382 case PhyscannonPunt:
383 return "PhyscannonPunt"
384 case PhyscannonLow:
385 return "PhyscannonLow"
386 case PhyscannonMedium:
387 return "PhyscannonMedium"
388 case PhyscannonHigh:
389 return "PhyscannonHigh"
390 case PortalgunLeft:
391 return "PortalgunLeft"
392 case PortalgunRight:
393 return "PortalgunRight"
394 case PortalPlacementFailure:
395 return "PortalPlacementFailure"
396 default:
397 return fmt.Sprintf("%d", int(rumbleLookup))
398 }
399 }
400 type RumbleFlag uint8
401 const (
402 None RumbleFlag = 0
403 Stop RumbleFlag = 1
404 Loop RumbleFlag = 1 << 1
405 Restart RumbleFlag = 1 << 2
406 UpdateScale RumbleFlag = 1 << 3 // Apply DATA to this effect if already playing, but don't restart. <-- DATA is scale * 100
407 OnlyOne RumbleFlag = 1 << 4 // Don't play this effect if it is already playing.
408 RandomAmplitude RumbleFlag = 1 << 4 // Amplitude scale will be randomly chosen. Between 10% and 100%
409 InitialScale RumbleFlag = 1 << 4 // Data is the initial scale to start this effect ( * 100 )
410 )
411 rumble := struct {
412 Type int8
413 Scale float32
414 Flags uint8
415 }{
416 Type: reader.TryReadSInt8(),
417 Scale: float32(reader.TryReadUInt8()) / 100,
418 Flags: reader.TryReadUInt8(),
419 }
420 getFlags := func(flags RumbleFlag) []string {
421 var flagStrings []string
422 if flags&Stop != 0 {
423 flagStrings = append(flagStrings, "Stop")
424 }
425 if flags&Loop != 0 {
426 flagStrings = append(flagStrings, "Loop")
427 }
428 if flags&Restart != 0 {
429 flagStrings = append(flagStrings, "Restart")
430 }
431 if flags&UpdateScale != 0 {
432 flagStrings = append(flagStrings, "UpdateScale")
433 }
434 if flags&OnlyOne != 0 {
435 flagStrings = append(flagStrings, "OnlyOne")
436 }
437 if flags&RandomAmplitude != 0 {
438 flagStrings = append(flagStrings, "RandomAmplitude")
439 }
440 if flags&InitialScale != 0 {
441 flagStrings = append(flagStrings, "InitialScale")
442 }
443 return flagStrings
444 }
445 svcUserMessage.Data = rumble
446 writer.TempAppendLine("\t\t\tType: %s", getRumbleLookup(RumbleLookup(rumble.Type)))
447 writer.TempAppendLine("\t\t\tScale: %f", rumble.Scale)
448 writer.TempAppendLine("\t\t\tFlags: %v", getFlags(RumbleFlag(rumble.Flags)))
449}
450
451func (svcUserMessage *SvcUserMessage) parseBattery(reader *bitreader.Reader) {
452 battery := struct{ BatteryVal uint16 }{
453 BatteryVal: reader.TryReadUInt16(),
454 }
455 svcUserMessage.Data = battery
456 writer.TempAppendLine("\t\t\tBattery: %d", battery.BatteryVal)
457}
458
459func (svcUserMessage *SvcUserMessage) parseDamage(reader *bitreader.Reader) {
460 damage := struct {
461 Armor uint8
462 DamageTaken uint8
463 BitsDamage int32
464 VecFrom []float32
465 }{
466 Armor: reader.TryReadUInt8(),
467 DamageTaken: reader.TryReadUInt8(),
468 BitsDamage: reader.TryReadSInt32(),
469 VecFrom: []float32{reader.TryReadFloat32(), reader.TryReadFloat32(), reader.TryReadFloat32()},
470 }
471 svcUserMessage.Data = damage
472 writer.TempAppendLine("\t\t\tArmor: %d", damage.Armor)
473 writer.TempAppendLine("\t\t\tDamage Taken: %d", damage.DamageTaken)
474 writer.TempAppendLine("\t\t\tBits Damage: %d", damage.BitsDamage)
475 writer.TempAppendLine("\t\t\tVecFrom: %v", damage.VecFrom)
476}
477
478func (svcUserMessage *SvcUserMessage) parseVoiceMask(reader *bitreader.Reader) {
479 // const VoiceMaxPlayers = 2
480 voiceMask := struct {
481 PlayerMasks []struct {
482 GameRulesMask int32
483 BanMask int32
484 }
485 PlayerModEnable bool
486 }{
487 PlayerMasks: []struct {
488 GameRulesMask int32
489 BanMask int32
490 }{
491 {
492 GameRulesMask: reader.TryReadSInt32(),
493 BanMask: reader.TryReadSInt32(),
494 },
495 {
496 GameRulesMask: reader.TryReadSInt32(),
497 BanMask: reader.TryReadSInt32(),
498 },
499 },
500 PlayerModEnable: reader.TryReadUInt8() != 0,
501 }
502 svcUserMessage.Data = voiceMask
503 writer.TempAppendLine("\t\t\tPlayer Masks:")
504 writer.TempAppendLine("\t\t\t\t[0] Game Rules Mask: %d", voiceMask.PlayerMasks[0].GameRulesMask)
505 writer.TempAppendLine("\t\t\t\t[0] Ban Mask: %d", voiceMask.PlayerMasks[0].BanMask)
506 writer.TempAppendLine("\t\t\t\t[1] Game Rules Mask: %d", voiceMask.PlayerMasks[1].GameRulesMask)
507 writer.TempAppendLine("\t\t\t\t[1] Ban Mask: %d", voiceMask.PlayerMasks[1].BanMask)
508 writer.TempAppendLine("\t\t\t\tPlayer Mod Enable: %t", voiceMask.PlayerModEnable)
509}
510
511func (svcUserMessage *SvcUserMessage) parseCloseCaption(reader *bitreader.Reader) {
512 type CloseCaptionFlag uint8
513 const (
514 None CloseCaptionFlag = 0
515 WarnIfMissing CloseCaptionFlag = 1
516 FromPlayer CloseCaptionFlag = 1 << 1
517 GenderMale CloseCaptionFlag = 1 << 2
518 GenderFemale CloseCaptionFlag = 1 << 3
519 )
520 closeCaption := struct {
521 TokenName string
522 Duration float32
523 Flags uint8
524 }{
525 TokenName: reader.TryReadString(),
526 Duration: float32(reader.TryReadSInt16()) * 0.1,
527 Flags: reader.TryReadUInt8(),
528 }
529 getFlags := func(flags CloseCaptionFlag) []string {
530 var flagStrings []string
531 if flags&WarnIfMissing != 0 {
532 flagStrings = append(flagStrings, "WarnIfMissing")
533 }
534 if flags&FromPlayer != 0 {
535 flagStrings = append(flagStrings, "FromPlayer")
536 }
537 if flags&GenderMale != 0 {
538 flagStrings = append(flagStrings, "GenderMale")
539 }
540 if flags&GenderFemale != 0 {
541 flagStrings = append(flagStrings, "GenderFemale")
542 }
543 return flagStrings
544 }
545 svcUserMessage.Data = closeCaption
546 writer.TempAppendLine("\t\t\tToken Name: %s", closeCaption.TokenName)
547 writer.TempAppendLine("\t\t\tDuration: %f", closeCaption.Duration)
548 writer.TempAppendLine("\t\t\tFlags: %v", getFlags(CloseCaptionFlag(closeCaption.Flags)))
549}
550
551func (svcUserMessage *SvcUserMessage) parseKeyHintText(reader *bitreader.Reader) {
552 keyHintText := struct {
553 Count uint8
554 KeyString string
555 }{
556 Count: reader.TryReadUInt8(),
557 KeyString: reader.TryReadString(),
558 }
559 svcUserMessage.Data = keyHintText
560 writer.TempAppendLine("\t\t\tCount: %d", keyHintText.Count)
561 writer.TempAppendLine("\t\t\tString: %s", keyHintText.KeyString)
562}
563
564func (svcUserMessage *SvcUserMessage) parseLogoTimeMsg(reader *bitreader.Reader) {
565 logoTimeMsg := struct{ Time float32 }{
566 Time: reader.TryReadFloat32(),
567 }
568 svcUserMessage.Data = logoTimeMsg
569 writer.TempAppendLine("\t\t\tTime: %f", logoTimeMsg.Time)
570}
571
572func (svcUserMessage *SvcUserMessage) parseAchivementEvent(reader *bitreader.Reader) {
573 achivementEvent := struct{ AchivementID int32 }{
574 AchivementID: reader.TryReadSInt32(),
575 }
576 svcUserMessage.Data = achivementEvent
577 writer.TempAppendLine("\t\t\tPortal Count: %v", achivementEvent.AchivementID)
578}
579
580func (svcUserMessage *SvcUserMessage) parseMpMapCompleted(reader *bitreader.Reader) {
581 mpMapCompleted := struct {
582 Branch uint8
583 Level uint8
584 }{
585 Branch: reader.TryReadUInt8(),
586 Level: reader.TryReadUInt8(),
587 }
588 svcUserMessage.Data = mpMapCompleted
589 writer.TempAppendLine("\t\t\tBranch: %d", mpMapCompleted.Branch)
590 writer.TempAppendLine("\t\t\tLevel: %d", mpMapCompleted.Level)
591}
592
593func (svcUserMessage *SvcUserMessage) parseMpMapIncomplete(reader *bitreader.Reader) {}
594
595func (svcUserMessage *SvcUserMessage) parseMpTauntEarned(reader *bitreader.Reader) {
596 mpTauntEarned := struct {
597 TauntName string
598 AwardSilently bool
599 }{
600 TauntName: reader.TryReadString(),
601 AwardSilently: reader.TryReadBool(),
602 }
603 svcUserMessage.Data = mpTauntEarned
604 writer.TempAppendLine("\t\t\tTaunt Name: %s", mpTauntEarned.TauntName)
605 writer.TempAppendLine("\t\t\tAward Silently: %t", mpTauntEarned.AwardSilently)
606}
607
608func (svcUserMessage *SvcUserMessage) parseMpTauntLocked(reader *bitreader.Reader) {
609 mpTauntLocked := struct{ TauntName string }{
610 TauntName: reader.TryReadString(),
611 }
612 svcUserMessage.Data = mpTauntLocked
613 writer.TempAppendLine("\t\t\tTaunt Name: %s", mpTauntLocked.TauntName)
614}
615
616func (svcUserMessage *SvcUserMessage) parsePortalFxSurface(reader *bitreader.Reader) {
617 type PortalFizzleType int8
618 const (
619 PortalFizzleSuccess PortalFizzleType = iota // Placed fine (no fizzle)
620 PortalFizzleCantFit
621 PortalFizzleOverlappedLinked
622 PortalFizzleBadVolume
623 PortalFizzleBadSurface
624 PortalFizzleKilled
625 PortalFizzleCleanser
626 PortalFizzleClose
627 PortalFizzleNearBlue
628 PortalFizzleNearRed
629 PortalFizzleNone
630 )
631 getPortalFizzleType := func(portalFizzleType PortalFizzleType) string {
632 switch portalFizzleType {
633 case PortalFizzleSuccess:
634 return "PortalFizzleSuccess"
635 case PortalFizzleCantFit:
636 return "PortalFizzleCantFit"
637 case PortalFizzleOverlappedLinked:
638 return "PortalFizzleOverlappedLinked"
639 case PortalFizzleBadVolume:
640 return "PortalFizzleBadVolume"
641 case PortalFizzleBadSurface:
642 return "PortalFizzleBadSurface"
643 case PortalFizzleKilled:
644 return "PortalFizzleKilled"
645 case PortalFizzleCleanser:
646 return "PortalFizzleCleanser"
647 case PortalFizzleClose:
648 return "PortalFizzleClose"
649 case PortalFizzleNearBlue:
650 return "PortalFizzleNearBlue"
651 case PortalFizzleNearRed:
652 return "PortalFizzleNearRed"
653 case PortalFizzleNone:
654 return "PortalFizzleNone"
655 default:
656 return fmt.Sprintf("%d", int(portalFizzleType))
657 }
658 }
659 portalFxSurface := struct {
660 PortalEnt uint16
661 OwnerEnt uint16
662 Team uint8
663 PortalNum uint8
664 Effect uint8
665 Origin []float32
666 Angles []float32
667 }{
668 PortalEnt: reader.TryReadUInt16(),
669 OwnerEnt: reader.TryReadUInt16(),
670 Team: reader.TryReadUInt8(),
671 PortalNum: reader.TryReadUInt8(),
672 Effect: reader.TryReadUInt8(),
673 Origin: []float32{},
674 Angles: []float32{},
675 }
676 existsX, existsY, existsZ := reader.TryReadBool(), reader.TryReadBool(), reader.TryReadBool()
677 if existsX {
678 portalFxSurface.Origin = append(portalFxSurface.Origin, readBitCoord(reader))
679 } else {
680 portalFxSurface.Origin = append(portalFxSurface.Origin, 0)
681 }
682 if existsY {
683 portalFxSurface.Origin = append(portalFxSurface.Origin, readBitCoord(reader))
684 } else {
685 portalFxSurface.Origin = append(portalFxSurface.Origin, 0)
686 }
687 if existsZ {
688 portalFxSurface.Origin = append(portalFxSurface.Origin, readBitCoord(reader))
689 } else {
690 portalFxSurface.Origin = append(portalFxSurface.Origin, 0)
691 }
692 existsX, existsY, existsZ = reader.TryReadBool(), reader.TryReadBool(), reader.TryReadBool()
693 if existsX {
694 portalFxSurface.Angles = append(portalFxSurface.Angles, readBitCoord(reader))
695 } else {
696 portalFxSurface.Angles = append(portalFxSurface.Angles, 0)
697 }
698 if existsY {
699 portalFxSurface.Angles = append(portalFxSurface.Angles, readBitCoord(reader))
700 } else {
701 portalFxSurface.Angles = append(portalFxSurface.Angles, 0)
702 }
703 if existsZ {
704 portalFxSurface.Angles = append(portalFxSurface.Angles, readBitCoord(reader))
705 } else {
706 portalFxSurface.Angles = append(portalFxSurface.Angles, 0)
707 }
708 svcUserMessage.Data = portalFxSurface
709 _ = getPortalFizzleType(PortalFizzleType(2))
710 writer.TempAppendLine("\t\t\tPortal Entity: %d", portalFxSurface.PortalEnt)
711 writer.TempAppendLine("\t\t\tOwner Entity: %d", portalFxSurface.OwnerEnt)
712 writer.TempAppendLine("\t\t\tTeam: %d", portalFxSurface.Team)
713 writer.TempAppendLine("\t\t\tPortal Number: %d", portalFxSurface.PortalNum)
714 writer.TempAppendLine("\t\t\tEffect: %s", getPortalFizzleType(PortalFizzleType(portalFxSurface.Effect)))
715 writer.TempAppendLine("\t\t\tOrigin: %v", portalFxSurface.Origin)
716 writer.TempAppendLine("\t\t\tAngles: %v", portalFxSurface.Angles)
717}
718
719func (svcUserMessage *SvcUserMessage) parseScoreboardTempUpdate(reader *bitreader.Reader) {
720 scoreboardTempUpdate := struct {
721 NumPortals int32
722 TimeTaken int32
723 }{
724 NumPortals: reader.TryReadSInt32(),
725 TimeTaken: reader.TryReadSInt32(),
726 }
727 svcUserMessage.Data = scoreboardTempUpdate
728 writer.TempAppendLine("\t\t\tPortal Count: %v", scoreboardTempUpdate.NumPortals)
729 writer.TempAppendLine("\t\t\tCM Ticks: %v", scoreboardTempUpdate.TimeTaken)
730}
731
732type UserMessageType uint8
15 733
16const ( 734const (
17 EUserMessageTypeUnknown UserMessageType = iota 735 EUserMessageTypeGeiger UserMessageType = iota // done
18 EUserMessageTypeInvalid 736 EUserMessageTypeTrain // done
19 EUserMessageTypeGeiger 737 EUserMessageTypeHudText // done
20 EUserMessageTypeTrain 738 EUserMessageTypeSayText // done
21 EUserMessageTypeHudText 739 EUserMessageTypeSayText2 // done
22 EUserMessageTypeSayText 740 EUserMessageTypeTextMsg // done
23 EUserMessageTypeSayText2 741 EUserMessageTypeHUDMsg // done
24 EUserMessageTypeTextMsg 742 EUserMessageTypeResetHUD // done // called every respawn
25 EUserMessageTypeHUDMsg
26 EUserMessageTypeResetHUD
27 EUserMessageTypeGameTitle 743 EUserMessageTypeGameTitle
28 EUserMessageTypeItemPickup 744 EUserMessageTypeItemPickup
29 EUserMessageTypeShowMenu 745 EUserMessageTypeShowMenu
30 EUserMessageTypeShake 746 EUserMessageTypeShake // done
31 EUserMessageTypeFade 747 EUserMessageTypeTilt
32 EUserMessageTypeVGUIMenu 748 EUserMessageTypeFade // done
33 EUserMessageTypeRumble 749 EUserMessageTypeVGUIMenu // done // Show VGUI menu
34 EUserMessageTypeBattery 750 EUserMessageTypeRumble // done // Send a rumble to a controller
35 EUserMessageTypeDamage 751 EUserMessageTypeBattery // done
36 EUserMessageTypeVoiceMask 752 EUserMessageTypeDamage // done
753 EUserMessageTypeVoiceMask // done
37 EUserMessageTypeRequestState 754 EUserMessageTypeRequestState
38 EUserMessageTypeCloseCaption 755 EUserMessageTypeCloseCaption // done // Show a caption (by string id number)(duration in 10th of a second)
39 EUserMessageTypeHintText 756 EUserMessageTypeCloseCaptionDirect // Show a forced caption (by string id number)(duration in 10th of a second)
40 EUserMessageTypeKeyHintText 757 EUserMessageTypeHintText // Displays hint text display
758 EUserMessageTypeKeyHintText // done // Displays hint text display
41 EUserMessageTypeSquadMemberDied 759 EUserMessageTypeSquadMemberDied
42 EUserMessageTypeAmmoDenied 760 EUserMessageTypeAmmoDenied
43 EUserMessageTypeCreditsMsg 761 EUserMessageTypeCreditsMsg
44 EUserMessageTypeCreditsPortalMsg 762 EUserMessageTypeLogoTimeMsg // done
45 EUserMessageTypeLogoTimeMsg 763 EUserMessageTypeAchievementEvent // done
46 EUserMessageTypeAchievementEvent
47 EUserMessageTypeEntityPortalled
48 EUserMessageTypeKillCam
49 EUserMessageTypeTilt
50 EUserMessageTypeCloseCaptionDirect
51 EUserMessageTypeUpdateJalopyRadar 764 EUserMessageTypeUpdateJalopyRadar
52 EUserMessageTypeCurrentTimescale 765 EUserMessageTypeCurrentTimescale // Send one float for the new timescale
53 EUserMessageTypeDesiredTimescale 766 EUserMessageTypeDesiredTimescale // Send timescale and some blending vars
54 EUserMessageTypeInventoryFlash 767 EUserMessageTypeCreditsPortalMsg // portal 1 end
768 EUserMessageTypeInventoryFlash // portal 2 start
55 EUserMessageTypeIndicatorFlash 769 EUserMessageTypeIndicatorFlash
56 EUserMessageTypeControlHelperAnimate 770 EUserMessageTypeControlHelperAnimate
57 EUserMessageTypeTakePhoto 771 EUserMessageTypeTakePhoto
@@ -59,14 +773,14 @@ const (
59 EUserMessageTypeHudPingIndicator 773 EUserMessageTypeHudPingIndicator
60 EUserMessageTypeOpenRadialMenu 774 EUserMessageTypeOpenRadialMenu
61 EUserMessageTypeAddLocator 775 EUserMessageTypeAddLocator
62 EUserMessageTypeMPMapCompleted 776 EUserMessageTypeMPMapCompleted // done
63 EUserMessageTypeMPMapIncomplete 777 EUserMessageTypeMPMapIncomplete // done
64 EUserMessageTypeMPMapCompletedData 778 EUserMessageTypeMPMapCompletedData
65 EUserMessageTypeMPTauntEarned 779 EUserMessageTypeMPTauntEarned // done
66 EUserMessageTypeMPTauntUnlocked 780 EUserMessageTypeMPTauntUnlocked
67 EUserMessageTypeMPTauntLocked 781 EUserMessageTypeMPTauntLocked // done
68 EUserMessageTypeMPAllTauntsLocked 782 EUserMessageTypeMPAllTauntsLocked
69 EUserMessageTypePortalFX_Surface 783 EUserMessageTypePortalFX_Surface // done
70 EUserMessageTypePaintWorld 784 EUserMessageTypePaintWorld
71 EUserMessageTypePaintEntity 785 EUserMessageTypePaintEntity
72 EUserMessageTypeChangePaintColor 786 EUserMessageTypeChangePaintColor
@@ -78,22 +792,165 @@ const (
78 EUserMessageTypeApplyHitBoxDamageEffect 792 EUserMessageTypeApplyHitBoxDamageEffect
79 EUserMessageTypeSetMixLayerTriggerFactor 793 EUserMessageTypeSetMixLayerTriggerFactor
80 EUserMessageTypeTransitionFade 794 EUserMessageTypeTransitionFade
81 EUserMessageTypeScoreboardTempUpdate 795 EUserMessageTypeScoreboardTempUpdate // done
82 EUserMessageTypeChallengeModCheatSession 796 EUserMessageTypeChallengeModCheatSession
83 EUserMessageTypeChallengeModCloseAllUI 797 EUserMessageTypeChallengeModCloseAllUI
84) 798)
85 799
86func ParseSvcUserMessage(reader *bitreader.Reader) SvcUserMessage { 800func (userMessageType UserMessageType) String() string {
87 svcUserMessage := SvcUserMessage{ 801 switch userMessageType {
88 Type: int8(reader.TryReadBits(8)), 802 case EUserMessageTypeGeiger:
89 Length: int16(reader.TryReadBits(12)), 803 return "Geiger"
804 case EUserMessageTypeTrain:
805 return "Train"
806 case EUserMessageTypeHudText:
807 return "HudText"
808 case EUserMessageTypeSayText:
809 return "SayText"
810 case EUserMessageTypeSayText2:
811 return "SayText2"
812 case EUserMessageTypeTextMsg:
813 return "TextMsg"
814 case EUserMessageTypeHUDMsg:
815 return "HUDMsg"
816 case EUserMessageTypeResetHUD:
817 return "ResetHUD"
818 case EUserMessageTypeGameTitle:
819 return "GameTitle"
820 case EUserMessageTypeItemPickup:
821 return "ItemPickup"
822 case EUserMessageTypeShowMenu:
823 return "ShowMenu"
824 case EUserMessageTypeShake:
825 return "Shake"
826 case EUserMessageTypeTilt:
827 return "Tilt"
828 case EUserMessageTypeFade:
829 return "Fade"
830 case EUserMessageTypeVGUIMenu:
831 return "VGUIMenu"
832 case EUserMessageTypeRumble:
833 return "Rumble"
834 case EUserMessageTypeBattery:
835 return "Battery"
836 case EUserMessageTypeDamage:
837 return "Damage"
838 case EUserMessageTypeVoiceMask:
839 return "VoiceMask"
840 case EUserMessageTypeRequestState:
841 return "RequestState"
842 case EUserMessageTypeCloseCaption:
843 return "CloseCaption"
844 case EUserMessageTypeCloseCaptionDirect:
845 return "CloseCaptionDirect"
846 case EUserMessageTypeHintText:
847 return "HintText"
848 case EUserMessageTypeKeyHintText:
849 return "KeyHintText"
850 case EUserMessageTypeSquadMemberDied:
851 return "SquadMemberDied"
852 case EUserMessageTypeAmmoDenied:
853 return "AmmoDenied"
854 case EUserMessageTypeCreditsMsg:
855 return "CreditsMsg"
856 case EUserMessageTypeLogoTimeMsg:
857 return "LogoTimeMsg"
858 case EUserMessageTypeAchievementEvent:
859 return "AchievementEvent"
860 case EUserMessageTypeUpdateJalopyRadar:
861 return "UpdateJalopyRadar"
862 case EUserMessageTypeCurrentTimescale:
863 return "CurrentTimescale"
864 case EUserMessageTypeDesiredTimescale:
865 return "DesiredTimescale"
866 case EUserMessageTypeCreditsPortalMsg:
867 return "CreditsPortalMsg"
868 case EUserMessageTypeInventoryFlash:
869 return "InventoryFlash"
870 case EUserMessageTypeIndicatorFlash:
871 return "IndicatorFlash"
872 case EUserMessageTypeControlHelperAnimate:
873 return "ControlHelperAnimate"
874 case EUserMessageTypeTakePhoto:
875 return "TakePhoto"
876 case EUserMessageTypeFlash:
877 return "Flash"
878 case EUserMessageTypeHudPingIndicator:
879 return "HudPingIndicator"
880 case EUserMessageTypeOpenRadialMenu:
881 return "OpenRadialMenu"
882 case EUserMessageTypeAddLocator:
883 return "AddLocator"
884 case EUserMessageTypeMPMapCompleted:
885 return "MPMapCompleted"
886 case EUserMessageTypeMPMapIncomplete:
887 return "MPMapIncomplete"
888 case EUserMessageTypeMPMapCompletedData:
889 return "MPMapCompletedData"
890 case EUserMessageTypeMPTauntEarned:
891 return "MPTauntEarned"
892 case EUserMessageTypeMPTauntUnlocked:
893 return "MPTauntUnlocked"
894 case EUserMessageTypeMPTauntLocked:
895 return "MPTauntLocked"
896 case EUserMessageTypeMPAllTauntsLocked:
897 return "MPAllTauntsLocked"
898 case EUserMessageTypePortalFX_Surface:
899 return "PortalFX_Surface"
900 case EUserMessageTypePaintWorld:
901 return "PaintWorld"
902 case EUserMessageTypePaintEntity:
903 return "PaintEntity"
904 case EUserMessageTypeChangePaintColor:
905 return "ChangePaintColor"
906 case EUserMessageTypePaintBombExplode:
907 return "PaintBombExplode"
908 case EUserMessageTypeRemoveAllPaint:
909 return "RemoveAllPaint"
910 case EUserMessageTypePaintAllSurfaces:
911 return "PaintAllSurfaces"
912 case EUserMessageTypeRemovePaint:
913 return "RemovePaint"
914 case EUserMessageTypeStartSurvey:
915 return "StartSurvey"
916 case EUserMessageTypeApplyHitBoxDamageEffect:
917 return "ApplyHitBoxDamageEffect"
918 case EUserMessageTypeSetMixLayerTriggerFactor:
919 return "SetMixLayerTriggerFactor"
920 case EUserMessageTypeTransitionFade:
921 return "TransitionFade"
922 case EUserMessageTypeScoreboardTempUpdate:
923 return "ScoreboardTempUpdate"
924 case EUserMessageTypeChallengeModCheatSession:
925 return "ChallengeModCheatSession"
926 case EUserMessageTypeChallengeModCloseAllUI:
927 return "ChallengeModCloseAllUI"
928 default:
929 return "Unknown"
90 } 930 }
91 svcUserMessage.Data = reader.TryReadBitsToSlice(uint64(svcUserMessage.Length))
92 writer.TempAppendLine("\t\tType: %d", svcUserMessage.Type)
93 writer.TempAppendLine("\t\tData: %v", svcUserMessage.Data)
94 return svcUserMessage
95} 931}
96 932
97// func byteToUserMessageType() { 933func readBitCoord(reader *bitreader.Reader) float32 {
98 934 const (
99// } 935 CoordIntBits uint64 = 14
936 CoordFracBits uint64 = 5
937 CoordDenom = 1 << CoordFracBits
938 CoordRes = 1.0 / CoordDenom
939 )
940 val := float32(0)
941 hasInt := reader.TryReadBool()
942 hasFrac := reader.TryReadBool()
943 if hasInt || hasFrac {
944 sign := reader.TryReadBool()
945 if hasInt {
946 val += float32(reader.TryReadBits(CoordIntBits) + 1)
947 }
948 if hasFrac {
949 val += float32(reader.TryReadBits(CoordFracBits)) * CoordRes
950 }
951 if sign {
952 val = -val
953 }
954 }
955 return val
956}