Sending messages to all clients in Unity PUN (C#) - c#

this is a silly question but i can't figgure it out, I tried looking it out but nothing was what i was looking, so i thought that asking might be a good way.
In my game i would like a client to be able to send messages (messages not for the users, if not for the other clients to interpreted into code) to everyone in it's room:
"(User1): How many points does everyone have?"
"(User2): Hey, I got 5 points"
"(User3): Hey, I got 10 points"
"(User1): Time is over, tell me your points:"
"(User2): Hey, I got 20 points"
"(User3): Hey, I got 30 points"
"(User1): User 3 won"
Of course, this is more of a graphical example, what I want is to be able to send messages to all clients (in the room) so other clients can process tjem. I would like to know how to do it and if this is a possible trough PUN (Unity Photon Network). Or if there is other way to do this over UnityNetworking. Additionally, this would be over LAN not over the internet.
Thank you in advanced.

You are looking for RPC's:
https://doc.photonengine.com/en-us/pun/current/gameplay/rpcsandraiseevent
This allows you to call to only the master, to only clients, or to a specific player.
Before the function you put "[PunRPC]" (without the qoutes), this makes the method callable by RPC:
[PunRPC]
public void GetScore(int score)
{
//Do something
}
The class that calls the RPC requires have a photonview attached or extend from Pohotn.PunBehaviour, and calls the function as following:
photonView.RPC("function", targets, arguments);
The "function" is the rpc as a string, in the example case it would be GetScore, the target could be either a PhotonPlayer instance, or you could use the PhotonTargets enum.
I like to create a singleton class that handles the RPC's (Remember, this only works if the class has only 1 instance):
using UnityEngine;
public class RPC : Photon.PunBehaviour
{
public static RPC singleton;
private void Awake()
{
if(singleton != null && singleton != this)
Destroy(this);
singleton = this;
}
//Called by someone who wants to set the score
public void CallSetScore(sbyte score)
{
this.photonView.RPC("SetScore", PhotonTargets.All, score);
}
[PunRPC]
public void SetScore(sbyte score)
{
//Do something with the score
}
}

Related

How do you stop a client/host in Unity Netcode for Gameobjects?

I managed to get some kind of multiplayer working with Unity Netcode which I understand is very new, but I'm running into a pretty big issue. I can't seem to be able to disconnect from the server instance. This causes an issue in which I load the game scene and the NetworkManager instance is spawned. Then I return to the main menu scene and load the game scene again, and now there are 2 NetworkManager instances which obviously causes errors.
I read in the docs that you can use this:
public void Disconnect()
{
if (IsHost)
{
NetworkManager.Singleton.StopHost();
}
else if (IsClient)
{
NetworkManager.Singleton.StopClient();
}
else if (IsServer)
{
NetworkManager.Singleton.StopServer();
}
UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
}
However all these stop functions don't seem to exist at all under the singleton. Am I missing a library or something?
For now I am using a workaround which is just disabling "Don't destroy" and then the NetworkManager is destroyed after switching scenes, but what if I wanted to keep it after switching a scene? Is there no way to control when the Server/Client is stopped? I'm assuming this is a bug but I just want to make sure.
void Disconnect()
{
NetworkManager.Singleton.Shutdown();
}
Disconnects clients if connected and stops server if running.
To answer your full question, you also need a Cleanup() function in your non-networked scene that checks for astray NetworkManager singletons and destroys them. This will avoid unnecessary duplication of network managers.
void Cleanup()
{
if (NetworkManager.Singleton != null)
{
Destroy(NetworkManager.Singleton.gameObject);
}
}

Unity MLAPI ServerRPC not getting called

I'm implementing a system in which a player can pickup an item from the game world. After picking the item up, I want to destroy the game object. To do this, I figured I'd make a ServerRPC that deletes the object, but the ServerRPC is not getting called. I tried making a ClientRPC as well, but that one isn't being called either. I noticed that it only gets called when the host of the game tries to pick up the item -- any ideas on how to fix this? The documentation is pretty bare but please let me know if I missed anything. Here is my code:
public override void Interact(GameObject player)
{
player.GetComponent<PlayerInventory>().AddItem(this.item);
Debug.Log("before");
TestClientRpc();
TestServerRpc();
}
[ServerRpc]
public void TestServerRpc()
{
Debug.Log("server");
// Destroy(gameObject);
}
[ClientRpc]
public void TestClientRpc()
{
Debug.Log("client");
// Destroy(gameObject);
}
EDIT: image of the components on the weapon
EDIT: This class extends a class that extends the networkbehaviour class. The objects are just sitting in the scene at start -- they are not spawned/instantiated (not sure if that matters or not).
The most plausible solution to this problem , either it is running into a error in some previous lines or as some other person pointed out , requireOwnership is not set to false .
For in depth analysis of MLAPI , https://youtube.com/channel/UCBStHvqSDEF751f0CWd3-Pg
I found the tutorial series on this channel best
I needed to add the following attribute to my ServerRpc:
[ServerRpc(RequireOwnership = false)]
since the player was not the owner of the item.
I have the same issue. Been looking at it for 2 days but I am inclined to think it is a bug to be honest.
I just spent 4+ hours banging my head against the wall because they didn't write this important info in the documentation. When calling ServerRpc from a client that doesn't own the Network Object, RPC attribute should be set to RequireOwnership = false.
For me the issue was that the GameObject(with NetwrokObject component) was sitting in the scene before starting the game, I couldn't use NetworkVariables nor ServerRpc. I think those are only usable on spawned object, but not sure. So I would say : either instantiate the object and add it to the NetworkManager or use normal variable on those objects.
Had the same issue but later realized I just forgot to inherit NetworkBehaviour
For Example:
using Unity.Netcode;
using UnityEngine;
class myObject : NetworkBehaviour {
public override void Interact(GameObject player)
{
player.GetComponent<PlayerInventory>().AddItem(this.item);
Debug.Log("before");
TestClientRpc();
TestServerRpc();
}
[ServerRpc]
public void TestServerRpc()
{
Debug.Log("server");
//Destroy(gameObject);
}
[ClientRpc]
public void TestClientRpc()
{
Debug.Log("client");
// Destroy(gameObject);
}
}
I had the same problem, where the ServerRpc wouldn't even be called. For me it worked after I made the GameObject that my script was attached to a prefab and added it to the NetworkManager's list of NetworkPrefabs.
Note: (I am using Netcode for GameObjects, I don't think it will be the same for other Multiplayer solutions)

C#: Access function of another class when having to use a default constructor and making it static is not an option

I am working on a little app that includes a broadcast receiver.
Broadcast receivers HAVE to HAVE a default constructor. Don't ask me why...
Anyway, This is it:
[BroadcastReceiver]
public class IntentReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (AudioManager.ActionAudioBecomingNoisy.Equals(intent.Action))
{
Activity_Player.Instance.PlayOrPauseLogic();
}
}
}
The problem is, that when this function fires (becoming noisy means someone unplugged the headset) I need to be able to stop the current music playing.
In my main class, I have a MediaPlayer object called mediaplayer. I now need to call this mediaplayer object with the ".Stop()" prompt.
This means however, that I cannot just create a new object from my class and then call Stop() on this media player, since this would not affect the currently playing media player.
I can also not hand the media player over into broadcast recevier, since the constructor needs to be the default one.
Lastley, making the mediaplayer object static is also NOT an option for many reasons now.
So what can I do?
What I did now is make a single instance of my activity and THEN call stop on this instance. This is a very bad solution since it causes memory leaks and I need to get rid of it.
Please help me.
Thank you! :)

