Skip to content

Commit

Permalink
fix: Update PUBG integration (#264)
Browse files Browse the repository at this point in the history
* fix: update pugb integration to work with latest version

* fix: add timeout to allow for file to write
  • Loading branch information
Segergren authored Jan 11, 2025
1 parent 5702778 commit 13ac1f8
Showing 1 changed file with 45 additions and 96 deletions.
141 changes: 45 additions & 96 deletions Classes/Integrations/PubgIntegration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace RePlays.Integrations {
Expand All @@ -18,15 +18,15 @@ public class MatchData {
public string FriendlyName { get; set; }
public int DemoFileLastOffset { get; set; }
public int SizeInBytes { get; set; }
public long Timestamp { get; set; } //Start of the game
public long Timestamp { get; set; } // Start of the game
public bool bIsLive { get; set; }
public bool bIncomplete { get; set; }
public bool bIsServerRecording { get; set; }
public bool bShouldKeep { get; set; }
public string GameVersion { get; set; }
public string Mode { get; set; }
public string RecordUserId { get; set; }
public string RecordUserNickName { get; set; } //Current players username
public string RecordUserNickName { get; set; } // Current players username
public string MapName { get; set; }
public bool bAllDeadOrWin { get; set; }
public List<object> Reports { get; set; }
Expand All @@ -45,53 +45,9 @@ public class DataOverview {
public string id { get; set; }
public string group { get; set; }
public string meta { get; set; }
public int time1 { get; set; } //milliseconds since MatchData.Timestamp
public int time1 { get; set; } // Milliseconds since MatchData.Timestamp
public int time2 { get; set; }
public string data { get; set; } //ASCII Shift Cipher encrypted game data
}

public class KillPlayerData {
[JsonPropertyName("*e8199c40d3")]
public string killerNetId { get; set; }
[JsonPropertyName("*3a79bf88c2")]
public string killerName { get; set; }
public string victimNetId { get; set; }
[JsonPropertyName("*c7a98fdd12")]
public string victimName { get; set; }
[JsonPropertyName("*83180ea500")]
public string damageCauseClassName { get; set; }
[JsonPropertyName("*834975f626")]
public string damageTypeCategory { get; set; }
[JsonPropertyName("*532cd7df97")]
public string damageReason { get; set; }
[JsonPropertyName("*967d42d4a0")]
public bool bGroggy { get; set; }
[JsonPropertyName("*af4e5308f0")]
public string killerPlayerId { get; set; }
[JsonPropertyName("*7441984e8b")]
public string victimPlayerId { get; set; }
}

public class DownedData {
[JsonPropertyName("*e8199c40d3")]
public string instigatorNetId { get; set; }
[JsonPropertyName("*3a79bf88c2")]
public string instigatorName { get; set; }
public string victimNetId { get; set; }
[JsonPropertyName("*c7a98fdd12")]
public string victimName { get; set; }
[JsonPropertyName("*83180ea500")]
public string damageCauseClassName { get; set; }
[JsonPropertyName("*834975f626")]
public string damageTypeCategory { get; set; }
[JsonPropertyName("*532cd7df97")]
public string damageReason { get; set; }
[JsonPropertyName("*967d42d4a0")]
public bool bGroggy { get; set; }
[JsonPropertyName("*af4e5308f0")]
public string instigatorPlayerId { get; set; }
[JsonPropertyName("*7441984e8b")]
public string victimPlayerId { get; set; }
public string data { get; set; } // Base64-encoded data
}

System.Timers.Timer timer = new System.Timers.Timer() {
Expand All @@ -100,6 +56,10 @@ public class DownedData {
private readonly string demoDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"TslGame\Saved\Demos");
private HashSet<string> oldDemos;
public override Task Start() {
if (!Directory.Exists(demoDirectory)) {
Directory.CreateDirectory(demoDirectory);
}

oldDemos = Directory.GetDirectories(demoDirectory).ToHashSet();
Logger.WriteLine("Starting PUBG integration");
timer.Elapsed += (sender, e) => {
Expand All @@ -113,8 +73,8 @@ public override Task Start() {
foreach (var demoPath in newDemos) {
Logger.WriteLine("Found new PUBG match data: " + demoPath);
HashSet<string> appliedBookmarks = new HashSet<string>();

//Get match data
Thread.Sleep(500);
// Get match data
string json = GetJsonFromFile(Path.Combine(demoPath, @"PUBG.replayinfo"));
MatchData matchData = JsonSerializer.Deserialize<MatchData>(json);

Expand All @@ -132,34 +92,38 @@ public override Task Start() {
}

public override Task Shutdown() {
//Wait two second to make sure the bookmark file has been added
Logger.WriteLine("Shutting down PUBG integration");
timer.Stop();
return Task.CompletedTask;
}

private void AddDownedBookmarks(string demoPath, MatchData matchData, HashSet<string> appliedBookmarks) {
//All downing are saved as individual files (ex. groggy0, groggy1, groggy3)
// All downing are saved as individual files (ex. groggy0, groggy1, groggy3)
string[] downedMetaFiles = Directory.GetFiles(demoPath + @"\events", "groggy*");
Logger.WriteLine("Found " + downedMetaFiles.Length + " downed players");
foreach (string downedPlayerFilePath in downedMetaFiles) {
//Get (encrypted) event data
// Get event data
string json = GetJsonFromFile(downedPlayerFilePath);
DataOverview dataOverview = JsonSerializer.Deserialize<DataOverview>(json);

//Decrypt data
string cipherEncodedData = Encoding.UTF8.GetString(Convert.FromBase64String(dataOverview.data));
DownedData downedData = JsonSerializer.Deserialize<DownedData>(DecryptAsciiShiftCipher(cipherEncodedData));
// Decode and create a list
string jsonData = Encoding.UTF8.GetString(Convert.FromBase64String(dataOverview.data));
Dictionary<string, object> downedDataDictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonData);
List<object> downedDataList = downedDataDictionary.Values.ToList();

// We need to get by index because PUBG changes variable name every patch
string instigatorName = downedDataList[1]?.ToString();
string victimName = downedDataList[3]?.ToString();

//If current user downed someone (not themselves)
if (downedData.instigatorName == matchData.RecordUserNickName && downedData.victimName != matchData.RecordUserNickName) {
Logger.WriteLine(downedData.instigatorName + " downed " + downedData.victimName);
// If current user downed someone (not themselves)
if (instigatorName == matchData.RecordUserNickName && victimName != matchData.RecordUserNickName) {
Logger.WriteLine(instigatorName + " downed " + victimName);

//DateTime at the event (downing)
// DateTime at the event (downing)
DateTime bookmarkDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTimeOffset.FromUnixTimeMilliseconds(matchData.Timestamp + dataOverview.time1).DateTime, TimeZoneInfo.Local);

//Add to a HashSet to make sure that RePlays bookmarks the actual downing (not the kill)
appliedBookmarks.Add(downedData.victimName);
// Add to a HashSet to make sure that RePlays bookmarks the actual downing (not the kill)
appliedBookmarks.Add(victimName);

Bookmark bookmark = new() {
type = Bookmark.BookmarkType.Kill
Expand All @@ -170,28 +134,33 @@ private void AddDownedBookmarks(string demoPath, MatchData matchData, HashSet<st
}

private void AddKillsBookmarks(string demoPath, MatchData matchData, HashSet<string> appliedBookmarks) {
//All kills are saved as individual files (ex. kill0, kill1, kill3)
// All kills are saved as individual files (ex. kill0, kill1, kill3)
string[] killsMetaFiles = Directory.GetFiles(demoPath + @"\events", "kill*");
Logger.WriteLine("Found " + killsMetaFiles.Length + " kills");
foreach (string killFilePath in killsMetaFiles) {
//Get (encrypted) event data
//Get event data
string json = GetJsonFromFile(killFilePath);
DataOverview killDataOverview = JsonSerializer.Deserialize<DataOverview>(json);

//Decrypt data
string cipherEncodedData = Encoding.UTF8.GetString(Convert.FromBase64String(killDataOverview.data));
KillPlayerData killData = JsonSerializer.Deserialize<KillPlayerData>(DecryptAsciiShiftCipher(cipherEncodedData));
// Decode and create a list
string jsonData = Encoding.UTF8.GetString(Convert.FromBase64String(killDataOverview.data));
Dictionary<string, object> killDataDictionary = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonData);
List<object> killDataList = killDataDictionary.Values.ToList();

//If current user kills someone (not themselves)
if (killData.killerName == matchData.RecordUserNickName && killData.victimName != matchData.RecordUserNickName) {
Logger.WriteLine(killData.killerName + " killed " + killData.victimName);
// We need to get by index because PUBG changes variable name every patch
string killerName = killDataList[1]?.ToString();
string victimName = killDataList[3]?.ToString();

//DateTime at the event (kill)
// If current user kills someone (not themselves)
if (killerName == matchData.RecordUserNickName && victimName != matchData.RecordUserNickName) {
Logger.WriteLine(killerName + " killed " + victimName);

// DateTime at the event (kill)
DateTime bookmarkDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTimeOffset.FromUnixTimeMilliseconds(matchData.Timestamp + killDataOverview.time1).DateTime, TimeZoneInfo.Local);
bool killedDirectlyWithoutDowning = appliedBookmarks.Add(killData.victimName);
bool killedDirectlyWithoutDowning = appliedBookmarks.Add(victimName);

//Only add the kill if I haven't downed the person before (this is known as an instant kill where the victim is alone left in their team)
//This is to prevent bookmarks at both downing and killing.
// Only add the kill if I haven't downed the person before (this is known as an instant kill where the victim is alone left in their team)
// This is to prevent bookmarks at both downing and killing.
if (killedDirectlyWithoutDowning) {
Bookmark bookmark = new() {
type = Bookmark.BookmarkType.Kill
Expand All @@ -204,32 +173,12 @@ private void AddKillsBookmarks(string demoPath, MatchData matchData, HashSet<str


private string GetJsonFromFile(string fileLocation) {
//The file includes random characters at the start and end
// The file includes random characters at the start and end
string json = File.ReadAllText(fileLocation);
int jsonStartIndex = json.IndexOf("{");
int jsonEndIndex = json.LastIndexOf("}") + 1;
return json.Substring(jsonStartIndex, jsonEndIndex - jsonStartIndex);
}

//PUBG uses ASCII Shift Cipher to encode data
private string DecryptAsciiShiftCipher(string encryptedText) {
int shift = 93;
string decryptedText = "";
foreach (char c in encryptedText) {
if (c >= '!' && c <= '|') {
int code = (int)c - shift;
if (code < (int)'!') {
code += 94;
}
decryptedText += (char)code;
}
else {
decryptedText += c;
}
}

return decryptedText;
}

}
}

0 comments on commit 13ac1f8

Please sign in to comment.