aboutsummaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
Diffstat (limited to 'backend')
-rw-r--r--backend/handlers/record.go44
-rw-r--r--backend/parser/parser.go94
2 files changed, 128 insertions, 10 deletions
diff --git a/backend/handlers/record.go b/backend/handlers/record.go
index 79e9d3b..c620430 100644
--- a/backend/handlers/record.go
+++ b/backend/handlers/record.go
@@ -3,12 +3,14 @@ package handlers
3import ( 3import (
4 "context" 4 "context"
5 "encoding/base64" 5 "encoding/base64"
6 "fmt"
6 "io" 7 "io"
7 "log" 8 "log"
8 "mime/multipart" 9 "mime/multipart"
9 "net/http" 10 "net/http"
10 "os" 11 "os"
11 "strconv" 12 "strconv"
13 "strings"
12 14
13 "github.com/gin-gonic/gin" 15 "github.com/gin-gonic/gin"
14 "github.com/google/uuid" 16 "github.com/google/uuid"
@@ -90,6 +92,7 @@ func CreateRecordWithDemo(c *gin.Context) {
90 } 92 }
91 var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string 93 var hostDemoUUID, hostDemoFileID, partnerDemoUUID, partnerDemoFileID string
92 var hostDemoScoreCount, hostDemoScoreTime int 94 var hostDemoScoreCount, hostDemoScoreTime int
95 var hostSteamID, partnerSteamID string
93 client := serviceAccount() 96 client := serviceAccount()
94 srv, err := drive.New(client) 97 srv, err := drive.New(client)
95 if err != nil { 98 if err != nil {
@@ -127,7 +130,7 @@ func CreateRecordWithDemo(c *gin.Context) {
127 c.JSON(http.StatusOK, models.ErrorResponse(err.Error())) 130 c.JSON(http.StatusOK, models.ErrorResponse(err.Error()))
128 return 131 return
129 } 132 }
130 hostDemoScoreCount, hostDemoScoreTime, err = parser.ProcessDemo("backend/parser/" + uuid + ".dem") 133 hostDemoScoreCount, hostDemoScoreTime, hostSteamID, partnerSteamID, err = parser.ProcessDemo("backend/parser/" + uuid + ".dem")
131 if err != nil { 134 if err != nil {
132 deleteFile(srv, file.Id) 135 deleteFile(srv, file.Id)
133 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordProcessDemoFail, err.Error()) 136 CreateLog(user.(models.User).SteamID, LogTypeRecord, LogDescriptionCreateRecordProcessDemoFail, err.Error())
@@ -140,6 +143,25 @@ func CreateRecordWithDemo(c *gin.Context) {
140 c.JSON(http.StatusOK, models.ErrorResponse("Processing demo went wrong. Please contact a web admin and provide the demo in question.")) 143 c.JSON(http.StatusOK, models.ErrorResponse("Processing demo went wrong. Please contact a web admin and provide the demo in question."))
141 return 144 return
142 } 145 }
146 if !isCoop {
147 hostSplit := strings.Split(hostSteamID, ":")
148 if hostSplit[len(hostSplit)-1] != user.(models.User).SteamID {
149 c.JSON(http.StatusOK, models.ErrorResponse(fmt.Sprintf("Host SteamID from demo and request does not match! Check your submission and try again.\nDemo Host SteamID: %s\nRequest Host SteamID: %s", hostSplit[len(hostSplit)-1], user.(models.User).SteamID)))
150 return
151 }
152 } else {
153 partnerSplit := strings.Split(partnerSteamID, ":")
154 if partnerSplit[len(partnerSplit)-1] != record.PartnerID {
155 c.JSON(http.StatusOK, models.ErrorResponse(fmt.Sprintf("Partner SteamID from demo and request does not match! Check your submission and try again.\nDemo Partner SteamID: %s\nRequest Partner SteamID: %s", partnerSplit[len(partnerSplit)-1], record.PartnerID)))
156 return
157 }
158 var verifyCoopSteamID string
159 database.DB.QueryRow("SELECT steam_id FROM users WHERE steam_id = $1", record.PartnerID).Scan(&verifyCoopSteamID)
160 if verifyCoopSteamID != record.PartnerID {
161 c.JSON(http.StatusOK, models.ErrorResponse("Given partner SteamID does not match an account on LPHUB."))
162 return
163 }
164 }
143 if i == 0 { 165 if i == 0 {
144 hostDemoFileID = file.Id 166 hostDemoFileID = file.Id
145 hostDemoUUID = uuid 167 hostDemoUUID = uuid
@@ -384,3 +406,23 @@ func createFile(service *drive.Service, name string, mimeType string, content io
384func deleteFile(service *drive.Service, fileId string) { 406func deleteFile(service *drive.Service, fileId string) {
385 service.Files.Delete(fileId) 407 service.Files.Delete(fileId)
386} 408}
409
410// Convert from SteamID64 to Legacy SteamID bits
411func convertSteamID(steamID64 int64) int64 {
412 return (steamID64 >> 1) & 0x7FFFFFF
413}
414
415// Convert from Legacy SteamID bits to SteamID64
416func convertSteamID64(steamID string) int64 {
417 const baseSteam64ID = 76561197960265728 // Origin of this value remains unclear
418 parts := strings.Split(steamID, ":")
419 userId, err := strconv.Atoi(parts[2])
420 if err != nil {
421 return 0
422 }
423 steam64ID := baseSteam64ID + int64(userId*2) // Reason for multiplication by 2 is unknown
424 if parts[1] == "1" {
425 steam64ID++
426 }
427 return steam64ID
428}
diff --git a/backend/parser/parser.go b/backend/parser/parser.go
index db84256..1a80d4a 100644
--- a/backend/parser/parser.go
+++ b/backend/parser/parser.go
@@ -9,10 +9,10 @@ import (
9) 9)
10 10
11// Don't try to understand it, feel it. 11// Don't try to understand it, feel it.
12func ProcessDemo(filePath string) (portalCount int, tickCount int, err error) { 12func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID string, partnerSteamID string, err error) {
13 file, err := os.Open(filePath) 13 file, err := os.Open(filePath)
14 if err != nil { 14 if err != nil {
15 return 0, 0, err 15 return 0, 0, "", "", err
16 } 16 }
17 reader := bitreader.NewReader(file, true) 17 reader := bitreader.NewReader(file, true)
18 demoFileStamp := reader.TryReadString() 18 demoFileStamp := reader.TryReadString()
@@ -20,13 +20,13 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, err error) {
20 networkProtocol := reader.TryReadSInt32() 20 networkProtocol := reader.TryReadSInt32()
21 reader.SkipBytes(1056) 21 reader.SkipBytes(1056)
22 if demoFileStamp != "HL2DEMO" { 22 if demoFileStamp != "HL2DEMO" {
23 return 0, 0, errors.New("invalid demo file stamp") 23 return 0, 0, "", "", errors.New("invalid demo file stamp")
24 } 24 }
25 if demoProtocol != 4 { 25 if demoProtocol != 4 {
26 return 0, 0, errors.New("this parser only supports demos from new engine") 26 return 0, 0, "", "", errors.New("this parser only supports demos from new engine")
27 } 27 }
28 if networkProtocol != 2001 { 28 if networkProtocol != 2001 {
29 return 0, 0, errors.New("this parser only supports demos from portal 2") 29 return 0, 0, "", "", errors.New("this parser only supports demos from portal 2")
30 } 30 }
31 for { 31 for {
32 packetType := reader.TryReadUInt8() 32 packetType := reader.TryReadUInt8()
@@ -217,21 +217,97 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, err error) {
217 case 33: 217 case 33:
218 packetReader.SkipBits(packetReader.TryReadBits(32)) 218 packetReader.SkipBits(packetReader.TryReadBits(32))
219 default: 219 default:
220 panic("unknown msg type") 220 return 0, 0, "", "", errors.New("unknown msg type")
221 } 221 }
222 } 222 }
223 case 3, 7: 223 case 3, 7:
224 case 4, 6, 9: 224 case 4, 6:
225 reader.SkipBytes(uint64(reader.TryReadSInt32())) 225 reader.SkipBytes(uint64(reader.TryReadSInt32()))
226 case 5, 8: 226 case 5, 8:
227 reader.SkipBits(32) 227 reader.SkipBits(32)
228 reader.SkipBytes(uint64(reader.TryReadSInt32())) 228 reader.SkipBytes(uint64(reader.TryReadSInt32()))
229 case 9:
230 type StringTableClass struct {
231 Name string
232 Data string
233 }
234 type StringTableEntry struct {
235 Name string
236 EntryData any
237 }
238 type StringTable struct {
239 Name string
240 TableEntries []StringTableEntry
241 Classes []StringTableClass
242 }
243 size := reader.TryReadSInt32()
244 stringTableReader := bitreader.NewReaderFromBytes(reader.TryReadBytesToSlice(uint64(size)), true)
245 tableCount := stringTableReader.TryReadBits(8)
246 guidCount := 0
247 for i := 0; i < int(tableCount); i++ {
248 tableName := stringTableReader.TryReadString()
249 entryCount := stringTableReader.TryReadBits(16)
250 for i := 0; i < int(entryCount); i++ {
251 stringTableReader.TryReadString()
252 if stringTableReader.TryReadBool() {
253 byteLen, err := stringTableReader.ReadBits(16)
254 if err != nil {
255 return 0, 0, "", "", errors.New("error on reading entry length")
256 }
257 stringTableEntryReader := bitreader.NewReaderFromBytes(stringTableReader.TryReadBytesToSlice(byteLen), true)
258 if tableName == "userinfo" {
259 const SignedGuidLen int32 = 32
260 const MaxPlayerNameLength int32 = 32
261 userInfo := struct {
262 SteamID uint64
263 Name string
264 UserID int32
265 GUID string
266 FriendsID uint32
267 FriendsName string
268 FakePlayer bool
269 IsHltv bool
270 CustomFiles []uint32
271 FilesDownloaded uint8
272 }{
273 SteamID: stringTableEntryReader.TryReadUInt64(),
274 Name: stringTableEntryReader.TryReadStringLength(uint64(MaxPlayerNameLength)),
275 UserID: stringTableEntryReader.TryReadSInt32(),
276 GUID: stringTableEntryReader.TryReadStringLength(uint64(SignedGuidLen) + 1),
277 }
278 stringTableEntryReader.SkipBytes(3)
279 userInfo.FriendsID = stringTableEntryReader.TryReadUInt32()
280 userInfo.FriendsName = stringTableEntryReader.TryReadStringLength(uint64(MaxPlayerNameLength))
281 userInfo.FakePlayer = stringTableEntryReader.TryReadUInt8() != 0
282 userInfo.IsHltv = stringTableEntryReader.TryReadUInt8() != 0
283 stringTableEntryReader.SkipBytes(2)
284 userInfo.CustomFiles = []uint32{stringTableEntryReader.TryReadUInt32(), stringTableEntryReader.TryReadUInt32(), stringTableEntryReader.TryReadUInt32(), stringTableEntryReader.TryReadUInt32()}
285 userInfo.FilesDownloaded = stringTableEntryReader.TryReadUInt8()
286 stringTableEntryReader.SkipBytes(3)
287 if guidCount == 0 {
288 hostSteamID = userInfo.GUID
289 } else if guidCount == 1 {
290 partnerSteamID = userInfo.GUID
291 }
292 }
293 }
294 }
295 if stringTableReader.TryReadBool() {
296 classCount := stringTableReader.TryReadBits(16)
297 for i := 0; i < int(classCount); i++ {
298 stringTableReader.TryReadString()
299 if stringTableReader.TryReadBool() {
300 stringTableReader.TryReadStringLength(uint64(stringTableReader.TryReadUInt16()))
301 }
302 }
303 }
304 }
229 default: 305 default:
230 return 0, 0, errors.New("invalid packet type") 306 return 0, 0, "", "", errors.New("invalid packet type")
231 } 307 }
232 if packetType == 7 { 308 if packetType == 7 {
233 break 309 break
234 } 310 }
235 } 311 }
236 return portalCount, tickCount, nil 312 return portalCount, tickCount, hostSteamID, partnerSteamID, nil
237} 313}