I want to make a P2P networked (thinking about using mirror) game with unity, with no dedicated server, similar to Terraria and Valheim - unity3d

I have managed to basically connect with my friend over internet, by forwarding my IP address from my router settings... this is not viable because there are few people willing to do what I did to play games with their friends. So how to actually do UDP hole punching (basically what I did manually to my router) in unity using the mirror networking solution...

A common solution to this problem is WebRTC, which takes care of the hole punching under the hood. Unity maintains this package which implements WebRTC. They also provide a great tutorial on how to use it. The nuts and bolts of it are:
using UnityEngine;
using Unity.WebRTC;
public class MyPlayerScript : MonoBehaviour
{
RTCPeerConnection localConnection, remoteConnection;
RTCDataChannel sendChannel, receiveChannel;
private void Awake()
{
// Initialize WebRTC
WebRTC.Initialize();
// Create local peer
localConnection = new RTCPeerConnection();
sendChannel = localConnection.CreateDataChannel("sendChannel");
channel.OnOpen = handleSendChannelStatusChange;
channel.OnClose = handleSendChannelStatusChange;
// Create remote peer
remoteConnection = new RTCPeerConnection();
remoteConnection.OnDataChannel = ReceiveChannelCallback;
// register comms paths
localConnection.OnIceCandidate = e => {
!string.IsNullOrEmpty(e.candidate)
|| remoteConnection.AddIceCandidate(ref e);
}
remoteConnection.OnIceCandidate = e => {
!string.IsNullOrEmpty(e.candidate)
|| localConnection.AddIceCandidate(ref e);
}
localConnection.OnIceConnectionChange = state => {
Debug.Log(state);
}
}
//handle begin
IEnumerator Call(){
var op1 = localConnection.CreateOffer();
yield return op1;
var op2 = localConnection.SetLocalDescription(ref op1.desc);
yield return op2;
var op3 = remoteConnection.SetRemoteDescription(ref op1.desc);
yield return op3;
var op4 = remoteConnection.CreateAnswer();
yield return op4;
var op5 = remoteConnection.setLocalDescription(op4.desc);
yield return op5;
var op6 = localConnection.setRemoteDescription(op4.desc);
yield return op6;
}
//handle send messages
void SendMessage(string message)
{
sendChannel.Send(message);
}
void SendBinary(byte[] bytes)
{
sendChannel.Send(bytes);
}
//handle receive messages
void ReceiveChannelCallback(RTCDataChannel channel)
{
receiveChannel = channel;
receiveChannel.OnMessage = HandleReceiveMessage;
}
void HandleReceiveMessage(byte[] bytes)
{
var message = System.Text.Encoding.UTF8.GetString(bytes);
Debug.Log(message);
}
//handle end
private void OnDestroy()
{
sendChannel.Close();
receiveChannel.Close();
localConnection.Close();
remoteConnection.Close();
WebRTC.Finalize();
}
}

I have also found a useful way to do this using mirror and the epic free relay for this. Thanks so much for the other answers, it really helped understand better what I needed to search and use!

You can use Photon Bolt or Photon Fusion to let players host a game on their local machine like minecraft, etc. Photon provides relay as well as tries to use STUN to establish direct peer to peer connection to the host via UDP. PUN2 is also a good choice, although I like Photon Bolt/Fusion better - it's less of a simple RPC framework and more programmer oriented. Also, PUN does not do any STUN direct peer connection, it will always be relayed. Photon Bolt and Fusion will first attempt a STUN direct peer connection and then fallback to relay if necessary. It's been around for years and is the best choice.
Sure you can develop a Unity game with Mirror (although without a relay built in) but it's not nearly as easy to setup and use as Photon Bolt/Fusion and they don't provide a relay. As someone mentioned, you might be able to hack something together in some way but yeah - not recommended.
Ugh, yeah don't use WebRTC for a Unity game (or probably anything other than streaming music/video like it was made for to be honest).
Unity's MLAPI is "under development" and their last API was suddenly dropped "deprecated", so I wouldn't use that.

