Optimize RPC event parsing

This commit is contained in:
Natsumi
2022-05-31 10:38:33 +12:00
parent 877b4b8954
commit 29c0daf436
3 changed files with 184 additions and 134 deletions
+34 -2
View File
@@ -5,18 +5,22 @@
// For a copy, see <https://opensource.org/licenses/MIT>. // For a copy, see <https://opensource.org/licenses/MIT>.
using CefSharp; using CefSharp;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic;
using System.IO.Pipes; using System.IO.Pipes;
using System.Text; using System.Text;
namespace VRCX namespace VRCX
{ {
class IPCClient internal class IPCClient
{ {
private NamedPipeServerStream _ipcServer; private NamedPipeServerStream _ipcServer;
private byte[] _recvBuffer = new byte[1024 * 8]; private byte[] _recvBuffer = new byte[1024 * 8];
private string _currentPacket; private string _currentPacket;
private VRCEventDeserialization.EventEntry _eventEntry;
private readonly VRCEventDeserialization eventDeserialization = new VRCEventDeserialization();
public IPCClient(NamedPipeServerStream ipcServer) public IPCClient(NamedPipeServerStream ipcServer)
{ {
@@ -51,6 +55,34 @@ namespace VRCX
if (string.IsNullOrEmpty(packet)) if (string.IsNullOrEmpty(packet))
continue; continue;
var eventData = System.Text.Json.JsonSerializer.Deserialize<VRCEventDeserialization.EventData>(packet);
if (eventData.type == "OnEvent" && eventData.OnEventData.Code == 6)
{
var byteArray = Convert.FromBase64String(eventData.OnEventData.Parameters[245].ToString());
_eventEntry = eventDeserialization.DeserializeData(byteArray);
if (VRCEventDeserialization.ignoreEvents.Contains(_eventEntry.EventType))
continue;
_eventEntry.type = "VRCEvent";
_eventEntry.dt = eventData.dt;
_eventEntry.senderId = eventData.OnEventData.Sender;
var jsonData = System.Text.Json.JsonSerializer.Serialize(_eventEntry);
MainForm.Instance.Browser.ExecuteScriptAsync("$app.ipcEvent", jsonData);
continue;
}
else if (eventData.type == "OnEvent" && eventData.OnEventData.Code == 7)
{
_eventEntry = new VRCEventDeserialization.EventEntry
{
type = "Event7",
dt = eventData.dt,
senderId = eventData.OnEventData.Sender
};
var jsonData = System.Text.Json.JsonSerializer.Serialize(_eventEntry);
MainForm.Instance.Browser.ExecuteScriptAsync("$app.ipcEvent", jsonData);
continue;
}
MainForm.Instance.Browser.ExecuteScriptAsync("$app.ipcEvent", packet); MainForm.Instance.Browser.ExecuteScriptAsync("$app.ipcEvent", packet);
} }
@@ -71,4 +103,4 @@ namespace VRCX
ipcClient.EndWrite(asyncResult); ipcClient.EndWrite(asyncResult);
} }
} }
} }
+63 -2
View File
@@ -6,10 +6,11 @@ using System.Text;
namespace VRCX namespace VRCX
{ {
class VRCEventDeserialization internal class VRCEventDeserialization
{ {
private byte[] byteData; private byte[] byteData;
private int byteOffset; private int byteOffset;
private static readonly Dictionary<int, Type> DataType = new Dictionary<int, Type> { private static readonly Dictionary<int, Type> DataType = new Dictionary<int, Type> {
{2, typeof(byte)}, {2, typeof(byte)},
{3, typeof(double)}, {3, typeof(double)},
@@ -29,10 +30,55 @@ namespace VRCX
public class EventEntry public class EventEntry
{ {
public string type { get; set; }
public string dt { get; set; }
public int senderId { get; set; }
public int Type { get; set; } public int Type { get; set; }
public string EventType { get; set; } public string EventType { get; set; }
public object Data { get; set; } public object Data { get; set; }
} }
public class EventData
{
public string dt { get; set; }
public string type { get; set; }
public EventDataContainer OnEventData { get; set; }
public OperationContainer OnOperationResponseData { get; set; }
}
public class EventDataContainer
{
public byte Code { get; set; }
public int Sender { get; set; }
public Dictionary<byte, object> Parameters { get; set; }
public byte SenderKey { get; set; }
public byte CustomDataKey { get; set; }
}
public class OperationContainer
{
public byte OperationCode { get; set; }
public short ReturnCode { get; set; }
public string DebugMessage { get; set; }
public Dictionary<byte, object> Parameters { get; set; }
}
public static readonly List<string> ignoreEvents = new List<string>
{
"ReceiveVoiceStatsSyncRPC",
"initUSpeakSenderRPC",
"SanityCheck",
"UdonSyncRunProgramAsRPC",
"InformOfBadConnection",
"SetTimerRPC",
"IncrementPortalPlayerCountRPC",
"PlayEffect",
"PlayEmoteRPC",
"CancelRPC",
"_SendOnSpawn",
"RefreshAvatar",
"InternalApplyOverrideRPC"
};
private byte DeserializeByte() private byte DeserializeByte()
{ {
@@ -142,34 +188,49 @@ namespace VRCX
{ {
case 1: case 1:
return null; return null;
case 2: case 2:
return DeserializeByte(); return DeserializeByte();
case 3: case 3:
return DeserializeDouble(); return DeserializeDouble();
case 4: case 4:
return DeserializeFloat(); return DeserializeFloat();
case 5: case 5:
return DeserializeInt(); return DeserializeInt();
case 6: case 6:
return DeserializeShort(); return DeserializeShort();
case 7: case 7:
return DeserializeInt64(); return DeserializeInt64();
case 8: case 8:
return DeserializeBool(); return DeserializeBool();
case 9: case 9:
return DeserializeString(); return DeserializeString();
case 10: case 10:
return DeserializeObjectArray(); return DeserializeObjectArray();
case 11: case 11:
return DeserializeTypeArray(); return DeserializeTypeArray();
case 100: case 100:
return DeserializeVector2(); return DeserializeVector2();
case 101: case 101:
return DeserializeVector3(); return DeserializeVector3();
case 102: case 102:
return DeserializeVector4(); return DeserializeVector4();
case 103: case 103:
return DeserializeQuaternion(); return DeserializeQuaternion();
default: default:
throw new Exception("Ignoring data type: " + type); throw new Exception("Ignoring data type: " + type);
} }
@@ -205,4 +266,4 @@ namespace VRCX
return eventEntry; return eventEntry;
} }
} }
} }
+87 -130
View File
@@ -8637,9 +8637,7 @@ speechSynthesis.getVoices();
}; };
$app.methods.parsePhotonEvent = function (data, gameLogDate) { $app.methods.parsePhotonEvent = function (data, gameLogDate) {
if (data.Code === 7) { if (data.Code === 226) {
this.photonEvent7List.set(data.Sender, gameLogDate);
} else if (data.Code === 226) {
// nothing // nothing
} else if (data.Code === 253) { } else if (data.Code === 253) {
// SetUserProperties // SetUserProperties
@@ -8831,41 +8829,16 @@ speechSynthesis.getVoices();
hasInstantiated: true hasInstantiated: true
}); });
} }
} else if (data.Code === 6) { }
// VRC Event };
var senderId = data.Parameters[254];
AppApi.DeserializeVrcEvent(data.Parameters[245]).then((json) => { $app.methods.parseVRCEvent = function (eventData) {
try { // VRC Event
var eventData = JSON.parse(json); var senderId = eventData.senderId;
} catch { var datetime = eventData.dt;
console.error( if (this.debugPhotonLogging) {
'error parsing DeserializeVrcEvent json:', console.log('VrcEvent:', eventData);
json }
);
return;
}
if (
eventData.EventType === 'ReceiveVoiceStatsSyncRPC' ||
eventData.EventType === 'initUSpeakSenderRPC' ||
eventData.EventType === 'SanityCheck' ||
(eventData.EventType === 'UdonSyncRunProgramAsRPC' &&
eventData.Data[0] !== 'Beep') ||
eventData.EventType === 'InformOfBadConnection' ||
eventData.EventType === 'SetTimerRPC' ||
eventData.EventType === 'IncrementPortalPlayerCountRPC' ||
eventData.EventType === 'PlayEffect' ||
eventData.EventType === 'PlayEmoteRPC' ||
eventData.EventType === 'CancelRPC' ||
eventData.EventType === '_SendOnSpawn' ||
eventData.EventType === 'RefreshAvatar' ||
eventData.EventType === 'InternalApplyOverrideRPC'
) {
// Trash
return;
}
if (this.debugPhotonLogging) {
console.log('VrcEvent:', eventData);
}
if ( if (
eventData.EventType === '_InstantiateObject' && eventData.EventType === '_InstantiateObject' &&
eventData.Data[0] === 'Portals/PortalInternalDynamic' eventData.Data[0] === 'Portals/PortalInternalDynamic'
@@ -8875,102 +8848,85 @@ speechSynthesis.getVoices();
} else if ( } else if (
eventData.EventType === '_DestroyObject' && eventData.EventType === '_DestroyObject' &&
this.lastPortalList.has(eventData.Data[0]) this.lastPortalList.has(eventData.Data[0])
) { ) {
var portalId = eventData.Data[0]; var portalId = eventData.Data[0];
var date = this.lastPortalList.get(portalId); var date = this.lastPortalList.get(portalId);
var time = timeToText(Date.parse(gameLogDate) - date); var time = timeToText(Date.parse(datetime) - date);
this.addEntryPhotonEvent({ this.addEntryPhotonEvent({
photonId: senderId, photonId: senderId,
text: `DeletedPortal ${time}`, text: `DeletedPortal ${time}`,
type: 'DeletedPortal', type: 'DeletedPortal',
created_at: gameLogDate created_at: datetime
}); });
return; return;
} else if (eventData.EventType === 'ConfigurePortal') { } else if (eventData.EventType === 'ConfigurePortal') {
var instanceId = `${eventData.Data[0]}:${eventData.Data[1]}`; var instanceId = `${eventData.Data[0]}:${eventData.Data[1]}`;
if (this.lastPortalId) { if (this.lastPortalId) {
this.lastPortalList.set( this.lastPortalList.set(
this.lastPortalId, this.lastPortalId,
Date.parse(gameLogDate) Date.parse(datetime)
); );
this.lastPortalId = ''; this.lastPortalId = '';
} }
var displayName = this.getDisplayNameFromPhotonId(senderId); var displayName = this.getDisplayNameFromPhotonId(senderId);
if (displayName) { if (displayName) {
var ref1 = { var ref1 = {
id: this.getUserIdFromPhotonId(senderId), id: this.getUserIdFromPhotonId(senderId),
displayName displayName
}; };
this.parsePhotonPortalSpawn( this.parsePhotonPortalSpawn(datetime, instanceId, ref1);
gameLogDate, }
instanceId, return;
ref1 } else if (eventData.Type > 34) {
); var entry = {
} created_at: datetime,
return; type: 'Event',
} else if (eventData.Type > 34) { data: `${displayName} called non existent RPC ${eventData.Type}`
var entry = { };
created_at: gameLogDate,
type: 'Event',
data: `${displayName} called non existent RPC ${eventData.Type}`
};
this.addPhotonEventToGameLog(entry); this.addPhotonEventToGameLog(entry);
} }
if (eventData.Type === 14) { if (eventData.Type === 14) {
if (eventData.EventType === 'ChangeVisibility') { if (eventData.EventType === 'ChangeVisibility') {
if (eventData.Data[0] === true) { if (eventData.Data[0] === true) {
var text = 'EnableCamera'; var text = 'EnableCamera';
} else if (eventData.Data[0] === false) { } else if (eventData.Data[0] === false) {
var text = 'DisableCamera'; var text = 'DisableCamera';
}
} else if (
eventData.EventType === 'UdonSyncRunProgramAsRPC' &&
eventData.Data[0] === 'Beep'
) {
if (!this.isRpcWorld(this.lastLocation.location)) {
return;
}
var text = 'Beep';
} else if (
eventData.EventType === 'ReloadAvatarNetworkedRPC'
) {
var text = 'AvatarReset';
} else if (eventData.EventType === 'SpawnEmojiRPC') {
var text = `SpawnEmoji ${
this.photonEmojis[eventData.Data]
}`;
} else {
var eventVrc = '';
if (eventData.Data) {
eventVrc = ` ${JSON.stringify(
eventData.Data
).replace(/"([^(")"]+)":/g, '$1:')}`;
}
var text = `${eventData.EventType}${eventVrc}`;
}
this.addEntryPhotonEvent({
photonId: senderId,
text,
type: 'Event',
created_at: gameLogDate
});
} else {
var eventType = '';
if (eventData.EventType) {
eventType = ` ${JSON.stringify(
eventData.EventType
).replace(/"([^(")"]+)":/g, '$1:')}`;
}
if (this.debugPhotonLogging) {
var displayName =
this.getDisplayNameFromPhotonId(senderId);
var feed = `RPC ${displayName} ${
this.photonEventType[eventData.Type]
}${eventType}`;
console.log('VrcRpc:', feed);
}
} }
} else if (eventData.EventType === 'ReloadAvatarNetworkedRPC') {
var text = 'AvatarReset';
} else if (eventData.EventType === 'SpawnEmojiRPC') {
var text = `SpawnEmoji ${this.photonEmojis[eventData.Data]}`;
} else {
var eventVrc = '';
if (eventData.Data) {
eventVrc = ` ${JSON.stringify(eventData.Data).replace(
/"([^(")"]+)":/g,
'$1:'
)}`;
}
var text = `${eventData.EventType}${eventVrc}`;
}
this.addEntryPhotonEvent({
photonId: senderId,
text,
type: 'Event',
created_at: datetime
}); });
} else {
var eventType = '';
if (eventData.EventType) {
eventType = ` ${JSON.stringify(eventData.EventType).replace(
/"([^(")"]+)":/g,
'$1:'
)}`;
}
if (this.debugPhotonLogging) {
var displayName = this.getDisplayNameFromPhotonId(senderId);
var feed = `RPC ${displayName} ${
this.photonEventType[eventData.Type]
}${eventType}`;
console.log('VrcRpc:', feed);
}
} }
}; };
@@ -18797,20 +18753,14 @@ speechSynthesis.getVoices();
} }
switch (data.type) { switch (data.type) {
case 'OnEvent': case 'OnEvent':
if ( if (this.debugPhotonLogging) {
this.debugPhotonLogging &&
data.OnEventData.Code !== 6 &&
data.OnEventData.Code !== 7
) {
console.log( console.log(
'OnEvent', 'OnEvent',
data.OnEventData.Code, data.OnEventData.Code,
data.OnEventData data.OnEventData
); );
} }
if (data.OnEventData.Code !== 7) { this.photonEventPulse();
this.photonEventPulse();
}
this.parsePhotonEvent(data.OnEventData, data.dt); this.parsePhotonEvent(data.OnEventData, data.dt);
break; break;
case 'OnOperationResponse': case 'OnOperationResponse':
@@ -18827,6 +18777,13 @@ speechSynthesis.getVoices();
); );
this.photonEventPulse(); this.photonEventPulse();
break; break;
case 'VRCEvent':
this.photonEventPulse();
this.parseVRCEvent(data);
break;
case 'Event7':
this.photonEvent7List.set(data.senderId, data.dt);
break;
case 'Ping': case 'Ping':
if (!this.photonLoggingEnabled) { if (!this.photonLoggingEnabled) {
this.photonLoggingEnabled = true; this.photonLoggingEnabled = true;