Unity TCP Server Hang Unity scene - unity3d

I am trying to implement a TCP server in unity. I am using unity pro 3.5 and when I run this code in a scene, then unity hang, no response at all untill I kill it with Task Manager.
using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.Net;
using System.Text;
public class Server : MonoBehaviour {
private IPAddress ipAd;
public string IP="127.0.0.1";
public int port = 8001;
private Socket s;
void Update ()
{
}
// Use this for initialization
void Awake () {
port = 8001;
ipAd = IPAddress.Parse(IP);
msg = "Listening at " + IP + ":" + port.ToString();
this.s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.s.Bind(new IPEndPoint(ipAd,port));
this.s.Listen(200);
while (true)
this.ReceiveMessage(this.s.Accept()); //hang if this line activated
}
private void ReceiveMessage(Socket socket)
{
byte[] tempbuffer = new byte[10000];
socket.Receive(tempbuffer);
rec.AssignFromByteArray(tempbuffer);
}
}

Server should run in it's own thread so the GameLoop can continue even if tcp_Listener.AcceptSocket() is a blocking call.
using UnityEngine;
using System.Collections;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;
public class Server : MonoBehaviour {
private bool mRunning;
public static string msg = "";
public Thread mThread;
public TcpListener tcp_Listener = null;
void Awake() {
mRunning = true;
ThreadStart ts = new ThreadStart(Receive);
mThread = new Thread(ts);
mThread.Start();
print("Thread done...");
}
public void stopListening() {
mRunning = false;
}
void Receive() {
tcp_Listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8001);
tcp_Listener.Start();
print("Server Start");
while (mRunning)
{
// check if new connections are pending, if not, be nice and sleep 100ms
if (!tcp_Listener.Pending()){
Thread.Sleep(100);
}
else {
Socket ss = tcp_Listener.AcceptSocket();
BonePos rec = new BonePos();
byte[] tempbuffer = new byte[10000];
ss.Receive(tempbuffer); // received byte array from client
rec.AssignFromByteArray(tempbuffer); // my own datatype
}
}
}
void Update() {
}
void OnApplicationQuit() { // stop listening thread
stopListening();// wait for listening thread to terminate (max. 500ms)
mThread.Join(500);
}
}
See Related answer from answers.unity3d.com

You get the hanging because of this:
while (true)
this.ReceiveMessage(this.s.Accept()); //hang if this line activated
The entire process is stuck in this loop, so nothing else gets processed.
What you could do (though I would not recommend it) is to perform the action inside update() instead of an infinite loop.
what I suggest is to use something else to run your server instead of unity.
If you are creating a game server, you can use PUN (Photon Unity Networking) or write a standalone server in C#, or even an open-sourced server in python that matches your needs.

You have to get rid of this statement:
while (true)
this.ReceiveMessage(this.s.Accept());
And your server has to run in his own thread.

Unity's engine is single threaded, and the life cycle methods of monobehaviours run synchronously in a predefined order. An infinite loop in one life cycle method will infinitely hang the program.
Coroutines are one way that you can create infinite loops without hanging indefinitely. This use case would likely make use of WaitForSeconds

Related

How to execute RPC calls on all clients when this method sent from one client?

How to execute RPC calls on all clients when this method sent from one client? Is it exists some solution in Unity netcode like RPCTargets.All in Photon?
There you go
using UnityEngine;
using Mirror;
public class ExampleChangeSomething : NetworkBehaviour // ->Inherit from NetworkBehaviour
{
void Start()
{
UI_ChangeSomething();
}
//Attach this to the Button click or call this function from somewhere(e.g. I call this on Start)
public void UI_ChangeSomething()
{
//Client Send command to the Server
Cmd_ChangeSomething("Please Print this Text");
}
//Execute this on Server
[Command]
void Cmd_ChangeSomething(string _text)
{
//The server then tell every Clients to execute the "Rpc_ChangeSomething" Function
Rpc_ChangeSomething(_text);
}
[ClientRpc]
void Rpc_ChangeSomething(string _text)
{
ChangeSomething(_text);
}
//the logic that needs to be executed by Every Client
private void ChangeSomething(string _text)
{
//your logic here.
Debug.Log(_text); // every client will print "Please Print this Text" in their Console
}
}

