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

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
}
}

Related

Mirror/UNET c# - No authority on object even when using Command & ClientRpc - What am I missing?

Long story: I have made a multiplayer chat using Mirror/Unet that works. I have made it so after x number of seconds, the gameobject that displays the chat (“textContainer”) goes inactive. I would like it so that when client 1 presses “Submit”, all the client’s gameobjects for textContainer goes active again. But it only works on the client that presses Submit, and not on ALL clients. I have set up the functions in [Client] [Command] and [ClientRpc] but am still getting an error about no authority on object.
I think it is because client 1 does not have authority to request client 2 to activate their UI panel. I thought the Command and ClientRpc would have fixed this issue?
Short story: So, for simplicity, say when client 1 presses the Input for “Submit”, I would like all client’s textContainer GameObjects to go active.
I am hoping someone might be able to point me in the right direction. Cheers.
This script would be attached to the player prefab.
public GameObject textContainer;
[Client]
void Update()
{
if (Input.GetAxisRaw("Submit") == 1)
{
CmdActivateChatClientRPC();
textContainer.SetActive(true);
}
}
[Command]
private void CmdActivateChatClientRPC()
{
ActivateChatClientRPC();
}
[ClientRpc]
private void ActivateChatClientRPC()
{
textContainer.SetActive(true);
}
You are not checking whether you have the authority over this object.
void Update()
{
// Does your device have the authority over this object?
// only then you can invoke a Cmd
if(!hasAuthority) return;
if (Input.GetAxisRaw("Submit") == 1)
{
CmdActivateChatClientRPC();
textContainer.SetActive(true);
}
}
[Command]
private void CmdActivateChatClientRPC()
{
ActivateChatClientRPC();
}
// This will be executed on ALL clients!
// BUT only for this one instance of this class
[ClientRpc]
private void ActivateChatClientRPC()
{
textContainer.SetActive(true);
}
If it is rather about only you trying to invoke a command on an object that belongs to the server it gets tricky ;)
You will have to relay the command through your local player object and then on the server forward the call to the according object e.g. referencing the actual target via it's Network identity.
To me it sounds a bit like you are having each player prefab has this class and you want to invoke the RPC on all of them.
This is something that can only be done by the server like e.g.
void Update()
{
// Does your device have the authority over this object?
// only then you can invoke a Cmd
if(!hasAuthority) return;
if (Input.GetAxisRaw("Submit") == 1)
{
CmdActivateChatClientRPC();
textContainer.SetActive(true);
}
}
[Command]
private void CmdActivateChatClientRPC()
{
foreach (var player in NetworkManager.singleton.client.connection.playerControllers)
{
if (player.IsValid)
{
player.gameObject.GetComponent<YourScriptName>().ActivateChatClientRPC();
}
}
}
Seems that .client.connection.playerControllers was removed.
I guess in that case you would need a custom NetworkManager and keep track of them yourself via OnServerConnect and OnServerDisconnect somewhat like
public CustomNetworkManager : NetworkManager
{
public static readonly HashSet<NetworkConnection> players = new HashSet<NetworkConnection>();
public override void OnServerConnect(NetworkConnection conn)
{
base.OnServerConnect(conn);
players.Add(conn);
}
public override void OnServerDisconnect(NetworkConnection conn)
{
base.OnServerDisconnect(conn);
players.Remove(conn);
}
}
and then instead access
foreach (var player in CustomNetworkManager.players)
{
player.identity.GetComponent<YourScriptName>().ActivateChatClientRPC();
}
How about
client->server: request to activate
and then
server->client: singal
[SyncVar(hook = nameof(AcivateText))]
bool textActivated = false;
[Command]
private void CmdActivateChatClientRPC()
{
ActivateChatClientRPC();
}
void AcivateText(bool oldval, bool newval)
{
textContainer.SetActive(newval);
}

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();
}
}

Disconnect client from IHubContext<THub>

