Files
2026-02-21 16:45:37 +08:00

474 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using Oculus.Platform.Models;
using UnityEngine;
namespace Oculus.Platform.Samples.VrHoops
{
public class P2PManager
{
private class RemotePlayerData
{
public PeerConnectionState state;
public RemotePlayer player;
public float remoteTimeOffset;
public float lastReceivedBallsTime;
public readonly Dictionary<int, P2PNetworkBall> activeBalls = new Dictionary<int, P2PNetworkBall>();
}
public delegate float StartTimeOffer(float remoteTime);
private readonly Dictionary<ulong, RemotePlayerData> m_remotePlayers = new Dictionary<ulong, RemotePlayerData>();
private float m_timeForNextBallUpdate;
private const byte TIME_SYNC_MESSAGE = 1;
private const uint TIME_SYNC_MESSAGE_SIZE = 5u;
private const int TIME_SYNC_MESSAGE_COUNT = 7;
private const byte START_TIME_MESSAGE = 2;
private const uint START_TIME_MESSAGE_SIZE = 5u;
private const byte BACKBOARD_UPDATE_MESSAGE = 3;
private const uint BACKBOARD_UPDATE_MESSAGE_SIZE = 41u;
private const byte LOCAL_BALLS_UPDATE_MESSAGE = 4;
private const uint LOCAL_BALLS_UPDATE_MESSATE_SIZE_MAX = 353u;
private const float LOCAL_BALLS_UPDATE_DELAY = 0.1f;
private const byte SCORE_UPDATE_MESSAGE = 5;
private const uint SCORE_UPDATE_MESSAGE_SIZE = 5u;
private readonly Dictionary<int, P2PNetworkBall> m_localBalls = new Dictionary<int, P2PNetworkBall>();
private readonly byte[] readBuffer = new byte[353];
private readonly Dictionary<ulong, List<float>> m_remoteSyncTimeCache = new Dictionary<ulong, List<float>>();
private readonly Dictionary<ulong, float> m_remoteSentTimeCache = new Dictionary<ulong, float>();
private StartTimeOffer m_startTimeOfferCallback;
public StartTimeOffer StartTimeOfferCallback
{
private get
{
return m_startTimeOfferCallback;
}
set
{
m_startTimeOfferCallback = value;
}
}
public P2PManager()
{
Net.SetPeerConnectRequestCallback(PeerConnectRequestCallback);
Net.SetConnectionStateChangedCallback(ConnectionStateChangedCallback);
}
public void UpdateNetwork()
{
if (m_remotePlayers.Count == 0)
{
return;
}
Packet packet;
while ((packet = Net.ReadPacket()) != null)
{
if (m_remotePlayers.ContainsKey(packet.SenderID))
{
packet.ReadBytes(readBuffer);
switch (readBuffer[0])
{
case 1:
ReadTimeSyncMessage(packet.SenderID, readBuffer);
break;
case 2:
ReceiveMatchStartTimeOffer(packet.SenderID, readBuffer);
break;
case 3:
ReceiveBackboardUpdate(packet.SenderID, readBuffer);
break;
case 4:
ReceiveBallTransforms(packet.SenderID, readBuffer, packet.Size);
break;
case 5:
ReceiveScoredUpdate(packet.SenderID, readBuffer);
break;
}
}
}
if (Time.time >= m_timeForNextBallUpdate && m_localBalls.Count > 0)
{
SendLocalBallTransforms();
}
}
public void AddRemotePlayer(RemotePlayer player)
{
if (!m_remotePlayers.ContainsKey(player.ID))
{
m_remotePlayers[player.ID] = new RemotePlayerData();
m_remotePlayers[player.ID].state = PeerConnectionState.Unknown;
m_remotePlayers[player.ID].player = player;
if (PlatformManager.MyID < player.ID)
{
Debug.Log("P2P Try Connect to: " + player.ID);
Net.Connect(player.ID);
}
}
}
public void DisconnectAll()
{
foreach (ulong key in m_remotePlayers.Keys)
{
Net.Close(key);
}
m_remotePlayers.Clear();
}
private void PeerConnectRequestCallback(Message<Oculus.Platform.Models.NetworkingPeer> msg)
{
if (m_remotePlayers.ContainsKey(msg.Data.ID))
{
Debug.LogFormat("P2P Accepting Connection request from {0}", msg.Data.ID);
Net.Accept(msg.Data.ID);
}
else
{
Debug.LogFormat("P2P Ignoring unauthorized Connection request from {0}", msg.Data.ID);
}
}
private void ConnectionStateChangedCallback(Message<Oculus.Platform.Models.NetworkingPeer> msg)
{
Debug.LogFormat("P2P {0} Connection state changed to {1}", msg.Data.ID, msg.Data.State);
if (!m_remotePlayers.ContainsKey(msg.Data.ID))
{
return;
}
m_remotePlayers[msg.Data.ID].state = msg.Data.State;
switch (msg.Data.State)
{
case PeerConnectionState.Connected:
if (PlatformManager.MyID < msg.Data.ID)
{
SendTimeSyncMessage(msg.Data.ID);
}
break;
case PeerConnectionState.Timeout:
if (PlatformManager.MyID < msg.Data.ID)
{
Net.Connect(msg.Data.ID);
}
break;
case PeerConnectionState.Closed:
m_remotePlayers.Remove(msg.Data.ID);
break;
}
}
private void SendTimeSyncMessage(ulong remoteID)
{
if (!m_remoteSyncTimeCache.ContainsKey(remoteID))
{
m_remoteSyncTimeCache[remoteID] = new List<float>();
}
float realtimeSinceStartup = Time.realtimeSinceStartup;
m_remoteSentTimeCache[remoteID] = realtimeSinceStartup;
byte[] array = new byte[5] { 1, 0, 0, 0, 0 };
int offset = 1;
PackFloat(realtimeSinceStartup, array, ref offset);
Net.SendPacket(remoteID, array, SendPolicy.Reliable);
}
private void ReadTimeSyncMessage(ulong remoteID, byte[] msg)
{
if (!m_remoteSentTimeCache.ContainsKey(remoteID))
{
SendTimeSyncMessage(remoteID);
return;
}
int offset = 1;
float num = UnpackFloat(msg, ref offset);
float realtimeSinceStartup = Time.realtimeSinceStartup;
float num2 = (realtimeSinceStartup - m_remoteSentTimeCache[remoteID]) / 2f;
float item = realtimeSinceStartup - (num + num2);
m_remoteSyncTimeCache[remoteID].Add(item);
if (m_remoteSyncTimeCache[remoteID].Count < 7)
{
SendTimeSyncMessage(remoteID);
return;
}
if (PlatformManager.MyID < remoteID)
{
SendTimeSyncMessage(remoteID);
}
m_remoteSyncTimeCache[remoteID].Sort();
float num3 = m_remoteSyncTimeCache[remoteID][3];
double num4 = 0.0;
foreach (float item2 in m_remoteSyncTimeCache[remoteID])
{
num4 += (double)item2;
}
num4 /= 7.0;
double num5 = 0.0;
foreach (float item3 in m_remoteSyncTimeCache[remoteID])
{
num5 += (num4 - (double)item3) * (num4 - (double)item3);
}
num5 = Math.Sqrt(num5) / 7.0;
num4 = 0.0;
int num6 = 0;
foreach (float item4 in m_remoteSyncTimeCache[remoteID])
{
if ((double)Math.Abs(item4 - num3) < num5)
{
num4 += (double)item4;
num6++;
}
}
num4 /= (double)num6;
Debug.LogFormat("Time offset to {0} is {1}", remoteID, num4);
m_remoteSyncTimeCache.Remove(remoteID);
m_remoteSentTimeCache.Remove(remoteID);
m_remotePlayers[remoteID].remoteTimeOffset = (float)num4;
OfferMatchStartTime();
}
private float ShiftRemoteTime(ulong remoteID, float remoteTime)
{
if (m_remotePlayers.ContainsKey(remoteID))
{
return remoteTime + m_remotePlayers[remoteID].remoteTimeOffset;
}
return remoteTime;
}
private void OfferMatchStartTime()
{
byte[] array = new byte[5] { 2, 0, 0, 0, 0 };
int offset = 1;
PackFloat(StartTimeOfferCallback(0f), array, ref offset);
foreach (ulong key in m_remotePlayers.Keys)
{
if (m_remotePlayers[key].state == PeerConnectionState.Connected)
{
Net.SendPacket(key, array, SendPolicy.Reliable);
}
}
}
private void ReceiveMatchStartTimeOffer(ulong remoteID, byte[] msg)
{
int offset = 1;
float remoteTime = UnpackTime(remoteID, msg, ref offset);
StartTimeOfferCallback(remoteTime);
}
public void SendBackboardUpdate(float time, Vector3 pos, Vector3 moveDir, Vector3 nextMoveDir)
{
byte[] array = new byte[41];
array[0] = 3;
int offset = 1;
PackFloat(time, array, ref offset);
PackVector3(pos, array, ref offset);
PackVector3(moveDir, array, ref offset);
PackVector3(nextMoveDir, array, ref offset);
foreach (KeyValuePair<ulong, RemotePlayerData> remotePlayer in m_remotePlayers)
{
if (remotePlayer.Value.state == PeerConnectionState.Connected)
{
Net.SendPacket(remotePlayer.Key, array, SendPolicy.Reliable);
}
}
}
private void ReceiveBackboardUpdate(ulong remoteID, byte[] msg)
{
int offset = 1;
float remoteTime = UnpackTime(remoteID, msg, ref offset);
Vector3 pos = UnpackVector3(msg, ref offset);
Vector3 moveDir = UnpackVector3(msg, ref offset);
Vector3 nextMoveDir = UnpackVector3(msg, ref offset);
P2PNetworkGoal goal = m_remotePlayers[remoteID].player.Goal;
goal.RemoteBackboardUpdate(remoteTime, pos, moveDir, nextMoveDir);
}
public void AddNetworkBall(GameObject ball)
{
m_localBalls[ball.GetInstanceID()] = ball.AddComponent<P2PNetworkBall>();
}
public void RemoveNetworkBall(GameObject ball)
{
m_localBalls.Remove(ball.GetInstanceID());
}
private void SendLocalBallTransforms()
{
m_timeForNextBallUpdate = Time.time + 0.1f;
int num = 5 + m_localBalls.Count * 29;
byte[] array = new byte[num];
array[0] = 4;
int offset = 1;
PackFloat(Time.realtimeSinceStartup, array, ref offset);
foreach (P2PNetworkBall value in m_localBalls.Values)
{
PackBool(value.IsHeld(), array, ref offset);
PackInt32(value.gameObject.GetInstanceID(), array, ref offset);
PackVector3(value.transform.localPosition, array, ref offset);
PackVector3(value.velocity, array, ref offset);
}
foreach (KeyValuePair<ulong, RemotePlayerData> remotePlayer in m_remotePlayers)
{
if (remotePlayer.Value.state == PeerConnectionState.Connected)
{
Net.SendPacket(remotePlayer.Key, array, SendPolicy.Unreliable);
}
}
}
private void ReceiveBallTransforms(ulong remoteID, byte[] msg, ulong msgLength)
{
int offset = 1;
float num = UnpackTime(remoteID, msg, ref offset);
if (num < m_remotePlayers[remoteID].lastReceivedBallsTime)
{
return;
}
m_remotePlayers[remoteID].lastReceivedBallsTime = num;
while (offset != (int)msgLength)
{
bool isHeld = UnpackBool(msg, ref offset);
int key = UnpackInt32(msg, ref offset);
Vector3 pos = UnpackVector3(msg, ref offset);
Vector3 vel = UnpackVector3(msg, ref offset);
if (!m_remotePlayers[remoteID].activeBalls.ContainsKey(key))
{
P2PNetworkBall p2PNetworkBall = m_remotePlayers[remoteID].player.CreateBall().AddComponent<P2PNetworkBall>();
p2PNetworkBall.transform.SetParent(m_remotePlayers[remoteID].player.transform.parent);
m_remotePlayers[remoteID].activeBalls[key] = p2PNetworkBall;
}
P2PNetworkBall p2PNetworkBall2 = m_remotePlayers[remoteID].activeBalls[key];
if ((bool)p2PNetworkBall2)
{
p2PNetworkBall2.ProcessRemoteUpdate(num, isHeld, pos, vel);
}
}
}
public void SendScoreUpdate(uint score)
{
byte[] array = new byte[5] { 5, 0, 0, 0, 0 };
int offset = 1;
PackUint32(score, array, ref offset);
foreach (KeyValuePair<ulong, RemotePlayerData> remotePlayer in m_remotePlayers)
{
if (remotePlayer.Value.state == PeerConnectionState.Connected)
{
Net.SendPacket(remotePlayer.Key, array, SendPolicy.Reliable);
}
}
}
private void ReceiveScoredUpdate(ulong remoteID, byte[] msg)
{
int offset = 1;
uint score = UnpackUint32(msg, ref offset);
m_remotePlayers[remoteID].player.ReceiveRemoteScore(score);
}
private void PackVector3(Vector3 vec, byte[] buf, ref int offset)
{
PackFloat(vec.x, buf, ref offset);
PackFloat(vec.y, buf, ref offset);
PackFloat(vec.z, buf, ref offset);
}
private Vector3 UnpackVector3(byte[] buf, ref int offset)
{
Vector3 result = default(Vector3);
result.x = UnpackFloat(buf, ref offset);
result.y = UnpackFloat(buf, ref offset);
result.z = UnpackFloat(buf, ref offset);
return result;
}
private void PackQuaternion(Quaternion quat, byte[] buf, ref int offset)
{
PackFloat(quat.x, buf, ref offset);
PackFloat(quat.y, buf, ref offset);
PackFloat(quat.z, buf, ref offset);
PackFloat(quat.w, buf, ref offset);
}
private void PackFloat(float value, byte[] buf, ref int offset)
{
Buffer.BlockCopy(BitConverter.GetBytes(value), 0, buf, offset, 4);
offset += 4;
}
private float UnpackFloat(byte[] buf, ref int offset)
{
float result = BitConverter.ToSingle(buf, offset);
offset += 4;
return result;
}
private float UnpackTime(ulong remoteID, byte[] buf, ref int offset)
{
return ShiftRemoteTime(remoteID, UnpackFloat(buf, ref offset));
}
private void PackInt32(int value, byte[] buf, ref int offset)
{
Buffer.BlockCopy(BitConverter.GetBytes(value), 0, buf, offset, 4);
offset += 4;
}
private int UnpackInt32(byte[] buf, ref int offset)
{
int result = BitConverter.ToInt32(buf, offset);
offset += 4;
return result;
}
private void PackUint32(uint value, byte[] buf, ref int offset)
{
Buffer.BlockCopy(BitConverter.GetBytes(value), 0, buf, offset, 4);
offset += 4;
}
private uint UnpackUint32(byte[] buf, ref int offset)
{
uint result = BitConverter.ToUInt32(buf, offset);
offset += 4;
return result;
}
private void PackBool(bool value, byte[] buf, ref int offset)
{
buf[offset++] = (byte)(value ? 1u : 0u);
}
private bool UnpackBool(byte[] buf, ref int offset)
{
return buf[offset++] != 0;
}
}
}