Related

how to apply Nanomsg Protocol using Java NIO

I'm new the world of TCP / IP, Networking, Sockets etc.. during my internship I should replace the Nanomsg Library with java-only code. My supervisor has also sent me the following protocol and wants me to write a implement it: https://github.com/nanomsg/nanomsg/blob/master/rfc/sp-tcp-mapping-01.txt .
So here are some questions that still arise in my mind:
The written code should be executed once the connection has been established. do you have any suggestions how I can do this ? I have tried it but I am not sure if that will work, here is what I have done:
`public void connectionInitiation(AsynchronousSocketChannel connectReq, AsynchronousSocketChannel connectionPair) {
if(connectReq)
Future<Integer> writeBytes = connectReq.write(header);
writeBytes.get();
ByteBuffer receivedbytes = ByteBuffer.allocate(4);
Future<Integer> readBytes = connectionPair.read(receivedbytes);
header.flip();
receivedbytes.flip();
if (!header.equals(receivedbytes)) {
connectReq.close();
} else if (receivedbytes.get(6) != 0x00 || receivedbytes.get(7) != 0x00) {
connectReq.close();
} else {
System.out.println("Protocol Header is successfully verified.");
}
}`
this method comes right after the connection has been established.
He asked me to do this in a separated class, how can I do this ? ( sorry if the question is quiet dumb )
I apologize if the questions are not clear enough or if the answer is tooo obvious.... I still in the learning phase and want to make sure I grasped this in the right way.

Unity Netcode: Change Ownership doesn't work for me

I have been trying to spawn gameObject(Specifically Player gameObject) in Server using this player.GetComponent().Spawn() and then tried to change ownership. It didn't works for me. So frustrated. Can anyone help on this.
I want to spawn two different player in server and client. So I tried to spawn all the players in server only and tried to change the ownership using ownerclientId, LocalClientId. Nothing worked. What happens is that it spawns two players in both server and client screen but only server has the ownership.
Code snippet:
private void Start()
{
var playerSelected = GameObject.Find("PlayerSelected");
int selectedPlayer = playerSelected.GetComponent<PlayerSelected>().selectPlayer;
if (NetworkManager.Singleton.IsServer)
{
PlayerSpawner(selectedPlayer, OwnerClientId);
}
else
{
PlayerSpawnerOnServerRpc(selectedPlayer, OwnerClientId);
}
}
[ServerRpc(RequireOwnership = false)]
void PlayerSpawnerOnServerRpc(int selectedPlayer, ulong clientId)
{
PlayerSpawner(selectedPlayer, clientId);
}
void PlayerSpawner(int selectedPlayer, ulong clientId)
{
GameObject player;
switch(selectedPlayer)
{
case 0:
player = Instantiate(player1Prefab, spawnPositionPlayer1);
player.GetComponent<NetworkObject>().Spawn();
player.GetComponent<NetworkObject>().ChangeOwnership(clientId);
break;
case 1:
player = Instantiate(player2Prefab, spawnPositionPlayer2);
player.GetComponent<NetworkObject>().Spawn();
player.GetComponent<NetworkObject>().ChangeOwnership(clientId);
break;
}
}
The reason this doesn't work is that the server is sending two messages in quick succession.
player.GetComponent<NetworkObject>().Spawn();
player.GetComponent<NetworkObject>().ChangeOwnership(clientId);
The first tells the clients to spawn an object. The second gives ownership to the object. You cannot guarantee (even running locally) that the Spawn() has finished when the ChangeOwnership() is called. So when ChangeOwnership() is called, the client may not have finished initialising the object that's spawned.
Unity were aware of this, and provided you with SpawnWithOwnership (docs). Replace your two lines with one:
player.GetComponent<NetworkObject>().SpawnWithOwnership(clientId);