Calling "this" function from another class. What is the best way?

I am working on an app that plays music. Now, I have a function in my main class that chooses a random new song and plays said song:
private void ChooseRandomNewSongAndPlay(bool songHasCompleted)
{
Random rnd = new Random();
int rndValue = rnd.Next(0, Mp3ObjectSmall.Count());
int currentPos = 0;
if (!songHasCompleted)
{
currentPos = mediaPlayer.CurrentPosition; // if song infact has completed, reset position to else save current position (when next has been pressed)
}
WriteSeekingToDataBase(currentPos, CurrentSongObject);
mediaPlayer.Stop();
if (Android.Net.Uri.Parse(CurrentSongObject.Mp3Uri) != null)
{
PhotoAlbumAdapter.OldSongUri = Android.Net.Uri.Parse(CurrentSongObject.Mp3Uri);
}
PhotoAlbumAdapter.NewSongUri = (Android.Net.Uri.Parse(Mp3ObjectSmall[rndValue].Mp3Uri));
PlayMusic((Android.Net.Uri.Parse(Mp3ObjectSmall[rndValue].Mp3Uri)));
}
But I also set up a broadcast receiver, so when the user is in his car and clicks on next song from the car stereo, I also want the above function to be played.
But here is the problem:
I cannot make this above function public static since it calls other non static functions. I would have to make those static too, but that would cause many, many other errors and is not a good solution at all I believe.
Also, I cannot create a new object of my main class in within the broadcast receiver as such: class xy = new class(). I cannot do that, because that would also create another object of my mediaplayer object, but this object needs to be the same to skip to a next some. If it isnt, just anoither song is played on top of the first song which of course is also not good.
Lastly, I cannot just hand over the class as a parameter to the constructor of the broadcast receiver. I am getting told then that the braodcast receiver needs to have a "standart constructor" so I cannot alter the parameters.
Unfortunately, these 3 optiones are all I believe I have and neither seems to work. What I really, really do not want to do is to copy paste all functions from my main class into the broadcast receiver for obvious reasons.
Can you guys help me out here?
Thank you!
You didn't specified if you broadcast receiver is an activity-local broadcast receiver or application-wide broadcast receiver.
LocalBroadcastReceivers can be created as activity's property and let you broadcast messages within the app, while classic BroadcastReceivers can let you respond to an action coming from outside your app even if your app is closed.
If you have a BroadcastReceiver you can, when it receives a message from, make it republish the message through LocalBroacastManager.
Also your activity must setup a LocalBroadcastReceiver that handles that particular internal broadcast message.
Doing so, you don't worry about Activity state. When the global BroadcastReceiver is invoked, it broadcast the message inside the application, and if there's some receiver listening to they will be invoked all, otherwise the message is simply ignored with no bugs, retain cycles or crashes.
I suppose you are using Xamarin Android.
You can get the full documentation about receivers here
https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/broadcast-receivers
The easiest way is to just create a single static instance from your class and fire the functions:
public static Activity_Player Instance;
then in the broadcast recevier:
Instance.MYFUNCTION
How about creating a property on the broadcast receiver and using that to pass a reference to the main class?
public class MyBroadcastReceiver
{
// Constructor
public MyBroadcastReceiver() {}
// property
public MyMainClass {get;set;}
}
public MyMainClass
{
private void CreateBroadcastReceiver()
{
var br = new MyBroadcastReceiver();
br.MyMainClass = this;
}
}

