diff options
| author | Arda Serdar Pektezol <1669855+pektezol@users.noreply.github.com> | 2024-01-28 17:25:03 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-28 17:25:03 +0300 |
| commit | 344b5888f5de2707d9f78a27ce48f3731cecc034 (patch) | |
| tree | d079aa28eea9cf746ff86bb620995aea5aeee91b | |
| parent | fix: time (#137) (diff) | |
| download | lphub-344b5888f5de2707d9f78a27ce48f3731cecc034.tar.gz lphub-344b5888f5de2707d9f78a27ce48f3731cecc034.tar.bz2 lphub-344b5888f5de2707d9f78a27ce48f3731cecc034.zip | |
feat: check steam ids when uploading demo (#148)
| -rw-r--r-- | backend/handlers/record.go | 44 | ||||
| -rw-r--r-- | backend/parser/parser.go | 94 |
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 | |||
| 3 | import ( | 3 | import ( |
| 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 | |||
| 384 | func deleteFile(service *drive.Service, fileId string) { | 406 | func 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 | ||
| 411 | func convertSteamID(steamID64 int64) int64 { | ||
| 412 | return (steamID64 >> 1) & 0x7FFFFFF | ||
| 413 | } | ||
| 414 | |||
| 415 | // Convert from Legacy SteamID bits to SteamID64 | ||
| 416 | func 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. |
| 12 | func ProcessDemo(filePath string) (portalCount int, tickCount int, err error) { | 12 | func 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 | } |