Photon matchmaking - Join or create a room using in Unity with an SQL lobby

I am trying to implement skill based matchmaking using Photon in Unity. It seems
I got most of this code from the documentation and it works but not well.
The problem is that you can't use JoinOrCreate() with the sql lobby type so my logic here is try and find a room, if it fails create one.
void init()
{
_client = Game.Context.GetComponent<SocketConnectionManager>().client;
joinRoom();
}
public void joinRoom()
{
TypedLobby sqlLobby = new TypedLobby("skillLobby", LobbyType.SqlLobby);
string sqlLobbyFilter = "C0 BETWEEN 100 AND 200";
_client.OpJoinRandomRoom(null, MatchMaker.MaxPlayers, MatchmakingMode.FillRoom, sqlLobby, sqlLobbyFilter);
}
public void createRoom()
{
RoomOptions o = new RoomOptions();
o.MaxPlayers = MatchMaker.MaxPlayers;
o.CustomRoomProperties = new Hashtable() { { "C0", Game.Me.getInt("trophies") } };
o.CustomRoomPropertiesForLobby = new string[] { "C0" }; // this makes "C0" available in the lobby
TypedLobby sqlLobby = new TypedLobby("skillLobby", LobbyType.SqlLobby);
_client.OpCreateRoom("", o, sqlLobby);
}
private void onEvent(EventData obj)
{
if (_client.CurrentRoom != null)
{
if (_client.CurrentRoom.PlayerCount >= _client.CurrentRoom.MaxPlayers)
{
// sweet I am good to go.
}
}
else
{
createRoom();
}
}
The problem is this is pretty unreliable. Say two players try to find a game at the same time they will both search fail and then both create. Now I have two players sitting in empty rooms instead of playing each other.
Any ideas on a better system?
Thanks all.
Thank you for choosing Photon!
First of all, there are few things that you should understand about Photon:
you can't use JoinOrCreate() with the sql lobby type
This is not correct.
Where did you read such thing?
Did you test this yourself? What did you test exactly?
onEvent (LoadBalancingClient.OnEventAction) callback cannot be used to be notified of a failed join random room operation. Instead, you should make use of the LoadBalancingClient.OnOpResponseAction callback, as follows:
private void OnOpResponse(OperationResponse operationResponse)
{
switch (operationResponse.Code)
{
case OperationCode.JoinRandomGame:
if (operationResponse.ReturnCode == ErrorCode.NoMatchFound)
{
createRoom();
}
break;
}
}
To detect a join event inside a room (local or remote player entered a room):
private void onEvent(EventData eventData)
{
switch (eventData.Code)
{
case EventCode.Join:
int actorNr = (int)eventData[ParameterCode.ActorNr];
PhotonPlayer originatingPlayer = this.GetPlayerWithId(actorNr);
if (originatingPlayer.IsLocal)
{
}
else
{
}
break;
}
}
To answer your question:
Say two players try to find a game at the same time they will both
search fail and then both create.
Any ideas on a better system?
No.
This issue happens only during the development phase where you use a few clients to run some tests. Once you have enough user base you won't notice this issue.

Looking for a SIP client that can use an h.264 RTSP stream as a video source