Multiplayer [SyncEvent] problem with non-player objects

I'm developing a multiplayer game based on turns.
So I have an script named gameController which is the one who has the global timer to alter the turns and also choose which players are attacking and which players are defending in the current turn.
This script is a singleton attached to a gameObject with a network identity on it (This network identity has no checkboxes marked to be controller by the server). I tried to have this game object spawned to all the clients when the server connects and also to have the game object already in the scene (booth cases aren't working).
Well, the main problem is that in the gameController script I have a checker in the update function to check if any new player is connected. In case is connected, it should call a syncEvent named EventOnNewPlayerAddedDelegate (I tried to call it directly, also using [command] and using [ ClientRpc]) to let the players know that they have to call their function named "OnRegisterPlayer", which is a function in their own player script that calls a function on gameController script, passing the player object (I tried via command and rpc also), something like this: GameController.instance.RegisterPlayer(this);
So anyone knows how can I trigger this SyncEvent to register the player to a non-player object controlled by the server?
Thank you very much.
I attach here a brief of booth scripts to make it easier to understand:
GameController:
public class GameController : NetworkBehaviour
{
public delegate void OnNewPlayerAddedDelegate();
[SyncEvent]
public event OnNewPlayerAddedDelegate EventOnNewPlayerAddedDelegate;
List<GamePlayerManager> players = new List<GamePlayerManager>();
public static GameController instance { get; private set; }
float timePerTorn = 30f;
bool isCountdown = false;
[System.NonSerialized]
public float countdownTime;
int roundNumber = 0;
int NumberOfPlayers;
int NumberOfPlayersChanged;
void Awake()
{
Debug.Log("Awaking ");
if (instance != null)
{
Debug.Log("Destoring ");
DestroyImmediate(this);
return;
}
instance = this;
}
// Use this for initialization
void Start()
{
if (!isServer)
{
return;
}
players = new List<GamePlayerManager>();
StartCountdown(5f);//20
}
void Update()
{
if (isServer)
{
if (isCountdown)
{
countdownTime -= Time.deltaTime;
if (countdownTime <= 0)
{
AlterGlobalTurns();
}
}
}
NumberOfPlayers = NetworkManager.singleton.numPlayers;
if (NumberOfPlayersChanged != NumberOfPlayers)
{
Debug.Log("num of players changed ---> " + NumberOfPlayers);
NumberOfPlayersChanged = NumberOfPlayers;
EventOnNewPlayerAddedDelegate();
//RpcNewPlayerAdded();
//CmdNewPlayerAdded();
}
}
[ClientRpc]
void RpcNewPlayerAdded()
{
Debug.Log("---------------- RpcNewPlayerAdded ------------");
EventOnNewPlayerAddedDelegate();
}
[Command]
void CmdNewPlayerAdded()
{
Debug.Log("---------------- CmdNewPlayerAdded ------------");
EventOnNewPlayerAddedDelegate();
}
public void RegisterPlayer(GamePlayerManager player)
{
Debug.Log("player ---> " + player.name);
if (players.Contains(player))
{
return;
}
Debug.Log("players ---> " + players);
players.Add(player);
}
}
PlayerScript:
public class GamePlayerManager : NetworkBehaviour
{
[System.NonSerialized]
public bool isPlayingOnTorn = true;
void Awake()
{
GameController.instance.EventOnNewPlayerAddedDelegate += OnRegisterPlayer;
}
private void Start()
{
if (!isLocalPlayer)
{
return;
}
}
public override void OnStartServer()
{
GameObject gc = (GameObject)Instantiate(NetworkManager.singleton.spawnPrefabs[2], transform.position, transform.rotation);
NetworkServer.Spawn(gc);
}
void OnRegisterPlayer(){
if (isLocalPlayer)
{
//GameController.instance.RegisterPlayer(this);
//RpcRegisterPlayer();
CmdRegisterPlayer();
}
}
[Command]
void CmdRegisterPlayer(){
Debug.Log("-------------Command Register player -------------");
GameController.instance.RegisterPlayer(this);
}
[ClientRpc]
void RpcRegisterPlayer()
{
Debug.Log("------------- RPC REgister Player -------------");
GameController.instance.RegisterPlayer(this);
}
}
I think I already saw the problem here.
The problem is that GameController is spawned by the playerScript(just if the player is also the host) in the function OnStartServer(). So the first problem is that the GameController doesn't detect the host player because it is not a new connections. And the second problem is when a second client is connected, the GameController detect the client connection and send the event signal faster than the client wakeup, so when the client is already working the signal is gone.
I solved the problem deleting this signal and checking directly in the playerScript if the GameController exists then check if the player is already registered.
My question now is that if there is anyway to instance and awake the server objects before the player(Host), which I understand that the answer is "no" because as the player is the host it needs to be running to have the server.
And the second question is if there is anyway or any signal to know that a new player is connected, and wait until it is awaked.
Thank you very much.
You can check the hole thread at Unity forums: https://forum.unity.com/threads/multiplayer-syncevent-problem-with-non-player-objects.589309/

Unity Networkserver.SpawnwithclientAuthroity doesn't work on host

I followed a Youtube tutorial https://www.youtube.com/watch?v=afz2An4X0vw on how to use Unity networking.
The point is to have an object with a network ID component spawn the player and in order to control the player we need to give the client spawning the player authority over that player.
However, having done exactly the same as in the video, whenever I host the game (with the unity Network HUD) the hasAuthority returns false instead of true on only the host.
It returns true on all the other clients (as it should).
To me it seems as if the hosting person doesn't see itself as a client but only the server, yet this doesn't happen in the video linked above.
How do I make the hosting player return true on hasAuthority as it refuses to do so with the code below?
The script on the object (spawned when the client connects):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class ThePlayerScript : NetworkBehaviour
{
public GameObject abigail;
public GameObject abigailN;
void Start ()
{
if(isLocalPlayer == false)
{
return;
}
CmdSpawnPlayer();
}
void Update () { }
[Command]
void CmdSpawnPlayer()
{
abigailN = Instantiate(abigail);
NetworkServer.SpawnWithClientAuthority(abigailN, connectionToClient);
}
}
The code on the player that is spawned by the gameobject:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class Abigail : NetworkBehaviour
{
public GameObject mainCamera;
Vector3 myLocation;
Vector3 cameraView;
// Use this for initialization
void Start ()
{
if(hasAuthority == false)
{
Destroy(this);
return;
}
mainCamera = GameObject.Find("Main Camera");
}
}
(I removed the Update function in the Abigail class as it is irrelevant as it doesn't get executed.)
I found out that hasAuthority in this case will only return false in the Start() method. If you use it in the Update() method it will return true properly if you assign authority simply as you do in "ThePlayerScipt" with:
NetworkServer.SpawnWithClientAuthority(abigailN, connectionToClient);
Hope that helps :)

UDP Client - how to close after BeginReceive

I need to receive small UDP packets from within my ERP system (Dynamics Nav). I created a class with some code from the internet (first time I use code for UDP), added a COM interface and use the resulting DLL within Nav. It worked right away, but I realized that the udp.Close() function did not really close the thing, and I couldn´t instantiate the class a second time.
After looking at many topics about udp.BeginReceive() at stackoverflow and on other sites, I understand the reason for that. There needs to be a final udp.EndReceive call before closing the upd object.
Since the class is running in the background its using a callback function defined by udp.BeginReceive. The callback function then gets the data by calling udp.EndReceive and finally stores it to a string. Nav can retrieve that string whenever it wants that data trough a simple property.
public string GetMessage { get { return(message); } }
public void Start()
{
udp = new UdpClient(PORT_NUMBER);
StartListening();
}
public void Stop()
{
udp.Close();
}
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
message = Encoding.ASCII.GetString(bytes);
StartListening();
}
Everthing is fine, except ...
Nav calls Start() which issues StartListening(), which defines the callback function. After receiving the data through udp.EndReceive it calls StartListening() again - this part is working fine.
As soon, as Nav calls the Stop() function however, the trouble starts, and I understand that this is, bacause there is no final call to EndReceive and thus an open session.
One may say, why don´t do an EndReceive() within the Stop() function before udp.Close()? Well, because I couldn´t find the correct parameters for that call.
Actually I do have a working class. I don´t try to close the session within the Stop() function but instead set a bool variable. The next time the callback function is issued, depending on that bool it doesn´t do a StartListening() but a upd.CLose() instead. And finally, to make sure there will be data to issue the callback function, I call my Send() function sending some single character.
While the following code is working perfectly, I know it´s kind of crazy:
public string GetMessage { get { return(message); } }
public void Start()
{
active = true;
udp = new UdpClient(PORT_NUMBER);
StartListening();
}
public void Stop()
{
active = false; // force callback function to close session
Send("x"); // issue callback function ... crazy
//udp.Close();
}
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
message = Encoding.ASCII.GetString(bytes);
if (active) StartListening(); else udp.Close();
}
Does anyone have a hint how to issue EndReceive() within my Stop() function before calling udp.Close()?
Thanks in advance
Michael
I recently found a solution just by using a Thread() instead of the async receive - works perfectly so far:
public class Bos_UDP
{
private UdpClient udp;
const int PORT_NUMBER = 15000;
private String message = "";
public Thread receiveThread;
public string GetMessage { get { return(message); } }
public void Start()
{
udp = new UdpClient(PORT_NUMBER);
receiveThread = new Thread(ThreadReceive);
receiveThread.Start();
}
public void Stop()
{
receiveThread.Abort(new object());
udp.Close();
}
public void ThreadReceive()
{
IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
while (true)
{
var data = udp.Receive(ref ip);
message = Encoding.Default.GetString(data);
}
}
public void Send(string message)
{
UdpClient client = new UdpClient();
IPEndPoint ip = new IPEndPoint(IPAddress.Parse("255.255.255.255"), PORT_NUMBER);
byte[] bytes = Encoding.ASCII.GetBytes(message);
client.Send(bytes, bytes.Length, ip);
client.Close();
}
}

In VR development ,how can I make a handle shotting?

I want to make an example of shot,
then I wrote this in the handle button event,
using UnityEngine;
using System.Collections;
public class fire : MonoBehaviour {
public GameObject bullet;
SteamVR_TrackedObject trackedObj;
void start() {
trackedObj = GetComponent<SteamVR_TrakedObject>();
}
void Update() {
var device = SteamVR_Controller.Input((int)trackedObj.index);
if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger)) {
GameObejct obj = Instantiate(bullet,transform.position);
Vector3d fwd = transform.TransformDirection(Vector3.forward);
obj.GetComponent.<Rigidbody>().AddForce(fwd*2800);
}
}
}
but when debugging and I press handle button ,it didn't produce a bullet,and had erred at the line
var device = SteamVR_Controller.Input((int)trackedObj.index);,
the error is:
Object reference not set to an instance of an object.
First You should need to confirm that your fire script is attached to your controller object and your controller object also attached SteamVR_TrackedObject script (which provide by steam plugin).
Then, lastly ensure this line is executing
void start() {
trackedObj = GetComponent<SteamVR_TrakedObject>();
}