mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 06:56:04 +02:00
Optimize RPC event parsing
This commit is contained in:
+34
-2
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user