Тема: Передача даних між потоками
Привіт. Пишу клієнтську частину для ММО ЕрПоГе. Використовую двигун Unity3D, в принципі там той самий С# але структура скриптів своя. Коротше, дивіться яка схема. Для початку - в Юніті є свої методи, котрі можна запускати лишень в головному потоці. Ці методи відповідають за роботу з об'єктами в сцені і так далі, наприклад, створення в сцені об'єкту та присвоєння йому координат необхідно робити в головному потоці. В скриптах Юніті є декілька важливих методів, це void Start, котрий являється чимось типу конструктора, він виконується до виконання інших скриптів, void Update, це метод, котрий виконується кожен фрейм. Так от, я використовую асинхронні сокети для взаємодії сервера та клієнта. І, очевидно, данні приймаються методом, котрий виконується ну ніяк не в головному потоці. Тоді як ці дані передати в головний потік? Ну я зробив просто, створив List<byte[]>, і коли асинхронний метод приймає дані, то я заношу ці дані в List, а в Update перевіряю, якщо в List щось є, то я витягую ці дані, та передаю на обробку в інший метод, а в тому методі, по закінченню всіх справ - я видаляю оброблені дані з List. Так от, ця схема і працює, але не ідеально, тому що не всі дані, котрі прийняв асинхронний метод, обробляються в методі, котрий викликається в Update. Яким ще чином можна організувати таку обробку даних? Ось код, щоб було зрозуміліше, зверніть увагу на методи ReceiveCallback та Update, перший приймає дані та заносить в чергу на обробку, а другий вже обробляє.
p.s. це не windows forms, тому ніякі Invoke не спрацюють.
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;
public class client : MonoBehaviour {
private GameObject player;
private Socket socket;
private byte[] buff;
private List<byte[]> listRec;
private characters iAm;
//private List<byte[]> listSend;
void Start () {
socket=globalConnection.s;
player = (GameObject)GameObject.FindGameObjectWithTag("Player");
buff = new byte[4096];
listRec=new List<byte[]>();
//listSend=new List<byte[]>();
iAm=globalConnection.activePlayer;
SetActivePlayer(iAm);
CreatePlayers();
}
void Update()
{
if(listRec.Count>0)
{
lock(listRec){
byte[] bf = listRec[listRec.Count-1];
ExecuteRec(bf);
}
}
/*Debug.Log("count of global GO "+globalConnection.players.Count);
foreach(var c in globalConnection.players)
Debug.Log(c);
/*if(listSend.Count>0)
{
StartCoroutine("ExecuteSend",listSend[listSend.Count-1]);
}*/
}
byte[] PackPos()
{
float x,y,z,rx,ry,rz,rw;
byte[] bf = new byte[64];
int index=4;
int char_id=iAm.id;
x=player.transform.position.x;
y=player.transform.position.y;
z=player.transform.position.z;
rx=player.transform.rotation.x;
ry=player.transform.rotation.y;
rz=player.transform.rotation.z;
rw=player.transform.rotation.w;
BitConverter.GetBytes(char_id).CopyTo(bf,index);
BitConverter.GetBytes(x).CopyTo(bf,index+4);
BitConverter.GetBytes(y).CopyTo(bf,index+8);
BitConverter.GetBytes(z).CopyTo(bf,index+12);
BitConverter.GetBytes(rx).CopyTo(bf,index+16);
BitConverter.GetBytes(ry).CopyTo(bf,index+20);
BitConverter.GetBytes(rz).CopyTo(bf,index+24);
BitConverter.GetBytes(rw).CopyTo(bf,index+28);
Encoding.UTF8.GetBytes("man").CopyTo(bf,0);
return bf;
}
void ExecuteRec(byte[] b)
{
string req = Encoding.UTF8.GetString(b,0,3);
switch(req)
{
case "man" :
SetMain(b);
Debug.Log("Receive man");
break;
case "act" :
CreateChar(b);
Debug.Log("Act");
break;
}
lock(listRec)
{
listRec.Remove(b);
} //yield return 0;
}
IEnumerator Sending()
{
while(true){
byte[] bf = PackPos();
try{
socket.BeginSend(bf,0,bf.Length,SocketFlags.None,new AsyncCallback(SendCallback),socket);
}
catch(Exception e)
{
Debug.Log("Sending "+e.Message);
}
yield return new WaitForSeconds(1);
}
}
void CreatePlayers()
{
if(globalConnection.activePlayers.Count>0)
{
GameObject go = (GameObject)Resources.Load("Players");
foreach(var pl in globalConnection.activePlayers)
{
GameObject g = (GameObject)Instantiate(go);
PlayerManager pm = (PlayerManager)g.GetComponent("PlayerManager");
pm.Construct(pl.name,pl.id,pl.x,pl.y,pl.z);
globalConnection.players.Add(pm); }
}
socket.BeginReceive(buff,0,buff.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),socket);
StartCoroutine("Sending");
}
void CreateChar(byte[] b)
{
float x = BitConverter.ToSingle(b,20);
float y = BitConverter.ToSingle(b,24);
float z = BitConverter.ToSingle(b,28);
string nick = Encoding.UTF8.GetString(b,10,20);
int id = BitConverter.ToInt32(b,4);
GameObject go = (GameObject)Resources.Load("Players");
lock(globalConnection.players)
{
GameObject g = (GameObject)Instantiate(go);
PlayerManager pm = (PlayerManager)g.GetComponent("PlayerManager");
pm.Construct(nick,id,x,y,z);
globalConnection.players.Add(pm);
}
}
void SetMain(byte[] b)
{
int char_id= BitConverter.ToInt32(b,4);
Debug.Log("char_id = "+char_id);
foreach(var pm in globalConnection.players)
{
//PlayerManager pm = (PlayerManager)ch.GetComponent("PlayerManager");
if(pm.id==char_id)
{
int index=4;
float x = BitConverter.ToSingle(b,index+4);
float y = BitConverter.ToSingle(b,index+8);
float z = BitConverter.ToSingle(b,index+12);
float rx = BitConverter.ToSingle(b,index+16);
float ry = BitConverter.ToSingle(b,index+20);
float rz = BitConverter.ToSingle(b,index+24);
float rw = BitConverter.ToSingle(b,index+28);
pm.transform.position=new Vector3(x,y,z);
pm.transform.rotation=new Quaternion(rx,ry,rz,rw);
Debug.Log("Set: x="+x+" y="+y+" z="+z);
}
}
}
void SetActivePlayer(characters ch)
{
byte[] bf = new byte[32];
Encoding.UTF8.GetBytes("act").CopyTo(bf,0);
BitConverter.GetBytes(ch.id).CopyTo(bf,3);
Encoding.UTF8.GetBytes(ch.name).CopyTo(bf,10);
socket.BeginReceive(buff,0,buff.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),socket);
socket.BeginSend(bf,0,bf.Length,SocketFlags.None,new AsyncCallback(SendCallback),socket);
}
void SendCallback(IAsyncResult ar)
{
Socket s = (Socket)ar.AsyncState;
try{
s.EndSend(ar);
}
catch(Exception e)
{
Debug.Log(e.Message);
}
}
void ReceiveCallback(IAsyncResult ar)
{
Debug.Log("Receive from client");
Socket s = (Socket)ar.AsyncState;
string ss = Encoding.UTF8.GetString(buff,0,3);
try{
int n = s.EndSend(ar);
lock(listRec){
listRec.Add(buff);
}
Debug.Log("ReceiveCallback "+n+" with command = "+ss);
}
catch(Exception e)
{
Debug.Log("ReceiveCallback "+e.Message);
s.Close();
}
s.BeginReceive(buff,0,buff.Length,SocketFlags.None,new AsyncCallback(ReceiveCallback),s);
}
}