I can call InvokeAsync from server code using the IHubContext interface, but sometimes I want to force these clients to disconnect.
So, is there any way to disconnect clients from server code that references the IHubContext interface?
Step 1:
using Microsoft.AspNetCore.Connections.Features;
using System.Collections.Generic;
using Microsoft.AspNetCore.SignalR;
public class ErrorService
{
readonly HashSet<string> PendingConnections = new HashSet<string>();
readonly object PendingConnectionsLock = new object();
public void KickClient(string ConnectionId)
{
//TODO: log
if (!PendingConnections.Contains(ConnectionId))
{
lock (PendingConnectionsLock)
{
PendingConnections.Add(ConnectionId);
}
}
}
public void InitConnectionMonitoring(HubCallerContext Context)
{
var feature = Context.Features.Get<IConnectionHeartbeatFeature>();
feature.OnHeartbeat(state =>
{
if (PendingConnections.Contains(Context.ConnectionId))
{
Context.Abort();
lock (PendingConnectionsLock)
{
PendingConnections.Remove(Context.ConnectionId);
}
}
}, Context.ConnectionId);
}
}
Step 2:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<ErrorService>();
...
}
Step 3:
[Authorize(Policy = "Client")]
public class ClientHub : Hub
{
ErrorService errorService;
public ClientHub(ErrorService errorService)
{
this.errorService = errorService;
}
public async override Task OnConnectedAsync()
{
errorService.InitConnectionMonitoring(Context);
await base.OnConnectedAsync();
}
....
Disconnecting without Abort() method:
public class TestService
{
public TestService(..., ErrorService errorService)
{
string ConnectionId = ...;
errorService.KickClient(ConnectionId);
In alpha 2 there is the Abort() on HubConnectionContext you could use to terminate a connection. I don't see, however, an easy way to access it from outside the hub.
Because you control the clients you could just invoke a client method and tell the client to disconnect. The advantage is that the client disconnect gracefully. The disadvantage is that it requires sending the message to the client instead of disconnecting the client solely on the server side.

How can send data from server to client with Unity Unet

i am using UNet. I have two gamers in scene. One of them is server one of them is client. I can get client data with [Command] and [ClientRPC] but i can't send data from server to client how can i solve this problem ?
Note: The server is a player also.
Here is my sending data from client to server code :
[Command]
void CmdSendDizilimToServer(string dizilim){
if (isLocalPlayer)
RpcSetPlayerDizilim(dizilim);
else
this.Sira = dizilim;
}
[ClientRpc]
void RpcSetPlayerDizilim(string dizilim){
this.Sira = dizilim;
}
'Dizilim' means 'Sequence'. 'Sira' means 'Order'.
Actually in server and client have their own 'Order' string. When the game begining one gameobject spawned. And i want server get client 'Order' script(i can do this) the client get server 'Order' string(i can't do that). How can i solve that ?
It is my spawned player object script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.Networking;
public class MultiPlayerOyunKontrol : NetworkBehaviour {
CreateCharacter cc;
public string Sira;
GameObject EnemyObject;
[SyncVar (hook = "DizilimGetir")]
string OyuncuDizilimi;
// Use this for initialization
void Start () {
if (!isLocalPlayer)
{
this.gameObject.name = "Oyuncu2";
OyuncuDizilimi = PlayerPrefs.GetString("Player Dizilim");
}
else
gameObject.name = "Oyuncu1";
}
// Update is called once per frame
void Update () {
}
public override void OnStartLocalPlayer(){
//CmdSendNameToServer (PlayerPrefs.GetString("Player Name"));
CmdSendDizilimToServer (PlayerPrefs.GetString ("Player Dizilim"));
}
[Command]
void CmdDusmanAktif(){
RpcAktiflestir ();
}
[Command]
void CmdSendNameToServer(string nameToSend)
{
RpcSetPlayerName(nameToSend);
}
[Command]
void CmdSendDizilimToServer(string dizilim){
if (isLocalPlayer)
RpcSetPlayerDizilim(dizilim);
else
this.Sira = dizilim;
}
[ClientRpc]
void RpcAktiflestir(){
EnemyObject = GameObject.FindGameObjectWithTag ("DusmanKarakterleri");
EnemyObject.SetActive (true);
}
[ClientRpc]
void RpcSetPlayerName(string name)
{
this.gameObject.name = name;
}
[ClientRpc]
void RpcSetPlayerDizilim(string dizilim){
this.Sira = dizilim;
}
void DizilimGetir(string dizilim) {
if(!isLocalPlayer)
this.Sira = dizilim;
}
}
I solve my question. When the client spawned the OnStartLocalPlayer() triggered but for only Server. The server can read client datas but the client can't trigger that function. And i write this code.
private void OnGUI()
{
if (isLocalPlayer)
{
if (GUI.Button(new Rect(0, 500, 100, 100), "Gonder"))
{
CmdSendDizilimToServer(PlayerPrefs.GetString("Player Dizilim"));
}
}
}
after that when i click button the client can read server datas.

Unity TCP Server Hang Unity scene

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