We are looking to integrate an IP camera (h.264 RTSP stream) with an asterisk PBX system for use in a school for distributed education (so a remote teacher can "dial in and teach").
Ideally we would like to be able to create a SIP client as an autoanswer pbx extension.
We are considering running a *nix box that can use a network video stream as a source for video, mix a separate audio source and present a SIP endpoint.
I understand that SIP express router may be able to:
"call an external C script, which could parse and change the SDP info within the SIP headers of clients it porxy's with, and change address of where it expects to recieve media from."
but I'm thinking it may be easier to look for a way to present an h.264 rtsp stream as /dev/videoX and use a standard SIP client.
If anyone has any pointers or any ideas for research I'd be really appreciative :-)
Thanks for reading!
W
p.s. there are IP cameras out there that claim to have SIP clients, but all I have seen only offer SIP for establishing a bi-directional audio session.
I don't know whether you found the correct answer to this question or not after all this time, but maybe I can give you some advice with VoIP and IP camera management and it would help anybody who has the same problem.
Your goal (if I understand your problem correctly) is basically creating a conference call solution which answers all the incoming calls and attach the IP camera video to all of these calls. I currently work at a company called Ozeki and - I don't know if there's an open-source solution for your problem or not - I'm going to show you an example code with SIP account registration, IP camera connection and answer all the incoming calls with the camera video.
So here is the source code:
public partial class Form1 : Form
{
private IIPCamera _camera;
private DrawingImageProvider _imageProvider;
private MediaConnector _connector;
private VideoViewerWF _videoViewerWf;
private ISoftPhone _softphone;
private IPhoneLine _phoneLine;
private IPhoneCall _call;
private PhoneCallAudioSender _audioSender;
private PhoneCallVideoSender _videoSender;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
_softphone.IncomingCall += softphone_IncomingCall;
_connector = new MediaConnector();
_videoSender = new PhoneCallVideoSender();
_audioSender = new PhoneCallAudioSender();
_imageProvider = new DrawingImageProvider();
_videoViewerWf = new VideoViewerWF();
SetVideoViewer();
}
private void SetVideoViewer()
{
CameraBox.Controls.Add(_videoViewerWf);
_videoViewerWf.Size = new Size(260, 180);
_videoViewerWf.BackColor = Color.Black;
_videoViewerWf.TabStop = false;
_videoViewerWf.FlipMode = FlipMode.None;
_videoViewerWf.Location = new Point(35, 30);
_videoViewerWf.Name = "_videoViewerWf";
}
private void Register(bool registrationRequired, string displayName, string userName, string authenticationId, string registerPassword, string domainHost)
{
try
{
var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost);
_phoneLine = _softphone.CreatePhoneLine(account);
_phoneLine.RegistrationStateChanged += phoneLine_RegistrationStateChanged;
_softphone.RegisterPhoneLine(_phoneLine);
}
catch (Exception ex)
{
label_Phoneline.Text = ex.Message;
}
}
private void phoneLine_RegistrationStateChanged(object sender, RegistrationStateChangedArgs e)
{
InvokeGuiThread(() => label_Phoneline.Text = e.State.ToString());
}
private void softphone_IncomingCall(object sender, VoIPEventArgs<iphonecall> e)
{
if (_camera != null)
{
_call = e.Item;
_call.CallStateChanged += call_CallStateChanged;
ConnectToCall();
_call.Answer();
}
}
private void ConnectToCall()
{
_videoSender.AttachToCall(_call);
_audioSender.AttachToCall(_call);
_connector.Connect(_camera.VideoChannel, _videoSender);
_connector.Connect(_camera.AudioChannel, _audioSender);
}
private void call_CallStateChanged(object sender, CallStateChangedArgs e)
{
InvokeGuiThread(() => label_Call.Text = e.State.ToString());
if (e.State == CallState.Completed)
if (_call != null)
{
_call.CallStateChanged -= call_CallStateChanged;
_connector.Disconnect(_camera.VideoChannel, _videoSender);
_connector.Disconnect(_camera.AudioChannel, _audioSender);
}
}
private void button_Connect_Click(object sender, EventArgs e)
{
_camera = IPCameraFactory.GetCamera("cameraIPAddress:8080", "admin", "admin");
_connector.Connect(_camera.VideoChannel, _imageProvider);
_videoViewerWf.SetImageProvider(_imageProvider);
_videoViewerWf.Start();
_camera.Start();
}
private void button_SIPRegister_Click(object sender, EventArgs e)
{
Register(true, "100", "100", "100", "100", "PBXAddress");
}
private void InvokeGuiThread(Action action)
{
BeginInvoke(action);
}
}
And the (example) GUI:
If you click on Connect button then the IPCameraFactory.GetCamera() will be called which is for connecting to the specified camera using the arguments (RTSP/RTP/HTTP messages in the background).
The Register button calls the Register() method to register your SIP account to the PBX. You can check the registration status at the Status label.
If there is an incoming call then the answer will occur automatically and the camera video and audio channel will attach to the call. So the caller will see and hear you.
Note:
Most of the IP camera microphones have really bad quality so you may connect your own microphone to the call. It's really simple too.
Create a Microphone object:
Microphone microphone = Microphone.GetDefaultDevice();
if (microphone != null)
microphone.Start();
And if there's an incoming call then connect this microphone to the call:
_connector.Connect(microphone, _audioSender);
And that's it. I hope I could help you!
live555 has some open-source implementations like openRTSP and playSIP which could help with your requirements.