C# - Design Thoughts on Passing by Reference

I have a C# Windows Service that routes incoming emails and twitter comments to available Agents (I don't really - but the app does something very similar). So, I have a list of Agents, and logic to route these emails and tweets to available agents.
How does my code look?
I have an AgentManager class that keeps track of which agents are available, their skill levels, etc.
I have an EmailManager class that routes Emails to Agents.
I have a TwitterManager class that routes Tweets to Agents.
So - logic pertaining to Agents, such as the list of Agents, who is available, who has capacity for a Tweet, who has capacity for an Email, etc is all in the AgentManager.
Now, when EmailManager detects that there is a new email and needs to assign it to an Agent, I want
to get my list of agents from AgentManager(rather than going back to the database or keeping a separate list of Agents in EmailManager).
My initial thought is to pass AgentManager to EmailManager by reference. The reason I want to do this is so as Agents change state, are added/removed, etc - EmailManager will always be working wit
h the latest AgentManager (and therefore the latest Agent list). Somehow - this feels dirty. I know it is out of fashion to pass by ref, but it seems to be a good way to handle this. Am I doing it wrong?
EDIT:
I am passing the AgentManager by reference for storage, not to change it in the EmailManager class.
From your descriptions seems more soud to go the other way.
An AgentManager process EMails and Tweets and knows everything of its Agents -
So it should have a method that receive a list of EMails/Tweets and process them.
Because we are speaking of reference-types the question about passing by ref is a bit unclear.
For example:
AgentManager ag = new AgentManager();
EMailManager eg = new EMailManager();
TweetManaget tg = new TweetManager();
eg.LoadEMail();
List<EMails> mails = eg.GetMailsList();
tg.LoadTweet();
List<Tweets> tws = tg.GetTweetsList();
ag.ProcessData(mails, tws);
EDIT: Looking at the comment from OP I have thought of another strategy
Let the EMailManager and TweetManager declare an Event to which the AgentManager subscribe-
eg.EmailReceived += ag.NotifyEmail;
tg.TweetPolled += ag.NotifyTweet;
public class EventManager
{
public delegate void OnMailReceived(EMails m);
public event MailReceived;
........
private void GetMail()
{
EMails m;
.....
if(MailReceived != null)
MailReceived(m);
}
}
public class AgentManager()
{
public void NotifyEMail(EMails m)
{
.....
}
}

Categories

Resources