Is there a way to do this? I want to prevent the local client running on the server (host) from sending some commands while allowing all the remote clients to do so. Is this possible? Currently the code below runs on ALL clients. Is there a way to modify the script to make it function in this way? Thank you!
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
public class Player_Data : NetworkBehaviour
{
private void Start()
{
if (!isLocalPlayer)
{
return;
}
CmdSendServerData();
}
[Command]
void CmdSendServerData()
{
DisplayDataOnScreen("CmdSendServerData Called");
}
[Server]
void DisplayDataOnScreen(string data)
{
GameObject infoDisplayText = GameObject.Find("InfoDisplay");
infoDisplayText.GetComponent<Text>().text += data + "\n";
}
}
Use isServer property. You can check it before cammand call.
public class Player_Data : NetworkBehaviour
{
private void Start()
{
if (isLocalPlayer && !isServer)
{
CmdSendServerData();
}
}
[Command]
void CmdSendServerData()
{
DisplayDataOnScreen("CmdSendServerData Called");
}
[Server]
void DisplayDataOnScreen(string data)
{
GameObject infoDisplayText = GameObject.Find("InfoDisplay");
infoDisplayText.GetComponent<Text>().text += data + "\n";
}
}
Related
I can pass data from a different thread to the main thread in c# by using invoke (see below code), but how do I pass data the other direction? trying to pass by reference doesnt seem to be working. Using a global doesnt work either since there are multiple threads.
Ideally I would constantly be updating the local variable in the WS_Client function and then whenever I get a request in mywebsocketserver, it triggers the handle/action which would grab that constantly updated variable and pass it to the mywebsocketserver thread. Mywebsocketserver would then send the data out via the socket.
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class OrientationEvent
{
public event Action<Vector3> OnOrientationUpdate;
}
public class WS_Client : MonoBehaviour
{
public static GameObject _cube;
private Action<Vector3> callback;
WebSocket ws;
[SerializeField] float speed = 50.0f;
private Vector3 _orientation;
// Start is called before the first frame update
void Start()
{
_cube = GameObject.Find("MyCube");
if (_cube == null)
{
Debug.Log("couldnt find MyCube at first");
}
WebSocketServer wssv = new WebSocketServer(8080);
Action<Vector3> callback = HandleOrientationUpdate;
MyWebSocketServer wsService = new MyWebSocketServer(callback);
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService);
wssv.Start();
}
public WS_Client(OrientationEvent orientationEvent)
{
callback = HandleOrientationUpdate;
}
private void Update()
{
_cube.transform.rotation = Quaternion.Euler(_orientation);
}
private void HandleOrientationUpdate(Vector3 orientation)
{
Debug.Log("tries to rotate mycube");
Debug.Log(orientation);
// Update the cube's orientation using the data from the event
try
{
_orientation = orientation;
Debug.Log("translates the orientation");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private Action<Vector3> callback;
public MyWebSocketServer(Action<Vector3> callback)
{
this.callback = callback;
}
protected override void OnMessage(MessageEventArgs e)
{
string data = e.Data;
string[] components = data.Split(' ');
Debug.Log("splits components");
float x = float.Parse(components[0]);
float y = float.Parse(components[1]);
float z = float.Parse(components[2]);
Debug.Log("parses components");
Vector3 orientation = new Vector3(x, y, z);
// Vector3 vector = new Vector3((float)x, (float)y, (float)z);
Debug.Log("puts them in vector3");
try
{
callback?.Invoke(orientation);
Debug.Log("invokes action");
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
As said I don't see the nead to pass anything as a ref here.
I would rather do it explicit through a kind of "MainThreadDispatcher" thing like e.g.
public class WS_Client : MonoBehaviour
{
...
// Thread-safe stack
readonly ConcurrentStack<Vector3> lastReceived = new();
void Update()
{
// On the main thread handle the latest received value
if(lastReceived.TryPop(out var value)
{
_cube.transform.rotation = Quaternion.Euler(value);
// erase the others as they are outdated
lastReceived.Clear();
}
}
void HandleOrientationUpdate(Vector3 v)
{
// here only store for being handled on main thread
lastReceived.Push(v);
}
}
The same way in the other direction, here it depends a bit in your needs whether you need those reliable as a queue or again only the latest value (in that case I'd rather use a stack as above)
public class MyWebSocketServer : WebSocketBehavior
{
readonly ConcurrentQueue<Vector3> toSend = new();
readonly WaitHandle wait = new AutoResetEvent(false);
public MyWebSocketServer(...)
{
...
var sendThread = new Thread(SendThread);
sendThread.Start();
}
public void Send(Vector3 v)
{
toSend.Enqueue(v);
wait.Set();
}
private void SendThread()
{
while(true)
{
// Allows this thread to go idle => not eating unnecessary performance
wait.WaitOne();
// if using the queue go through all items, otherwise again only pop the newest value and erase the others
while(toSend.TryDequeue(out var v))
{
// However you finally send your value using ToString etc
// tbh I would prefer a byte[] based protocol over string
// it would be faster to parse and also more efficient bandwidth wise
SubmitVector(v);
}
}
}
So now you only need to call Send with the newest values to submit.
You most probably will not want to do this every frame as this will overload the network bandwidth a lot. Rather use a time interval like e.g. 0.2 seconds and interpolate at the receiver side.
I also found a simpler way to do everything, just share the variable in a shared class:
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Server;
using System;
public class MySharedData
{
public Vector3 handPosition { get; set; }
public Quaternion handRotation { get; set; }
// public Transform handData { get; set; }
}
public class WS_Client : MonoBehaviour
{
MySharedData sharedData = new MySharedData();
WebSocket ws;
public Transform handR;
void Start()
{
handR = GameObject.Find("LeftHandAnchor").GetComponent<Transform>();
// handR = GameObject.Find("RightHandAnchor").GetComponent<Transform>();
WebSocketServer wssv = new WebSocketServer(8080); //this websocketserver sets up a place for cpus to talk
MyWebSocketServer wsService = new MyWebSocketServer(sharedData); //this is actually a type of websocketbehavior (with a callback connected to the main thread) which
wssv.AddWebSocketService<MyWebSocketServer>("/MyWebSocket", () => wsService); //then gets implemented onto the websocketserver
wssv.Start();
}
private void Update()
{
sharedData.handPosition = handR.transform.position;
sharedData.handRotation = handR.transform.rotation;
}
}
public class MyWebSocketServer : WebSocketBehavior
{
private MySharedData _sharedData;
public MyWebSocketServer(MySharedData sharedData)
{
this._sharedData = sharedData;
}
protected override void OnMessage(MessageEventArgs e)
{
try
{
string data = e.Data;
Console.WriteLine("Received message: " + e.Data);
Vector3 handPosition = _sharedData.handPosition;
Quaternion handRotation = _sharedData.handRotation;
string responseMessage = string.Format("Position:({0},{1},{2}), Rotation:({3},{4},{5})",
handPosition.x, handPosition.y, handPosition.z,
handRotation.x, handRotation.y, handRotation.z);
Debug.Log(responseMessage);
Send(responseMessage);
}
catch (Exception ex)
{
Debug.LogError("An error occurred: " + ex.Message);
}
}
protected override void OnOpen()
{
// Handle client connection here
Debug.Log("someone connected");
}
protected override void OnClose(CloseEventArgs e)
{
// Handle client disconnection here
Debug.Log("someone disconnected");
}
}
In short. I have a tunnel building game where every tunnel piece has a Vector2 ChunkedPostion that generally describes where they are located in world space.
The problem is I need to Recalculate some things when one of these tunnels is destroyed and it would take longer than necessary to reload the whole map. Therefore, I'm hoping someone could tell me how to pass along this Chunkedpostion in an event that's called on the destruction of a tunnel so each tunnel could quickly check if the event is relevant to them before making the calculations.
simplified code:
public class transmitter : MonoBehaviour
{
public delegate void SendSignel();
public static event SendSignel OnSendSignel;
Vector2 ChunkedPostion;
private void Start()
{
ChunkedPostion = new Vector2(transform.position.x, transform.position.z);
}
void Update()
{
if (Input.GetKeyDown("s"))
{
OnSendSignel();//<= i would like to pass along ChunkedPostion in the Event
}
}
}
public class receiver : MonoBehaviour
{
//and then have someway to "Debug.Log" the recieved coordinates
private void OnEnable() { transmitter.OnSendSignel += PrintSignel; }
private void OnDisable() { transmitter.OnSendSignel -= PrintSignel; }
void PrintSignel()
{
Debug.Log("received");
}
}
Working Code:
public class transmitter : MonoBehaviour
{
//public delegate void SendSignel();
public delegate void SendSignel(Vector2 SenderPostion);
public static event SendSignel OnSendSignel;
Vector2 ChunkedPostion;
private void Start()
{
ChunkedPostion = new Vector2(transform.position.x, transform.position.z);
}
void Update()
{
if (Input.GetKeyDown("s"))
{
OnSendSignel(ChunkedPostion);//<= i would like to pass along ChunkedPostion in the Event
}
}
}
public class receiver : MonoBehaviour
{
//and then have someway to "Debug.Log" the recieved coordinates
private void OnEnable() { transmitter.OnSendSignel += PrintSignel; }
private void OnDisable() { transmitter.OnSendSignel -= PrintSignel; }
//void PrintSignel()
//{
// Debug.Log("received");
//}
void PrintSignel(Vector2 SenderPostion)
{
Debug.Log("received");
Debug.Log("at: " + SenderPostion);
}
}
I'm pretty sure you're using the wrong syntax, at least in the question you are.
public static event SendSignel OnSendSignel;
//should be
public static event EventHandler<Vector2> OnSendSignal
using this you'll be able to provide OnSendSignal a vector2 when you invoke it
I am making a simple memory game. I have already made the game work with smartfoxserver. But when I tried to build another machine and let them run simultaneously, one player would be log out when another log in. Could you guys help me with this one. Here is the code on the client. Also is once the game start is there any way for the two machine to connect to eachother. For example showing the score from Player1 to Player2. Thank you.
using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Entities.Data;
using Sfs2X.Requests;
using Sfs2X.Util;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using Sfs2X.Requests.MMO;
public class GameController : MonoBehaviour
{
public string defaultHost = "127.0.0.1";
public int defaultTcpport = 8888;
public int defaultWsport = 8080;
public string Zonename = "BasicExamples";
public string Username = "guest";
public string Roomname = "The Lobby";
private SmartFox sfs;
void Awake()
{
SourceSprites = Resources.LoadAll<Sprite>("Sprite/GameImages");
}
void Start()
{
Login_Click();
TotalGuess = btnlist.Count / 2;
GetButton();
AddListener();
AddSprites();
shuffle(GameSprite);
}
public void Login_Click()
{
if (sfs == null || !sfs.IsConnected)
{
sfs = new SmartFox();
sfs.ThreadSafeMode = true;
sfs.AddEventListener(SFSEvent.CONNECTION, OnConnection);
sfs.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
sfs.AddEventListener(SFSEvent.LOGIN, OnLogin);
sfs.AddEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
sfs.AddEventListener(SFSEvent.ROOM_JOIN, OnJoinRoom);
sfs.AddEventListener(SFSEvent.ROOM_JOIN_ERROR, OnJoinRoomError);
sfs.AddEventListener(SFSEvent.EXTENSION_RESPONSE, GetResult);
ConfigData cfg = new ConfigData();
cfg.Host = defaultHost;
cfg.Port = defaultTcpport;
cfg.Zone = "BasicExamples";
cfg.Debug = true;
Debug.LogError("defaultHost " + defaultHost);
Debug.LogError("defaultTcpport " + defaultTcpport);
sfs.Connect(cfg);
}
}
void OnLogin(BaseEvent evt)
{
Debug.Log("Login Success");
sfs.Send(new JoinRoomRequest("The Lobby"));
}
void OnJoinRoom(BaseEvent evt)
{
Debug.Log("Joined Room"+ evt.Params["room"]);
}
void OnJoinRoomError(BaseEvent evt)
{
Debug.Log("Join Room Error" + evt.Params["errorMessage"]);
}
void OnLoginError(BaseEvent evt)
{
Debug.Log("Login Error"+ evt.Params["errorMessage"]);
}
void OnConnection(BaseEvent evt)
{
if ((bool)evt.Params["success"])
{
Debug.Log("Connection Success");
sfs.Send(new LoginRequest(Username, "", Zonename));
}
else
{
Debug.Log("Connection Error");
}
}
void OnConnectionLost(BaseEvent evt)
{
}
Your problem is that all of your clients have the same username when you do LoginRequest.
SFS automatically disconnect other users with the same username.
You must create a unique username for all of your clients to they can connect together.
The simplest way to do this is to use the device id as a username.
sfs.Send(new LoginRequest(SystemInfo.deviceUniqueIdentifier, "", Zonename));
hope this helps.
I have the following simple class extending Networkbehaviour. I need to wait for a connection to server in order to call a [Command] funtcion.
Any UNET or multiplayer experts able to tell me what I am doing wrong?
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;
public class KeepClientConnected : NetworkBehaviour {
public int pingInterval = 30;
// Use this for initialization
void Awake () {
DontDestroyOnLoad(transform.gameObject);
}
void Start(){
}
void OnDisconnectedFromServer(NetworkDisconnection info)
{
if (Network.isServer)
Debug.Log("Local server connection disconnected");
else
if (info == NetworkDisconnection.LostConnection)
Debug.Log("Lost connection to the server");
else
Debug.Log("Successfully diconnected from the server");
}
private void OnConnectedToServer()
{
Debug.Log("0 ASSERT OnConnectedToServer");
StartCoroutine(myCoRoutine());
}
[Command]
void Cmd_KeepConnection()
{
Debug.Log("2 ASSERT calling KeepConnection on Server");
}
IEnumerator myCoRoutine()
{
while (true)
{
Cmd_KeepConnection();
Debug.Log("1 ASSERT calling KeepConnection on Server");
yield return new WaitForSeconds(2);
}
}
}
I haven't used OnConnectedToServer but I can give you a couple of alternatives.
Make a Player. Have this network behavior exist on that player. The player will only get created once the client is connected.
Extend NetworkManager and override OnStartClient while making sure to still call base.OnStartClient. In this method you can be sure that the client is started and ready as it passes a reference to the client to you.
As a side note I used the following code in my player's Update method:
if (KeepAlive)
{
LastPing += Time.deltaTime;
if (LastPing > 10)
{
LastPing = 0;
CmdPing();
}
}
I'm a Chinese developers, English is not very good, please predecessors read it for editing, thanks in advance.
In Unity, i write one script to listener an Event and in delegate function, i add another script the same listener in previous Event, but why the same listener be called immediately?
The following is add another script the same listener script:
using System.Collections.Generic;
using UnityEngine;
public class SceneManager : MonoBehaviour
{
private static SceneManager m_Instance;
public static SceneManager Instance
{
get
{
if (m_Instance == null)
{
m_Instance = (SceneManager)FindObjectOfType(typeof(SceneManager));
}
if (!m_Instance)
{
Debug.LogError("SceneManager could not find himself!");
}
return m_Instance;
}
}
[HideInInspector]
public List<EventDelegate> EventDelegetDo = new List<EventDelegate>();
public void RegisterBackBtnDo()
{
EventDelegate.Add(EventDelegetDo, Do);
}
public void UnRegisterBackBtnDo()
{
EventDelegate.Remove(EventDelegetDo, Do);
}
private void Start()
{
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.T))
{
EventDelegate.Execute(EventDelegetDo);
}
}
private void Do()
{
Debug.Log("===============ZJDebug=============== From SceneManager");
}
}
The following is initial register listener script:
using UnityEngine;
public class TDelegate : MonoBehaviour
{
public void RegisterBackBtnDo()
{
EventDelegate.Add(SceneManager.Instance.EventDelegetDo, Do);
}
public void UnRegisterBackBtnDo()
{
EventDelegate.Remove(SceneManager.Instance.EventDelegetDo, Do);
}
private void Start()
{
RegisterBackBtnDo();
}
private void Update()
{
}
private void Do()
{
Debug.Log("===============ZJDebug=============== From TDelegate");
SceneManager.Instance.RegisterBackBtnDo();
//Invoke("WaitForTest", float.NegativeInfinity);
}
private void WaitForTest()
{
SceneManager.Instance.RegisterBackBtnDo();
}
}
I have tried multiple variations of this, if invoke float.NegativeInfinity second register it not be called Immediately, can someone tell me why? in addition to delay call There is no better way called by after? English is too bad please forgive me thanks!