Files
kridoo fec7b90249 1
2025-11-17 16:24:39 +08:00

935 lines
29 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using BigSpace.Logic;
using BigSpace.XRCore.Event;
using Netly;
using Netly.Core;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using UnityEngine.XR;
public class VR2 : MonoBehaviour
{
public static VR2 Instance;
//public Game
TcpClient client;
bool isConnecting;
uint wearingState;
DateTime connTime, dateTime, posTime, allinfoTime;
Register register = new();
Logout logout = new();
VRInfo vrinfo = new();
VRPos vrpos = new();//自己的位置
ObjPos gPos = new();
Ask ask = new();
Inform inform = new();
BackInfo backInfo = new();
float statHight = 0; //开始的高度
Vector3 lastPos; //最后的位置
float distance = 0; //最后的位置和最新位置的距离
Vector3 myPositionChang; //这个是自己修改过的高度,要传给服务器
Vector3 db_myPosition; //自己对比距离的位置
Vector3 db_otherPosition; //别人对比距离的位置
float db_distance = 0; //对比的距离
//TimelineProgress timelineProgress = new();
string user = "u";
Transform cameraTran;
public Transform otherPlayers;
float quitTimers;
uint human;
private Dictionary<int, Action<string>> _messageHandlers = new();
public GameObject cubeObj;
internal UnityEvent<uint, int> OnTimelineProgress = new();
// 存储所有Good对象的字典
private Dictionary<string, Good> goodsDictionary = new Dictionary<string, Good>();
int erorrCount;
private Memory<byte> _receiveMemory = Memory<byte>.Empty; //缓冲区
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
}
void Start()
{
//PicoAPI._Instance.OpenTouPing();
//PicoAPI._Instance.KillAppsByPidOrPackageName("com.pvr.picocast");
Initialize();
GameTools.CreateDirectory(Config.RootPath);
Invoke("Init", 0.5f);
GlobalEventMgr.Listen<int>(GameEvent.EventAnimalSend, GameDataManage_EventAnimalSend);
// MyLoadSceneAsync(1);//加载第一个内容场景
}
void ReadConfig()
{
if (File.Exists(Config.IpConfig))
{
string[] str = File.ReadAllText(Config.IpConfig).Split('\n');
Config.TcpHost = str[0];
WebNet.webIp = str[1];
WebNet.assetsUrl = $"{WebNet.webIp}/api/dev/login";
WebNet.DownAssetsUrl = $"{WebNet.webIp}/storage/resource";
WebNet.UploadInfoUrl = $"{WebNet.webIp}/api/dev/uploadUsageInfo";
WebNet.getDeviceList = $"{WebNet.webIp}/api/tablet/getDeviceList";
WebNet.uploadEQ = $"{WebNet.webIp}/api/dev/uploadEQ";
}
else
{
string str = $"{Config.TcpHost}\n{WebNet.webIp}";
File.WriteAllText(Config.IpConfig, str);
}
}
void Init()
{
ReadConfig();
cameraTran = Camera.main.transform;
statHight = Camera.main.transform.position.y;
connTime = DateTime.Now;
dateTime = DateTime.Now.AddSeconds(-25);
posTime = DateTime.Now;
allinfoTime = DateTime.Now.AddSeconds(-45);
if (Application.isEditor)
Config.SN = "PA9412MGJC120200G";
else
Config.SN = PicoAPI._Instance.PicoSN();
client = new TcpClient(framing: false);
client.OnOpen(open);
client.OnClose(close);
client.OnError(error);
client.OnEvent(msgEvent);
client.OnData(msgData);
}
void Initialize()
{
_messageHandlers[7] = HandleAllIfo;
_messageHandlers[8] = HandleGood;
_messageHandlers[4] = HandleVRPosition;
_messageHandlers[6] = HandleLogout;
_messageHandlers[12] = HandleVRControl;
_messageHandlers[13] = HandleInform;
_messageHandlers[21] = HandleTimeLineProgress;
}
//发送创建动物事件
void GameDataManage_EventAnimalSend(int animalType)
{
sendCom(3, animalType, 0);
}
void Update()
{
//测试
if (Input.GetKeyDown(KeyCode.Space))
{
//Logout logout = new Logout();
//logout.group = 8;
//logout.sn = "PA9410MGJ9280275G";
//sendMessage(6, logout);
//logout.group = 8;
//logout.sn = "PA9410PGJ6170013H";
//sendMessage(6, logout);
//sendCom(1, 0, 0);
MyLoadSceneAsync(2);
}
else if(Input.GetKeyDown(KeyCode.Q))
{
MyLoadSceneAsync(1);
}
//退出游戏
if (Input.GetKey(KeyCode.JoystickButton0))
{
quitTimers += Time.deltaTime;
if (quitTimers > 3f)
{
quitTimers = 0f;
sendLogout();
Invoke("Quit", 2f);
}
}
if (InputDevices.GetDeviceAtXRNode(XRNode.RightHand).TryGetFeatureValue(CommonUsages.primaryButton, out bool islogout))
{
if (islogout)
{
quitTimers += Time.deltaTime;
if (quitTimers > 3f)
{
quitTimers = 0f;
sendLogout();
Quit();
}
}
else
{
quitTimers = 0f;
}
}
if (InputDevices.GetDeviceAtXRNode(XRNode.RightHand).TryGetFeatureValue(CommonUsages.primaryButton, out bool isshow))
{
if (isshow)
{
sendCom(1, 0, 0);
}
}
if (InputDevices.GetDeviceAtXRNode(XRNode.RightHand).TryGetFeatureValue(CommonUsages.secondaryButton, out bool ishide))
{
if (ishide)
{
sendCom(2, 0, 0);
}
}
//头盔变化
if (Application.platform == RuntimePlatform.Android)
{
if (InputDevices.GetDeviceAtXRNode(XRNode.Head).TryGetFeatureValue(CommonUsages.userPresence, out bool userPresence))
{
if (userPresence)
{
wearingState = 1;
}
else
{
wearingState = 0;
}
}
}
//连接服务器
if (client != null)
{
if (!isConnecting)
{
if (DateTime.Now > connTime.AddSeconds(2))
{
connTime = DateTime.Now;
Debug.Log($"连接中。。。ip:{Config.TcpHost}");
client.Open(new Host(Config.TcpHost, Config.TcpPort));
Invoke("sendRegister", 1f);
}
}
else
{
if (DateTime.Now > dateTime.AddSeconds(2))
{
dateTime = DateTime.Now;
//sendvrInfo(Config.SN, user, uint.Parse(PicoAPI._Instance.PicoPower()), 2, wearingState);
//sendvrInfo(Config.SN, user, 100, 1, 1);
}
if (DateTime.Now > posTime.AddSeconds(0.5f))
{
posTime = DateTime.Now;
if (cameraTran.position.y < (statHight * 0.7f)) //这个是自己判断自己的状态后加的 蹲着
{
myPositionChang = new Vector3(cameraTran.position.x, 3, cameraTran.position.z);
//cameraTran.position = new Vector3(cameraTran.position.x, 3, cameraTran.position.z);
}
else
{
distance = Vector3.Distance(lastPos, cameraTran.position);
//Debug.Log("最后的位置:" + lastPos + "||" + cameraTran.position+"||"+ distance);
if (distance <= 0.1f)
{
myPositionChang = new Vector3(cameraTran.position.x, 1, cameraTran.position.z);
//cameraTran.position = new Vector3(cameraTran.position.x, 1, cameraTran.position.z); //站立
}
else
{
myPositionChang = new Vector3(cameraTran.position.x, 2, cameraTran.position.z);
//cameraTran.position = new Vector3(cameraTran.position.x, 2, cameraTran.position.z); //行走
}
}
lastPos = cameraTran.position;
//Debug.Log("自己传的是什么状态:" + myPositionChang.y);
sendvrPos(Config.SN, user, Config.Group, myPositionChang, cameraTran.rotation);
}
if (DateTime.Now > allinfoTime.AddSeconds(50))
{
allinfoTime = DateTime.Now;
sendAskInfo();
//sendGoodAskInfo();
}
}
}
}
//Position GetPosition()
//{
// position.Pos = Vec3ToPos(nowObj.transform.position);
// position.Dir = Vec3ToDir(nowObj.transform.eulerAngles);
// position.State = GetMyState(nowObj.transform.position);
// return position;
//}
/// <summary>
/// 注册Good对象
/// </summary>
/// <param name="good">要注册的Good对象</param>
public void RegisterGood(Good good)
{
if (good == null) return;
string key = good.gameObject.name; // 使用物体名作为键
if (!goodsDictionary.ContainsKey(key))
{
goodsDictionary.Add(key, good);
}
else
{
// 已存在同名对象,可以选择替换或忽略
goodsDictionary[key] = good;
Debug.LogWarning($"已存在同名Good对象: {key}, 已替换为新对象");
}
}
/// <summary>
/// 注销Good对象
/// </summary>
/// <param name="good">要注销的Good对象</param>
public void UnregisterGood(Good good)
{
if (good == null) return;
string key = good.gameObject.name;
if (goodsDictionary.ContainsKey(key))
{
// 确保移除的是同一个对象
if (goodsDictionary[key] == good)
{
goodsDictionary.Remove(key);
}
}
}
#region
void sendMessage<T>(uint type, T msg)
{
try
{
// 序列化消息
string json = JsonConvert.SerializeObject(msg);
Debug.Log($"发送的json: {json}");
byte[] msgBytes = NE.GetBytes(json);
// 计算总长度(消息类型 + 消息内容 + 结尾的0
int totalLength = sizeof(uint) + msgBytes.Length + 1; // 类型长度 + 消息长度 + 1结尾的0
// 使用 MemoryStream 和 BinaryWriter 构建字节数组
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
writer.Write(totalLength); // 写入总长度
writer.Write(type); // 写入消息类型
writer.Write(msgBytes); // 写入消息内容
writer.Write((byte)0); // 写入结尾的0
// 发送数据
client.ToData(stream.ToArray());
}
}
catch (Exception ex)
{
Debug.LogError("发送消息失败: " + ex.Message);
}
}
//
void open()
{
isConnecting = true;
}
void close()
{
isConnecting = false;
}
void error(Exception e)
{
Debug.LogError($"连接错误:{e.Message}");
}
void msgEvent(string name, byte[] bytes)
{
string msg = NE.GetString(bytes);
}
void msgData(byte[] bytes)
{
if (bytes == null || bytes.Length == 0)
{
Debug.LogError("无效数据包:空数据");
return;
}
//string hexResult = "";
//foreach (byte b in bytes)
//{
// hexResult += b.ToString("X2") + " ";
//}
//Debug.Log($"消息字节: {hexResult}");
#region Memory缓冲区
// 将新数据追加到缓冲区
_receiveMemory = CombineMemory(_receiveMemory, bytes.AsMemory());
try
{
while (_receiveMemory.Length >= 8)
{
var bufferSpan = _receiveMemory.Span;
int contentLength = BitConverter.ToInt32(bufferSpan.Slice(0, 4));//数据长度
int type = BitConverter.ToInt32(bufferSpan.Slice(4, 4));// 数据类型
if (type > 100)
{
// 找到第一个 0 字节的位置
int zeroIndex = _receiveMemory.Span.IndexOf((byte)0);
if (zeroIndex >= 0)
{
_receiveMemory = _receiveMemory.Slice(zeroIndex + 1); // 移除到 0 字节之后
Debug.LogError($"数据包类型错误: {type},移除到 0 字节之后");
}
else
{
_receiveMemory = Memory<byte>.Empty; // 如果没有 0 字节,清空缓冲区
Debug.LogError($"数据包类型错误: {type},没有 0 字节,清空缓冲区");
}
break;
}
int totalPacketLength = 4 + contentLength;
int msgLength = contentLength - 5;//(减4减1)
try
{
//检查是否收到完整的数据包
if (_receiveMemory.Length >= totalPacketLength)
{
// 验证结尾字节是否为0
if (bufferSpan[totalPacketLength - 1] != 0)
{
_receiveMemory = Memory<byte>.Empty;
Debug.LogError("清除缓冲区,数据包格式错误: 缺少结尾0字节");
break;
}
var msgMemory = _receiveMemory.Slice(8, msgLength);
string msg = Encoding.UTF8.GetString(msgMemory.Span);
//Debug.Log($"收到的消息:{msg}");
if (_messageHandlers.TryGetValue(type, out var handler))
{
try
{
handler(msg);//业务处理
}
catch (Exception e)
{
Debug.LogError($"业务逻辑处理报错:{e.Message}");
}
}
else
{
//Debug.LogError($"未知的消息类型: {type}");
}
//Debug.Log("从缓冲区移除已处理的数据");
_receiveMemory = _receiveMemory.Slice(totalPacketLength);
erorrCount = 0;
}
else
{
Debug.LogError($"数据不完整,类型:{type},预期长度: {totalPacketLength}, 实际长度: {_receiveMemory.Length}");
erorrCount++;
if (erorrCount >= 10)
{
erorrCount = 0;
_receiveMemory = Memory<byte>.Empty;
Debug.LogError("清除缓冲区数据不完整超过10次");
}
break;
}
}
catch (Exception e)
{
Debug.LogError($"清除缓冲区,处理数据包时出错: {e.Message}");
_receiveMemory = Memory<byte>.Empty;
}
}
}
catch (Exception e)
{
Debug.LogError($"清除缓冲区,消息处理异常: {e.Message}");
_receiveMemory = Memory<byte>.Empty;
}
#endregion
}
/// <summary>
/// 加载更新玩家位置
/// </summary>
void LoadPlayer(VRPos vrpos, ref Player player)
{
if (!string.Equals(vrpos.sn, Config.SN))
{
if (!otherPlayers.Find(vrpos.sn))
{
player = GameTools.LoadPrefab<Player>("Player", otherPlayers);
}
else
{
player = otherPlayers.Find(vrpos.sn).GetComponent<Player>();
}
player.isOtherPlayer = true;
player.transform.name = vrpos.sn;
player.sn = vrpos.sn;
if (player.user)
{
player.user.text = vrpos.user;
if (vrpos.human > 1)
{
player.human = 0;
}
else
{
player.human = vrpos.human;
}
}
player.group = vrpos.group;
player.x = vrpos.x;
player.y = vrpos.y;
player.z = vrpos.z;
player.q = vrpos.q;
player.w = vrpos.w;
player.e = vrpos.e;
player.r = vrpos.r;
player.LastTime = Time.time;
if (Config.Group == player.group) //同组
{
player.gameObject.SetActive(true);
}
else //不同组
{
db_myPosition.x = lastPos.x;
db_myPosition.y = 0;
db_myPosition.z = lastPos.z;
db_otherPosition.x = player.x/ Config.times;
db_otherPosition.y = 0;
db_otherPosition.z = player.z/ Config.times;
db_distance = Vector3.Distance(db_myPosition, db_otherPosition);
//Debug.Log("多少米:"+ db_distance);
if (db_distance < 5)
player.gameObject.SetActive(true);
else
player.gameObject.SetActive(false);
}
}
}
/// <summary>
/// 加载更新物体位置
/// </summary>
void LoadGood(Good good, ObjPos gPos)
{
good.sn = gPos.sn;
good.user.text = gPos.user;
good.x = gPos.x;
good.y = gPos.y;
good.z = gPos.z;
good.q = gPos.q;
good.w = gPos.w;
good.e = gPos.e;
good.r = gPos.r;
good.isExistServer = true;
}
#endregion
#region
/// <summary>
/// 发送时间轴进度
/// </summary>
public void sendTimeLineProgress(uint scene, int progress)
{
//timelineProgress.sn = Config.SN;
//timelineProgress.group = Config.Group;
//timelineProgress.scene = scene;
//timelineProgress.progress = progress;
//sendMessage(16, timelineProgress);
//Debug.Log($"发送时间轴消息: {timelineProgress.scene}+++++{timelineProgress.progress}");
}
/// <summary>
/// 发送vr设备信息
/// </summary>
void sendvrInfo(string sn, string name, uint power, uint status, uint wear)
{
vrinfo.sn = sn;
vrinfo.user = name;
vrinfo.human = human;
//vrinfo.power = power;
//vrinfo.status = status;
//vrinfo.wear = wear;
sendMessage(2, vrinfo);
}
/// <summary>
/// 发送vr位置
/// </summary>
void sendvrPos(string sn, string name, uint group, Vector3 v3, Quaternion qua)
{
vrpos.sn = sn;
vrpos.user = name;
vrpos.human = human;
vrpos.group = group;
vrpos.x = (int)(v3.x * Config.times);
vrpos.y = (int)(v3.y * Config.times);
vrpos.z = (int)(v3.z * Config.times);
vrpos.q = (int)(qua.x * Config.times);
vrpos.w = (int)(qua.y * Config.times);
vrpos.e = (int)(qua.z * Config.times);
vrpos.r = (int)(qua.w * Config.times);
sendMessage(4, vrpos);
}
/// <summary>
/// 发送物体位置
/// </summary>
public void sendGoodPos(string objname, Vector3 v3, Quaternion qua)
{
gPos.sn = Config.SN;
gPos.user = user;
gPos.name = objname;
gPos.group = Config.Group;
gPos.x = (int)(v3.x * Config.times);
gPos.y = (int)(v3.y * Config.times);
gPos.z = (int)(v3.z * Config.times);
gPos.q = (int)(qua.x * Config.times);
gPos.w = (int)(qua.y * Config.times);
gPos.e = (int)(qua.z * Config.times);
gPos.r = (int)(qua.w * Config.times);
sendMessage(8, gPos);
}
/// <summary>
/// 注册
/// </summary>
void sendRegister()
{
register.type = 2;
register.user = "VR2";
register.sn = Config.SN;
sendMessage(1, register);
}
/// <summary>
/// 注销
/// </summary>
public void sendLogout()
{
Logout logout = new Logout();
logout.group = Config.Group;
logout.sn = Config.SN;
sendMessage(6, logout);
}
void Quit()
{
Application.Quit();
}
/// <summary>
/// 请求人物全量数据
/// </summary>
void sendAskInfo()
{
ask.type = 1;
sendMessage(5, ask);
}
/// <summary>
/// 请求物品全量数据
/// </summary>
void sendGoodAskInfo()
{
ask.type = 10;
sendMessage(9, ask);
}
/// <summary>
/// 发送通知
/// </summary>
void sendCom(uint com, int value, float f)
{
inform.sn = Config.SN;
inform.user = user;
inform.group = Config.Group;
inform.com = com;
inform.value = value;
inform.f = f;
sendMessage(13, inform);
}
/// <summary>
/// 回给平板消息
/// </summary>
void sendBackInfo()
{
backInfo.sn = Config.SN;
backInfo.url = PicoAPI._Instance.GetCastUrl();
sendMessage(14, backInfo);
}
#endregion
#region
private void HandleTimeLineProgress(string msg)
{
try
{
//TimelineProgress timelineProgress = JsonConvert.DeserializeObject<TimelineProgress>(msg);
//Debug.Log($"解析时间轴消息: {msg}");
//if (Config.Group == timelineProgress.group)
//{
// OnTimelineProgress.Invoke(timelineProgress.scene, timelineProgress.progress);
//}
}
catch (Exception ex)
{
Debug.LogError($"解析时间轴时遇到错误: {ex.Message}");
}
}
private void HandleInform(string msg)
{
//try
//{
// Inform inform = JsonConvert.DeserializeObject<Inform>(msg);
// switch (inform.com)
// {
// case 1:
// cubeObj.SetActive(true);
// break;
// case 2:
// cubeObj.SetActive(false);
// break;
// case 3:
// if (Config.SN != inform.sn)
// {
// Player player = otherPlayers.Find(inform.sn).GetComponent<Player>();
// GlobalEventMgr.Dispatch(GameEvent.EventAnimalSync, inform.value, player.transform.position); //收到同组玩家创建动物
// }
// break;
// default:
// break;
// }
//}
//catch (Exception ex)
//{
// Debug.LogError($"解析玩家通知时遇到错误: {ex.Message}");
//}
}
private void HandleVRControl(string msg)
{
try
{
VRControl vrControl = JsonConvert.DeserializeObject<VRControl>(msg);
if (string.Equals(vrControl.sn, Config.SN))
{
switch (vrControl.com)
{
case 1:
PicoAPI._Instance.ShutDown();
break;
case 2:
PicoAPI._Instance.Reboot();
break;
case 3:
PicoAPI._Instance.SetVolumeNum(vrControl.volume);
break;
case 4:
float ipd = (float)vrControl.volume / 100;
PicoAPI._Instance.SetIpd(ipd);
break;
case 5:
sendBackInfo();
break;
case 6:
switch (vrControl.volume)
{
case 0://启动
break;
case 1://暂停
break;
case 2://关闭
break;
}
break;
}
}
}
catch (Exception ex)
{
Debug.LogError($"解析控制设备时遇到错误: {ex.Message}");
}
}
private HashSet<string> _previousSNs = new HashSet<string>();
void HandleAllIfo(string msg)
{
try
{
var allIfo = JsonConvert.DeserializeObject<AllIfo>(msg);
var currentSNs = new HashSet<string>(); // 当前帧的 SN 集合
foreach (var vrGroupInfo in allIfo.all)
{
foreach (var vrpos in vrGroupInfo.vrs)
{
// 记录所有 vrpos.sn
currentSNs.Add(vrpos.sn);
Player p;
if (string.Equals(vrpos.sn, Config.SN))//如果是自己,则获得组名,获得体验者昵称
{
p = cameraTran.Find("Player").GetComponent<Player>();
p.group = Config.Group = vrpos.group;
p.user.text = user = vrpos.user;
p.human = human = vrpos.human;
}
else
{
p = null;
}
LoadPlayer(vrpos, ref p);
}
}
// 对比差异并隐藏消失的 SN
var missingSNs = _previousSNs.Except(currentSNs);
foreach (var sn in missingSNs)
{
otherPlayers.Find(sn).gameObject.SetActive(false);
}
// 更新缓存
_previousSNs = currentSNs;
}
catch (Exception ex)
{
Debug.LogError($"解析所有玩家信息时遇到错误: {ex.Message}");
}
}
void HandleGood(string msg)
{
try
{
var gpos = JsonConvert.DeserializeObject<ObjPos>(msg);
if (gpos.group == Config.Group && goodsDictionary.TryGetValue(gpos.name, out Good good))
{
LoadGood(good, gpos);
}
}
catch (Exception ex)
{
Debug.LogError($"解析物体位置时遇到错误: {ex.Message}");
}
}
void HandleVRPosition(string msg)
{
try
{
var pos = JsonConvert.DeserializeObject<VRPos>(msg);
Player player = null;
LoadPlayer(pos, ref player);
}
catch (Exception ex)
{
Debug.LogError($"解析玩家位置时遇到错误: {ex.Message}");
}
}
void HandleLogout(string msg)
{
try
{
logout = JsonConvert.DeserializeObject<Logout>(msg);
if (otherPlayers.Find(logout.sn))
otherPlayers.Find(logout.sn).gameObject.SetActive(false);
}
catch (Exception ex)
{
Debug.LogError($"解析玩家注销时遇到错误: {ex.Message}");
}
}
#endregion
int lastSceneIndex;//上次场景索引
IEnumerator loadSceneAsync(int index)
{
if (lastSceneIndex > 0)
{
SceneManager.UnloadSceneAsync(lastSceneIndex);
}
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);
while (!asyncLoad.isDone)
{
yield return null;
}
lastSceneIndex = index;
}
/// <summary>
/// 卸载旧场景再加载新场景
/// </summary>
public void MyLoadSceneAsync(int index)
{
StartCoroutine(loadSceneAsync(index));
}
// 合并两个 Memory<byte>(模拟 List<byte>.AddRange
private Memory<byte> CombineMemory(Memory<byte> a, Memory<byte> b)
{
if (a.IsEmpty) return b;
if (b.IsEmpty) return a;
var newBuffer = new byte[a.Length + b.Length];
a.CopyTo(newBuffer.AsMemory(0, a.Length));
b.CopyTo(newBuffer.AsMemory(a.Length, b.Length));
return newBuffer;
}
}