Encode/Decode xxxclass to byte[] than send it to Remote PC with C#

Hi i have got a TCP/IP Socket project.
i can send string messages to Server with Client side and i can get responses from server.
But getting one string message and sending only one string (or any other object).I wanna Encode Personel class to Byte array after send to Clients from server side.And Decode it. than get values from my class.
//SERVER SIDE CODE Connect() starts at on form load
private void Connect()
{
// start listen socket
dinleyiciSoket = new TcpListener(System.Net.IPAddress.Any, 10048);
dinleyiciSoket.Start();
Socket istemciSoketi = dinleyiciSoket.AcceptSocket();
NetworkStream agAkisi = new NetworkStream(istemciSoketi);
BinaryReader binaryOkuyucu = new BinaryReader(agAkisi);
BinaryWriter binaryYazici = new BinaryWriter(agAkisi);
string alinanMetin = binaryOkuyucu.ReadString();
MessageBox.Show(alinanMetin, "Yeni Genelge", MessageBoxButtons.OK);
binaryYazici.Write(true);
dinleyiciSoket.Stop();
Connect();
}
////////// CLIENT SIDE //////////////
private string IpAdresi(string host)
{
string address = "";
IPAddress[] addresslist = Dns.GetHostAddresses(host);
foreach (IPAddress theaddress in addresslist)
{
if (theaddress.AddressFamily == AddressFamily.InterNetwork)
{
address = theaddress.ToString();
}
}
return address;
}
bool onay;
private void button1_Click(object sender, EventArgs e)
{
//create socket connection
Socket istemciBaglantisi = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Bağlantıyı gerçekleştir
if (istemciBaglantisi.Connected != true)
{
istemciBaglantisi.Connect(IPAddress.Parse(IpAdresi(txtHost.Text)), 10048);
}
agAkisi = new NetworkStream(istemciBaglantisi);
binaryYazici = new BinaryWriter(agAkisi);
binaryOkuyucu = new BinaryReader(agAkisi);
binaryYazici.Write(txtMesaj.Text);
onay = binaryOkuyucu.ReadBoolean();
MessageBox.Show(onay.ToString());
istemciBaglantisi.Close();
}
Take a look at object serialization. See here for examples. That should get you going in the right direction.
You can use google's protocol buffers. It is a fast and compact mechanism for serializing objects. There are two implementations on .NET: protobuf-net and protobuf.
I'd use object serialization or XmlSerialization, both available in .NET. I would not look at Google's protocol buffers, because that RPC encoding has little advantage over what's already in .NET, but it is obscure, especially in the .NET world, and especially now. I wouldn't bet on it becoming mainstream for .net devs. As a result, you will only make your code harder to maintain by using this RPC encoding.
I don't really see the need for protobufs when the apps that are interconnecting are homogeneous, and are NOT on the scale of Google's datacenters. I also don't see the need even when heterogeneity is the rule, because we already have JSON and XML. They are both readable and serviceable, where protobufs are not.
In any case .NET has what you need for this, built-in.