first commit
This commit is contained in:
commit
35c0f73129
42 changed files with 3266 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
*.meta
|
||||||
|
"Visual Scripting"/.meta
|
||||||
|
Client/.meta
|
||||||
|
Images/.meta
|
BIN
.vs/Matchmaker/FileContentIndex/84f5bfc4-1178-4eb2-9ba0-96ef4f71fbdd.vsidx
Executable file
BIN
.vs/Matchmaker/FileContentIndex/84f5bfc4-1178-4eb2-9ba0-96ef4f71fbdd.vsidx
Executable file
Binary file not shown.
0
.vs/Matchmaker/FileContentIndex/read.lock
Executable file
0
.vs/Matchmaker/FileContentIndex/read.lock
Executable file
BIN
.vs/Matchmaker/v17/.suo
Executable file
BIN
.vs/Matchmaker/v17/.suo
Executable file
Binary file not shown.
3
.vs/ProjectSettings.json
Executable file
3
.vs/ProjectSettings.json
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"CurrentProjectSetting": null
|
||||||
|
}
|
8
.vs/VSWorkspaceState.json
Executable file
8
.vs/VSWorkspaceState.json
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"ExpandedNodes": [
|
||||||
|
"",
|
||||||
|
"\\Visual Scripting"
|
||||||
|
],
|
||||||
|
"SelectedNode": "\\Visual Scripting",
|
||||||
|
"PreviewInSolutionExplorer": false
|
||||||
|
}
|
BIN
.vs/slnx.sqlite
Executable file
BIN
.vs/slnx.sqlite
Executable file
Binary file not shown.
471
Client/Client.cs
Executable file
471
Client/Client.cs
Executable file
|
@ -0,0 +1,471 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Tom Weiland
|
||||||
|
* Copyright (c) 2022 Vincent Dowling
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using Scripts.Matchmaker;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.CustomServer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stores information about the connection.
|
||||||
|
/// </summary>
|
||||||
|
public class Client : MonoBehaviour
|
||||||
|
{
|
||||||
|
public ClientSend.StatusType? lastStatusType;
|
||||||
|
public int lastReceivedInt;
|
||||||
|
public ClientHandle.ClientAttrib lastReceivedAttrib;
|
||||||
|
|
||||||
|
public bool welcomeReceived;
|
||||||
|
private const int DataBufferSize = 4096;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For editor readability.
|
||||||
|
/// </summary>
|
||||||
|
public string displayName;
|
||||||
|
|
||||||
|
public string ip = Config.Address;
|
||||||
|
public int port = 26950;
|
||||||
|
public int myId;
|
||||||
|
public Tcp tcp;
|
||||||
|
public UDP udp;
|
||||||
|
private bool isConnected;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if it is connected to a server
|
||||||
|
/// </summary>
|
||||||
|
public bool IsConnected
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (tcp.socket is { Client: { Connected: true } })
|
||||||
|
{
|
||||||
|
/* pear to the documentation on Poll:
|
||||||
|
* When passing SelectMode.SelectRead as a parameter to the Poll method it will return
|
||||||
|
* -either- true if Socket.Listen(Int32) has been called and a connection is pending;
|
||||||
|
* -or- true if data is available for reading;
|
||||||
|
* -or- true if the connection has been closed, reset, or terminated;
|
||||||
|
* otherwise, returns false
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Detect if listClient disconnected
|
||||||
|
if (tcp.socket.Client.Poll(0, SelectMode.SelectRead))
|
||||||
|
{
|
||||||
|
byte[] buff = new byte[1];
|
||||||
|
if (tcp.socket.Client.Receive(buff, SocketFlags.Peek) == 0)
|
||||||
|
{
|
||||||
|
// Client disconnected
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDestroy()
|
||||||
|
{
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class ClientPackets
|
||||||
|
{
|
||||||
|
public delegate void PacketHandler(Client server, Packet packet);
|
||||||
|
|
||||||
|
// ReSharper disable once FieldCanBeMadeReadOnly.Global
|
||||||
|
public Dictionary<int, PacketHandler> packetHandlers;
|
||||||
|
|
||||||
|
public ClientPackets(Dictionary<int, PacketHandler> packetHandlers)
|
||||||
|
{
|
||||||
|
this.packetHandlers = packetHandlers;
|
||||||
|
packetHandlers.Add(int.MaxValue, ClientHandle.Welcome);
|
||||||
|
packetHandlers.Add(int.MaxValue - 1, ClientHandle.ServerDisconnect);
|
||||||
|
packetHandlers.Add(int.MaxValue - 2, ClientHandle.GetClientAttributesReceived);
|
||||||
|
packetHandlers.Add(int.MaxValue - 3, ClientHandle.StatusReceived);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClientPackets packets = new(new Dictionary<int, ClientPackets.PacketHandler>());
|
||||||
|
|
||||||
|
private void OnApplicationQuit()
|
||||||
|
{
|
||||||
|
Disconnect(); // Disconnect when the game is closed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Attempts to connect to the server.</summary>
|
||||||
|
public bool ConnectToServer(ClientPackets packetList)
|
||||||
|
{
|
||||||
|
welcomeReceived = false;
|
||||||
|
Debug.Log($"Attempting connection to server with ip={ip} port={port}");
|
||||||
|
this.packets = packetList;
|
||||||
|
tcp = new Tcp(this);
|
||||||
|
udp = new UDP(this);
|
||||||
|
|
||||||
|
packetList.packetHandlers.TryAdd(int.MaxValue, ClientHandle.Welcome);
|
||||||
|
packetList.packetHandlers.TryAdd(int.MaxValue - 1, ClientHandle.ServerDisconnect);
|
||||||
|
packetList.packetHandlers.TryAdd(int.MaxValue - 2, ClientHandle.GetClientAttributesReceived);
|
||||||
|
packetList.packetHandlers.TryAdd(int.MaxValue - 3, ClientHandle.StatusReceived);
|
||||||
|
|
||||||
|
InitializeClientData();
|
||||||
|
|
||||||
|
isConnected = true;
|
||||||
|
tcp.Connect(); // MatchmakerNode_Connect tcp, udp gets connected once tcp is done
|
||||||
|
|
||||||
|
// Wait for a response from the server
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
while (!welcomeReceived &&
|
||||||
|
((DateTime.Now.Ticks - startTime) < ClientSend.timeOutTime))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// If timed out
|
||||||
|
if ((DateTime.Now.Ticks - startTime) >= ClientSend.timeOutTime)
|
||||||
|
{
|
||||||
|
Debug.LogError($"ConnectToServer: Request timed out!.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subclass to handle TCP networking
|
||||||
|
/// </summary>
|
||||||
|
public class Tcp
|
||||||
|
{
|
||||||
|
public TcpClient socket;
|
||||||
|
private readonly Client instance;
|
||||||
|
|
||||||
|
private NetworkStream stream;
|
||||||
|
private Packet receivedData;
|
||||||
|
private byte[] receiveBuffer;
|
||||||
|
|
||||||
|
public Tcp(Client inst)
|
||||||
|
{
|
||||||
|
instance = inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Attempts to connect to the server via TCP.</summary>
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
socket = new TcpClient
|
||||||
|
{
|
||||||
|
ReceiveBufferSize = DataBufferSize,
|
||||||
|
SendBufferSize = DataBufferSize
|
||||||
|
};
|
||||||
|
|
||||||
|
receiveBuffer = new byte[DataBufferSize];
|
||||||
|
socket.BeginConnect(instance.ip, instance.port, ConnectCallback, socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Initializes the newly connected listClient's TCP-related info.</summary>
|
||||||
|
private void ConnectCallback(IAsyncResult result)
|
||||||
|
{
|
||||||
|
socket.EndConnect(result);
|
||||||
|
|
||||||
|
if (!socket.Connected)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = socket.GetStream();
|
||||||
|
|
||||||
|
receivedData = new Packet();
|
||||||
|
|
||||||
|
stream.BeginRead(receiveBuffer, 0, DataBufferSize, ReceiveCallback, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sends data to the listClient via TCP.</summary>
|
||||||
|
/// <param name="packet">The packet to send.</param>
|
||||||
|
public void SendData(Packet packet)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (socket != null)
|
||||||
|
{
|
||||||
|
stream.BeginWrite(packet.ToArray(), 0, packet.Length(), null, null); // Send data to server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.Log($"Error sending data to server via TCP: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads incoming data from the stream.</summary>
|
||||||
|
private void ReceiveCallback(IAsyncResult result)
|
||||||
|
{
|
||||||
|
Debug.Log("Received TCP data. Preparing to handle...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (instance.IsConnected)
|
||||||
|
{
|
||||||
|
int byteLength = stream.EndRead(result);
|
||||||
|
if (byteLength <= 0)
|
||||||
|
{
|
||||||
|
instance.Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = new byte[byteLength];
|
||||||
|
Array.Copy(receiveBuffer, data, byteLength);
|
||||||
|
|
||||||
|
receivedData.Reset(HandleData(data)); // Reset receivedData if all data was handled
|
||||||
|
stream.BeginRead(receiveBuffer, 0, DataBufferSize, ReceiveCallback, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in TCP Receive Callback: {ex}");
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Prepares received data to be used by the appropriate packet handler methods.</summary>
|
||||||
|
/// <param name="data">The received data.</param>
|
||||||
|
private bool HandleData(byte[] data)
|
||||||
|
{
|
||||||
|
Debug.Log("Now handling data (TCP)...");
|
||||||
|
int packetLength = 0;
|
||||||
|
|
||||||
|
receivedData.SetBytes(data);
|
||||||
|
|
||||||
|
if (receivedData.UnreadLength() >= 4)
|
||||||
|
{
|
||||||
|
// If listClient's received data contains a packet
|
||||||
|
packetLength = receivedData.ReadInt();
|
||||||
|
if (packetLength <= 0)
|
||||||
|
{
|
||||||
|
// If packet contains no data
|
||||||
|
return true; // Reset receivedData instance to allow it to be reused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (packetLength > 0 && packetLength <= receivedData.UnreadLength())
|
||||||
|
{
|
||||||
|
if (socket != null)
|
||||||
|
{
|
||||||
|
// While packet contains data AND packet data length doesn't exceed the length of the packet we're reading
|
||||||
|
var packetBytes = receivedData.ReadBytes(packetLength);
|
||||||
|
// ThreadManager.Start();
|
||||||
|
// ThreadManager.ExecuteOnMainThread(() =>
|
||||||
|
// {
|
||||||
|
using var packet = new Packet(packetBytes);
|
||||||
|
int packetId = packet.ReadInt();
|
||||||
|
Debug.Log($"Received TCP Packet number {packetId}");
|
||||||
|
instance.packets.packetHandlers
|
||||||
|
[packetId](instance, packet); // Call appropriate method to handle the packet
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
packetLength = 0; // Reset packet length
|
||||||
|
if (receivedData.UnreadLength() >= 4)
|
||||||
|
{
|
||||||
|
// If listClient's received data contains another packet
|
||||||
|
packetLength = receivedData.ReadInt();
|
||||||
|
if (packetLength <= 0)
|
||||||
|
{
|
||||||
|
// If packet contains no data
|
||||||
|
return true; // Reset receivedData instance to allow it to be reused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetLength <= 1)
|
||||||
|
{
|
||||||
|
return true; // Reset receivedData instance to allow it to be reused
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Disconnects from the server and cleans up the TCP connection.</summary>
|
||||||
|
private void Disconnect()
|
||||||
|
{
|
||||||
|
instance.Disconnect();
|
||||||
|
|
||||||
|
stream = null;
|
||||||
|
receivedData = null;
|
||||||
|
receiveBuffer = null;
|
||||||
|
socket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subclass to handle UDP networking
|
||||||
|
/// </summary>
|
||||||
|
public class UDP
|
||||||
|
{
|
||||||
|
public UdpClient socket;
|
||||||
|
private IPEndPoint endPoint;
|
||||||
|
private readonly Client instance;
|
||||||
|
|
||||||
|
public UDP(Client inst)
|
||||||
|
{
|
||||||
|
instance = inst;
|
||||||
|
endPoint = new IPEndPoint(IPAddress.Parse(instance.ip), instance.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Attempts to connect to the server via UDP.</summary>
|
||||||
|
/// <param name="localPort">The port number to bind the UDP socket to.</param>
|
||||||
|
public void Connect(int localPort)
|
||||||
|
{
|
||||||
|
socket = new UdpClient(localPort);
|
||||||
|
|
||||||
|
socket.Connect(endPoint);
|
||||||
|
socket.BeginReceive(ReceiveCallback, null);
|
||||||
|
|
||||||
|
using Packet packet = new Packet();
|
||||||
|
SendData(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sends data to the listClient via UDP.</summary>
|
||||||
|
/// <param name="packet">The packet to send.</param>
|
||||||
|
public void SendData(Packet packet)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
packet.InsertInt(instance.myId); // Insert the listClient's ID at the start of the packet
|
||||||
|
if (socket != null)
|
||||||
|
{
|
||||||
|
socket.BeginSend(packet.ToArray(), packet.Length(), null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.Log($"Error sending data to server via UDP: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Receives incoming UDP data.</summary>
|
||||||
|
private void ReceiveCallback(IAsyncResult result)
|
||||||
|
{
|
||||||
|
Debug.Log("Received UDP data. Preparing to handle...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (instance.IsConnected)
|
||||||
|
{
|
||||||
|
byte[] data = socket.EndReceive(result, ref endPoint);
|
||||||
|
socket.BeginReceive(ReceiveCallback, null);
|
||||||
|
|
||||||
|
if (data.Length < 4)
|
||||||
|
{
|
||||||
|
instance.Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HandleData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in UDP Receive Callback: {ex}");
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Prepares received data to be used by the appropriate packet handler methods.</summary>
|
||||||
|
/// <param name="data">The received data.</param>
|
||||||
|
private void HandleData(byte[] data)
|
||||||
|
{
|
||||||
|
Debug.Log("Now handling data (UDP)...");
|
||||||
|
using var packet = new Packet(data);
|
||||||
|
|
||||||
|
var packetLength = packet.ReadInt();
|
||||||
|
data = packet.ReadBytes(packetLength);
|
||||||
|
|
||||||
|
|
||||||
|
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||||
|
|
||||||
|
// ThreadManager.Start();
|
||||||
|
// ThreadManager.ExecuteOnMainThread(() =>
|
||||||
|
// {
|
||||||
|
using var packet2 = new Packet(data);
|
||||||
|
var packetId = packet2.ReadInt();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
instance.packets.packetHandlers
|
||||||
|
[packetId](instance, packet2); // Call appropriate method to handle the packet
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in UDP packet handler {packetId}: {e}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Disconnects from the server and cleans up the UDP connection.</summary>
|
||||||
|
private void Disconnect()
|
||||||
|
{
|
||||||
|
instance.Disconnect();
|
||||||
|
|
||||||
|
endPoint = null;
|
||||||
|
socket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Initializes all necessary listClient data.</summary>
|
||||||
|
private void InitializeClientData()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Disconnects from the server and stops all network traffic.</summary>
|
||||||
|
public void Disconnect()
|
||||||
|
{
|
||||||
|
if (!isConnected) return;
|
||||||
|
isConnected = false;
|
||||||
|
tcp.socket.Close();
|
||||||
|
|
||||||
|
udp?.socket?.Close();
|
||||||
|
|
||||||
|
Debug.Log("Disconnected from server.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
Client/ClientHandle.cs
Executable file
122
Client/ClientHandle.cs
Executable file
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Tom Weiland
|
||||||
|
* Copyright (c) 2022 Vincent Dowling
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using Scripts.Matchmaker;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.CustomServer
|
||||||
|
{
|
||||||
|
public static class ClientHandle
|
||||||
|
{
|
||||||
|
public class ClientAttrib
|
||||||
|
{
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
public int id;
|
||||||
|
public readonly string name;
|
||||||
|
public readonly string value;
|
||||||
|
|
||||||
|
public ClientAttrib(int id, string name, string value)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClientAttrib lastReceivedAttrib;
|
||||||
|
|
||||||
|
public static void GetClientAttributesReceived(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
var requestedId = packet.ReadInt();
|
||||||
|
var name = packet.ReadString();
|
||||||
|
var value = packet.ReadString();
|
||||||
|
|
||||||
|
Debug.Log("Received object attributes (Obj. ID: " + requestedId.ToString() + "): '" + name + "', '" +
|
||||||
|
value + "'.");
|
||||||
|
|
||||||
|
lastReceivedAttrib = new ClientAttrib(requestedId, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Welcome(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
var msg = packet.ReadString();
|
||||||
|
var myId = packet.ReadInt();
|
||||||
|
|
||||||
|
var serverAPIVersion = packet.ReadString();
|
||||||
|
var serverId = packet.ReadString();
|
||||||
|
var gameVersion = packet.ReadString();
|
||||||
|
|
||||||
|
// Version check
|
||||||
|
if (serverAPIVersion != Config.MatchmakerAPIVersion)
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
$"Client API version mismatch! Server API version is {serverAPIVersion} while Client API version is {Config.MatchmakerAPIVersion}.");
|
||||||
|
client.Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverId != Config.GameId)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Client-Server Game ID Mismatch! Server ID is {serverId} while Client Game ID is {Config.GameId}.");
|
||||||
|
client.Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version check
|
||||||
|
if (gameVersion != Config.GameVersion)
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
$"Client game version mismatch! Server game version is {gameVersion} while Client game version is {Config.GameVersion}.");
|
||||||
|
client.Disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.welcomeReceived = true;
|
||||||
|
|
||||||
|
Debug.Log($"Message from server: {msg}");
|
||||||
|
client.myId = myId;
|
||||||
|
ClientSend.WelcomeReceived(client);
|
||||||
|
|
||||||
|
client.udp.Connect(((IPEndPoint)client.tcp.socket.Client.LocalEndPoint).Port);
|
||||||
|
client.welcomeReceived = true;
|
||||||
|
//MatchmakerBehaviour.APIClientPackets.RequestAllLobbyIds(listClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ServerDisconnect(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
client.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StatusReceived(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
var type = packet.ReadInt();
|
||||||
|
Debug.Log($"Status received: {(ClientSend.StatusType)type}");
|
||||||
|
client.lastStatusType = (ClientSend.StatusType)type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
214
Client/ClientSend.cs
Executable file
214
Client/ClientSend.cs
Executable file
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Tom Weiland
|
||||||
|
* Copyright (c) 2022 Vincent Dowling
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using Scripts.Matchmaker;
|
||||||
|
using DateTime = System.DateTime;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.CustomServer
|
||||||
|
{
|
||||||
|
public static class ClientSend
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public enum StatusType
|
||||||
|
{
|
||||||
|
Ok,
|
||||||
|
Received,
|
||||||
|
Fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once FieldCanBeMadeReadOnly.Global
|
||||||
|
// ReSharper disable once ConvertToConstant.Global
|
||||||
|
public static float timeOutTime = 30000000;
|
||||||
|
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
public static void SendTcpDataNoSync(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
packet.WriteLength();
|
||||||
|
client.tcp.SendData(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
public static void SendUdpDataNoSync(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
packet.WriteLength();
|
||||||
|
client.udp.SendData(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SendTcpData(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
if (client.IsConnected)
|
||||||
|
{
|
||||||
|
// Send the data
|
||||||
|
packet.WriteLength();
|
||||||
|
client.tcp.SendData(packet);
|
||||||
|
if (Config.Sync)
|
||||||
|
{
|
||||||
|
// Wait for a response from the server
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
client.lastStatusType = null;
|
||||||
|
|
||||||
|
while (client.lastStatusType == null &&
|
||||||
|
((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// If timed out
|
||||||
|
if ((DateTime.Now.Ticks - startTime) < timeOutTime)
|
||||||
|
{
|
||||||
|
Debug.LogError($"SendTCPData: Request timed out!.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return client.lastStatusType is StatusType.Ok or StatusType.Received;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Attempted to send TCP data through a disconnected Client!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
public static bool SendUdpData(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
if (client.IsConnected)
|
||||||
|
{
|
||||||
|
// Send the data
|
||||||
|
packet.WriteLength();
|
||||||
|
client.udp.SendData(packet);
|
||||||
|
if (Config.Sync)
|
||||||
|
{
|
||||||
|
// Wait for a response from the server
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
client.lastStatusType = null;
|
||||||
|
|
||||||
|
while (client.lastStatusType == null &&
|
||||||
|
((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// If timed out
|
||||||
|
if ((DateTime.Now.Ticks - startTime) < timeOutTime)
|
||||||
|
{
|
||||||
|
Debug.LogError($"SendTCPData: Request timed out!.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return client.lastStatusType is StatusType.Ok or StatusType.Received;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// If status received
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Attempted to send UDP data through a disconnected Client!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Built-in Packets
|
||||||
|
|
||||||
|
public static void WelcomeReceived(Client client)
|
||||||
|
{
|
||||||
|
Debug.Log("Sending 'Welcome Received' message...");
|
||||||
|
using var packet = new Packet(int.MaxValue);
|
||||||
|
|
||||||
|
packet.Write(client.myId);
|
||||||
|
packet.Write(Config.MatchmakerAPIVersion);
|
||||||
|
packet.Write(Config.GameVersion);
|
||||||
|
packet.Write(Config.GameId);
|
||||||
|
|
||||||
|
SendTcpData(client, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SetClientAttribute(Client client, String name, String value)
|
||||||
|
{
|
||||||
|
Debug.Log("Sending request to change a Client Attribute...");
|
||||||
|
using var packet = new Packet(int.MaxValue - 1);
|
||||||
|
packet.Write(client.myId);
|
||||||
|
packet.Write(name);
|
||||||
|
packet.Write(value);
|
||||||
|
|
||||||
|
if (ClientSend.SendTcpData(client, packet))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClientHandle.ClientAttrib GetClientAttribute(Client client, int id, String name)
|
||||||
|
{
|
||||||
|
Debug.Log("Sending request to be sent a Client Attribute...");
|
||||||
|
using var packet = new Packet(int.MaxValue - 2);
|
||||||
|
packet.Write(client.myId);
|
||||||
|
packet.Write(id);
|
||||||
|
packet.Write(name);
|
||||||
|
|
||||||
|
ClientHandle.lastReceivedAttrib = null;
|
||||||
|
ClientSend.SendTcpData(client, packet);
|
||||||
|
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
while (ClientHandle.lastReceivedAttrib == null && ((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
Debug.LogError("GetClientAttribute: Request timed out!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClientHandle.lastReceivedAttrib;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Respond to the listClient with a status
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client">What Client is sending it</param>
|
||||||
|
/// <param name="status">What status</param>
|
||||||
|
public static void Status(Client client, StatusType status)
|
||||||
|
{
|
||||||
|
using var packet = new Packet(int.MaxValue - 3);
|
||||||
|
packet.Write((int)status);
|
||||||
|
|
||||||
|
SendTcpDataNoSync(client, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
403
Client/Packet.cs
Executable file
403
Client/Packet.cs
Executable file
|
@ -0,0 +1,403 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Tom Weiland
|
||||||
|
* Copyright (c) 2022 Vincent Dowling
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.CustomServer
|
||||||
|
{
|
||||||
|
|
||||||
|
public sealed class Packet : IDisposable
|
||||||
|
{
|
||||||
|
private List<byte> buffer;
|
||||||
|
private byte[] readableBuffer;
|
||||||
|
private int readPos;
|
||||||
|
|
||||||
|
/// <summary>Creates a new empty packet (without an ID).</summary>
|
||||||
|
public Packet()
|
||||||
|
{
|
||||||
|
buffer = new List<byte>(); // Initialize buffer
|
||||||
|
readPos = 0; // Set readPos to 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a new packet with a given ID. Used for sending.</summary>
|
||||||
|
/// <param name="id">The packet ID.</param>
|
||||||
|
public Packet(int id)
|
||||||
|
{
|
||||||
|
buffer = new List<byte>(); // Initialize buffer
|
||||||
|
readPos = 0; // Set readPos to 0
|
||||||
|
|
||||||
|
Write(id); // Write packet id to the buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Creates a packet from which data can be read. Used for receiving.</summary>
|
||||||
|
/// <param name="data">The bytes to add to the packet.</param>
|
||||||
|
public Packet(byte[] data)
|
||||||
|
{
|
||||||
|
buffer = new List<byte>(); // Initialize buffer
|
||||||
|
readPos = 0; // Set readPos to 0
|
||||||
|
|
||||||
|
SetBytes(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Functions
|
||||||
|
/// <summary>Sets the packet's content and prepares it to be read.</summary>
|
||||||
|
/// <param name="data">The bytes to add to the packet.</param>
|
||||||
|
public void SetBytes(byte[] data)
|
||||||
|
{
|
||||||
|
Write(data);
|
||||||
|
readableBuffer = buffer.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Inserts the length of the packet's content at the start of the buffer.</summary>
|
||||||
|
public void WriteLength()
|
||||||
|
{
|
||||||
|
buffer.InsertRange(0, BitConverter.GetBytes(buffer.Count)); // Insert the byte length of the packet at the very beginning
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Inserts the given int at the start of the buffer.</summary>
|
||||||
|
/// <param name="value">The int to insert.</param>
|
||||||
|
public void InsertInt(int value)
|
||||||
|
{
|
||||||
|
buffer.InsertRange(0, BitConverter.GetBytes(value)); // Insert the int at the start of the buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the packet's content in array form.</summary>
|
||||||
|
public byte[] ToArray()
|
||||||
|
{
|
||||||
|
readableBuffer = buffer.ToArray();
|
||||||
|
return readableBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the length of the packet's content.</summary>
|
||||||
|
public int Length()
|
||||||
|
{
|
||||||
|
return buffer.Count; // Return the length of buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the length of the unread data contained in the packet.</summary>
|
||||||
|
public int UnreadLength()
|
||||||
|
{
|
||||||
|
return Length() - readPos; // Return the remaining length (unread)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Resets the packet instance to allow it to be reused.</summary>
|
||||||
|
/// <param name="shouldReset">Whether or not to reset the packet.</param>
|
||||||
|
public void Reset(bool shouldReset = true)
|
||||||
|
{
|
||||||
|
if (shouldReset)
|
||||||
|
{
|
||||||
|
buffer.Clear(); // Clear buffer
|
||||||
|
readableBuffer = null;
|
||||||
|
readPos = 0; // Reset readPos
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
readPos -= 4; // "Unread" the last read int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Write Data
|
||||||
|
/// <summary>Adds a byte to the packet.</summary>
|
||||||
|
/// <param name="value">The byte to add.</param>
|
||||||
|
public void Write(byte value)
|
||||||
|
{
|
||||||
|
buffer.Add(value);
|
||||||
|
}
|
||||||
|
/// <summary>Adds an array of bytes to the packet.</summary>
|
||||||
|
/// <param name="value">The byte array to add.</param>
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
public void Write(IEnumerable<byte> value)
|
||||||
|
{
|
||||||
|
buffer.AddRange(value);
|
||||||
|
}
|
||||||
|
/// <summary>Adds a short to the packet.</summary>
|
||||||
|
/// <param name="value">The short to add.</param>
|
||||||
|
public void Write(short value)
|
||||||
|
{
|
||||||
|
buffer.AddRange(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
/// <summary>Adds an int to the packet.</summary>
|
||||||
|
/// <param name="value">The int to add.</param>
|
||||||
|
public void Write(int value)
|
||||||
|
{
|
||||||
|
buffer.AddRange(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
/// <summary>Adds a long to the packet.</summary>
|
||||||
|
/// <param name="value">The long to add.</param>
|
||||||
|
public void Write(long value)
|
||||||
|
{
|
||||||
|
buffer.AddRange(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
/// <summary>Adds a float to the packet.</summary>
|
||||||
|
/// <param name="value">The float to add.</param>
|
||||||
|
public void Write(float value)
|
||||||
|
{
|
||||||
|
buffer.AddRange(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
/// <summary>Adds a bool to the packet.</summary>
|
||||||
|
/// <param name="value">The bool to add.</param>
|
||||||
|
public void Write(bool value)
|
||||||
|
{
|
||||||
|
buffer.AddRange(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
/// <summary>Adds a string to the packet.</summary>
|
||||||
|
/// <param name="value">The string to add.</param>
|
||||||
|
public void Write(string value)
|
||||||
|
{
|
||||||
|
Write(value.Length); // Add the length of the string to the packet
|
||||||
|
buffer.AddRange(Encoding.ASCII.GetBytes(value)); // Add the string itself
|
||||||
|
}
|
||||||
|
/// <summary>Adds a Vector3 to the packet.</summary>
|
||||||
|
/// <param name="value">The Vector3 to add.</param>
|
||||||
|
public void Write(Vector3 value)
|
||||||
|
{
|
||||||
|
Write(value.x);
|
||||||
|
Write(value.y);
|
||||||
|
Write(value.z);
|
||||||
|
}
|
||||||
|
/// <summary>Adds a Quaternion to the packet.</summary>
|
||||||
|
/// <param name="value">The Quaternion to add.</param>
|
||||||
|
public void Write(Quaternion value)
|
||||||
|
{
|
||||||
|
Write(value.x);
|
||||||
|
Write(value.y);
|
||||||
|
Write(value.z);
|
||||||
|
Write(value.w);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Read Data
|
||||||
|
/// <summary>Reads a byte from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public byte ReadByte(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
if (buffer.Count > readPos)
|
||||||
|
{
|
||||||
|
// If there are unread bytes
|
||||||
|
var value = readableBuffer[readPos]; // Get the byte at readPos' position
|
||||||
|
if (moveReadPos)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true
|
||||||
|
readPos += 1; // Increase readPos by 1
|
||||||
|
}
|
||||||
|
return value; // Return the byte
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'byte'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads an array of bytes from the packet.</summary>
|
||||||
|
/// <param name="length">The length of the byte array.</param>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public byte[] ReadBytes(int length, bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
if (buffer.Count > readPos)
|
||||||
|
{
|
||||||
|
// If there are unread bytes
|
||||||
|
var value = buffer.GetRange(readPos, length).ToArray(); // Get the bytes at readPos' position with a range of _length
|
||||||
|
if (moveReadPos)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true
|
||||||
|
readPos += length; // Increase readPos by _length
|
||||||
|
}
|
||||||
|
return value; // Return the bytes
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'byte[]'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a short from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public short ReadShort(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
if (buffer.Count > readPos)
|
||||||
|
{
|
||||||
|
// If there are unread bytes
|
||||||
|
var value = BitConverter.ToInt16(readableBuffer, readPos); // Convert the bytes to a short
|
||||||
|
if (moveReadPos)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true and there are unread bytes
|
||||||
|
readPos += 2; // Increase readPos by 2
|
||||||
|
}
|
||||||
|
return value; // Return the short
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'short'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads an int from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public int ReadInt(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
if (buffer.Count > readPos)
|
||||||
|
{
|
||||||
|
// If there are unread bytes
|
||||||
|
var value = BitConverter.ToInt32(readableBuffer, readPos); // Convert the bytes to an int
|
||||||
|
if (moveReadPos)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true
|
||||||
|
readPos += 4; // Increase readPos by 4
|
||||||
|
}
|
||||||
|
return value; // Return the int
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'int'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a long from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public long ReadLong(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
if (buffer.Count > readPos)
|
||||||
|
{
|
||||||
|
// If there are unread bytes
|
||||||
|
var value = BitConverter.ToInt64(readableBuffer, readPos); // Convert the bytes to a long
|
||||||
|
if (moveReadPos)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true
|
||||||
|
readPos += 8; // Increase readPos by 8
|
||||||
|
}
|
||||||
|
return value; // Return the long
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'long'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a float from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public float ReadFloat(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
if (buffer.Count > readPos)
|
||||||
|
{
|
||||||
|
// If there are unread bytes
|
||||||
|
var value = BitConverter.ToSingle(readableBuffer, readPos); // Convert the bytes to a float
|
||||||
|
if (moveReadPos)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true
|
||||||
|
readPos += 4; // Increase readPos by 4
|
||||||
|
}
|
||||||
|
return value; // Return the float
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'float'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a bool from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public bool ReadBool(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
if (buffer.Count > readPos)
|
||||||
|
{
|
||||||
|
// If there are unread bytes
|
||||||
|
var value = BitConverter.ToBoolean(readableBuffer, readPos); // Convert the bytes to a bool
|
||||||
|
if (moveReadPos)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true
|
||||||
|
readPos += 1; // Increase readPos by 1
|
||||||
|
}
|
||||||
|
return value; // Return the bool
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'bool'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a string from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public string ReadString(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var length = ReadInt(); // Get the length of the string
|
||||||
|
var value = Encoding.ASCII.GetString(readableBuffer, readPos, length); // Convert the bytes to a string
|
||||||
|
if (moveReadPos && value.Length > 0)
|
||||||
|
{
|
||||||
|
// If _moveReadPos is true string is not empty
|
||||||
|
readPos += length; // Increase readPos by the length of the string
|
||||||
|
}
|
||||||
|
return value; // Return the string
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw new Exception("Could not read value of type 'string'!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a Vector3 from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public Vector3 ReadVector3(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
return new Vector3(ReadFloat(moveReadPos), ReadFloat(moveReadPos), ReadFloat(moveReadPos));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a Quaternion from the packet.</summary>
|
||||||
|
/// <param name="moveReadPos">Whether or not to move the buffer's read position.</param>
|
||||||
|
public Quaternion ReadQuaternion(bool moveReadPos = true)
|
||||||
|
{
|
||||||
|
return new Quaternion(ReadFloat(moveReadPos), ReadFloat(moveReadPos), ReadFloat(moveReadPos), ReadFloat(moveReadPos));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private bool disposed;
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposed) return;
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
buffer = null;
|
||||||
|
readableBuffer = null;
|
||||||
|
readPos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
// ReSharper disable once GCSuppressFinalizeForTypeWithoutDestructor
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
Client/ThreadManager.cs
Executable file
119
Client/ThreadManager.cs
Executable file
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Tom Weiland
|
||||||
|
* Copyright (c) 2022 Vincent Dowling
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.CustomServer
|
||||||
|
{
|
||||||
|
public static class ThreadManager
|
||||||
|
{
|
||||||
|
private static readonly List<Action> ActionsForMainThread = new();
|
||||||
|
private static readonly List<Action> ExecuteCopiedOnMainThread = new();
|
||||||
|
private static bool _actionToExecuteOnMainThread;
|
||||||
|
|
||||||
|
private const int TicksPerSec = 40;
|
||||||
|
private const float MsPerTick = 1000f / TicksPerSec;
|
||||||
|
private static bool _isRunning;
|
||||||
|
|
||||||
|
private static readonly Thread MainThreadObject = new(MainThread);
|
||||||
|
|
||||||
|
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
if (_isRunning) return;
|
||||||
|
_isRunning = true;
|
||||||
|
MainThreadObject.Start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Stop()
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Sets an action to be executed on the main thread.</summary>
|
||||||
|
/// <param name="action">The action to be executed on the main thread.</param>
|
||||||
|
public static void ExecuteOnMainThread(Action action)
|
||||||
|
{
|
||||||
|
if (action == null)
|
||||||
|
{
|
||||||
|
Debug.Log("No action to execute on main thread!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (ActionsForMainThread)
|
||||||
|
{
|
||||||
|
ActionsForMainThread.Add(action);
|
||||||
|
_actionToExecuteOnMainThread = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>Executes all code meant to run on the main thread. NOTE: Call this ONLY from the main thread.</summary>
|
||||||
|
public static void UpdateMain()
|
||||||
|
{
|
||||||
|
if (!_actionToExecuteOnMainThread) return;
|
||||||
|
ExecuteCopiedOnMainThread.Clear();
|
||||||
|
lock (ActionsForMainThread)
|
||||||
|
{
|
||||||
|
ExecuteCopiedOnMainThread.AddRange(ActionsForMainThread);
|
||||||
|
ActionsForMainThread.Clear();
|
||||||
|
_actionToExecuteOnMainThread = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var t in ExecuteCopiedOnMainThread)
|
||||||
|
{
|
||||||
|
t();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void MainThread()
|
||||||
|
{
|
||||||
|
Debug.Log($"Main thread started. Running at {TicksPerSec} ticks per second.");
|
||||||
|
var nextLoop = DateTime.Now;
|
||||||
|
|
||||||
|
while (_isRunning)
|
||||||
|
{
|
||||||
|
while (nextLoop < DateTime.Now)
|
||||||
|
{
|
||||||
|
UpdateMain();
|
||||||
|
|
||||||
|
nextLoop = nextLoop.AddMilliseconds(MsPerTick);
|
||||||
|
|
||||||
|
if (nextLoop > DateTime.Now)
|
||||||
|
{
|
||||||
|
Thread.Sleep(nextLoop - DateTime.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
Config.cs
Executable file
36
Config.cs
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
namespace Scripts.Matchmaker
|
||||||
|
{
|
||||||
|
public static class Config
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This allows you to name the game so your game cannot connect to servers it is not supposed to.
|
||||||
|
/// </summary>
|
||||||
|
public const string GameId = "My Game Name";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The version of the matchmaker APII. This must match here and on the server for a connection.
|
||||||
|
/// </summary>
|
||||||
|
public const string MatchmakerAPIVersion = "1.1.6";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The version of the game itself. Must match on the server for a connection.
|
||||||
|
/// Name is also valid, as it is a string.
|
||||||
|
/// </summary>
|
||||||
|
public const string GameVersion = "0.0.1";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What port the server will be on (0-65535). The lobby server will use a port two (2) above this.
|
||||||
|
/// </summary>
|
||||||
|
public const ushort Port = 26950;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What address the server is at.
|
||||||
|
/// </summary>
|
||||||
|
public const string Address = "192.168.1.204";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes it so it waits for a response from the server every time it sends data. Slow, advise use for debugging.
|
||||||
|
/// </summary>
|
||||||
|
public const bool Sync = false;
|
||||||
|
}
|
||||||
|
}
|
93
IMGUIMenuDemo.cs
Executable file
93
IMGUIMenuDemo.cs
Executable file
|
@ -0,0 +1,93 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Mirror;
|
||||||
|
using Scripts.CustomServer;
|
||||||
|
using Scripts.Matchmaker;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(Client))]
|
||||||
|
public class IMGUIMenuDemo : MonoBehaviour, IMatchmaker
|
||||||
|
{
|
||||||
|
private List<string> ServerList = new();
|
||||||
|
|
||||||
|
public static bool CreatePublic;
|
||||||
|
|
||||||
|
private string connectText;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
IMatchmaker.listClient = gameObject.GetComponent<Client>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Refresh()
|
||||||
|
{
|
||||||
|
IMatchmaker.Connect();
|
||||||
|
/* var x = IMatchmaker.GetAllLobbyIdsByAttribute("public", "True");
|
||||||
|
foreach (var y in x)
|
||||||
|
{
|
||||||
|
ServerList.Add(IMatchmaker.GetLobbyAttribute(y, "_auto_ip"));
|
||||||
|
}*/
|
||||||
|
// IMatchmaker.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
GUI.Box(new Rect(10, 10, 200, 500), "Connection Manager");
|
||||||
|
|
||||||
|
connectText = GUI.TextField(new Rect(30, 30, 160, 20), connectText);
|
||||||
|
|
||||||
|
if (GUI.Button(new Rect(30, 60, 160, 20), "Connect"))
|
||||||
|
{
|
||||||
|
// Check if it is an IP
|
||||||
|
NetworkManager.singleton.networkAddress = connectText;
|
||||||
|
NetworkManager.singleton.StartClient();
|
||||||
|
|
||||||
|
// Otherwise
|
||||||
|
IMatchmaker.Connect();
|
||||||
|
|
||||||
|
var uuidLobby = IMatchmaker.GetLobbyIdByUuid(connectText);
|
||||||
|
var address = "";
|
||||||
|
|
||||||
|
if (uuidLobby > 0)
|
||||||
|
{
|
||||||
|
NetworkManager.singleton.networkAddress = IMatchmaker.GetLobbyAttribute(uuidLobby, "_auto_uuid");
|
||||||
|
IMatchmaker.Disconnect();
|
||||||
|
NetworkManager.singleton.StartClient();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IMatchmaker.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUI.Button(new Rect(30, 100, 160, 20), "Start Host (Public)"))
|
||||||
|
{
|
||||||
|
CreatePublic = true;
|
||||||
|
NetworkManager.singleton.StartHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUI.Button(new Rect(30, 130, 160, 20), "Start Host (Private)"))
|
||||||
|
{
|
||||||
|
CreatePublic = false;
|
||||||
|
NetworkManager.singleton.StartHost();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GUI.Button(new Rect(30, 170, 160, 20), "Refresh"))
|
||||||
|
{
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
int yPos = 200;
|
||||||
|
|
||||||
|
foreach (var x in ServerList)
|
||||||
|
{
|
||||||
|
if(GUI.Button(new Rect(30, yPos+=30, 160, 20), x))
|
||||||
|
{
|
||||||
|
NetworkManager.singleton.networkAddress = x;
|
||||||
|
NetworkManager.singleton.StartClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
BIN
Images/preview.png
Executable file
BIN
Images/preview.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 154 KiB |
22
LICENSE.md
Executable file
22
LICENSE.md
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Tom Weiland
|
||||||
|
Copyright (c) 2022 Techiesplash
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
24
LobbyManager.cs
Executable file
24
LobbyManager.cs
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Mirror;
|
||||||
|
using Scripts.CustomServer;
|
||||||
|
using Scripts.Matchmaker;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(Client))]
|
||||||
|
public class LobbyManager : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public Text text;
|
||||||
|
|
||||||
|
public override void OnStartServer()
|
||||||
|
{
|
||||||
|
if (isServer)
|
||||||
|
{
|
||||||
|
IMatchmaker.lobbyClient = gameObject.GetComponent<Client>();
|
||||||
|
IMatchmaker.ConnectLobby();
|
||||||
|
IMatchmaker.SetLobbyAttribute("public", IMGUIMenuDemo.CreatePublic.ToString());
|
||||||
|
text.text = IMatchmaker.GetLobbyAttributeDirect("_auto_uuid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
654
Matchmaker.cs
Executable file
654
Matchmaker.cs
Executable file
|
@ -0,0 +1,654 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Tom Weiland
|
||||||
|
* Copyright (c) 2022 Vincent Dowling
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Scripts.CustomServer;
|
||||||
|
using UnityEngine;
|
||||||
|
using Client = Scripts.CustomServer.Client;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.Matchmaker
|
||||||
|
{
|
||||||
|
public interface IMatchmaker
|
||||||
|
{
|
||||||
|
/**************************************************************************************************************
|
||||||
|
* Matchmaker Programming Interface - External
|
||||||
|
**************************************************************************************************************/
|
||||||
|
|
||||||
|
// Global Variables --------------------------------------------------------------------------------------------
|
||||||
|
public static Client listClient;
|
||||||
|
public static Client lobbyClient;
|
||||||
|
public static int port = Config.Port;
|
||||||
|
public static string ip = Config.Address;
|
||||||
|
public static string GameId = Config.GameId;
|
||||||
|
|
||||||
|
public static readonly Client.ClientPackets apiPackets = new Client.ClientPackets(
|
||||||
|
new Dictionary<int, Client.ClientPackets.PacketHandler>()
|
||||||
|
{
|
||||||
|
{ 10, API.APIPacketHandlers.ReceiveLobbyId },
|
||||||
|
{ 11, API.APIPacketHandlers.GetLobbyAttributesReceived },
|
||||||
|
{ 12, API.APIPacketHandlers.FinishedSendingLobbyIds },
|
||||||
|
});
|
||||||
|
|
||||||
|
// API Functions - Connection Management -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MatchmakerNode_Connect the List Client.
|
||||||
|
/// </summary>
|
||||||
|
public static bool Connect()
|
||||||
|
{
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
listClient.ip = ip;
|
||||||
|
listClient.port = port;
|
||||||
|
listClient.welcomeReceived = false;
|
||||||
|
if (!listClient.ConnectToServer(apiPackets)) return false;
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
while (listClient.welcomeReceived == false && ((DateTime.Now.Ticks - startTime) < 30000000))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((DateTime.Now.Ticks - startTime) < 30000000) return true;
|
||||||
|
Debug.LogError("Failed to connect to List Server: Connection timed out!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError(
|
||||||
|
"Attempted to connect to the List Server while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MatchmakerNode_Connect the Lobby Client.
|
||||||
|
/// </summary>
|
||||||
|
public static bool ConnectLobby()
|
||||||
|
{
|
||||||
|
|
||||||
|
lobbyClient.ip = ip;
|
||||||
|
lobbyClient.port = port + 2;
|
||||||
|
lobbyClient.welcomeReceived = false;
|
||||||
|
if (lobbyClient.ConnectToServer(new Client.ClientPackets(new Dictionary<int, Client.ClientPackets.PacketHandler>())))
|
||||||
|
{
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
while (lobbyClient.welcomeReceived == false && ((DateTime.Now.Ticks - startTime) < 30000000))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((DateTime.Now.Ticks - startTime) < 30000000) return true;
|
||||||
|
Debug.LogError("Failed to connect to to Lobby Server: Connection timed out!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnect the List Client.
|
||||||
|
/// </summary>
|
||||||
|
public static void Disconnect()
|
||||||
|
{
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
listClient.Disconnect();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to disconnect a List Client while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnect the Lobby Client.
|
||||||
|
/// </summary>
|
||||||
|
public static void DisconnectLobby()
|
||||||
|
{
|
||||||
|
Debug.Log("Disconnecting from Lobby server...");
|
||||||
|
// SetLobbyAttribute("_auto_uuid", "DISCONNECTED_LOBBY");
|
||||||
|
// SetLobbyAttribute("_auto_ip", "DISCONNECTED_LOBBY");
|
||||||
|
// SetLobbyAttribute("_auto_port", "DISCONNECTED_LOBBY");
|
||||||
|
lobbyClient.Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Functions - Client Attributes ---------------------------------------------------------------------------
|
||||||
|
/// <summary>
|
||||||
|
/// Set an Attribute on the List Client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of the Attribute to set</param>
|
||||||
|
/// <param name="value">Value of the Attribute to set to</param>
|
||||||
|
public static bool SetClientAttribute(string name, string value)
|
||||||
|
{
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
if (listClient.IsConnected)
|
||||||
|
{
|
||||||
|
if (ClientSend.SetClientAttribute(listClient, name, value))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to set a List Client Attribute while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a List Client's Attribute.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">What List Client to find the Attribute in</param>
|
||||||
|
/// <param name="name">Name of the Attribute to get.</param>
|
||||||
|
/// <returns>The value of the Attribute. Returns null on failure.</returns>
|
||||||
|
public static string GetClientAttribute(int clientId, string name)
|
||||||
|
{
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
if (listClient.IsConnected)
|
||||||
|
{
|
||||||
|
var foo = ClientSend.GetClientAttribute(listClient, clientId, name);
|
||||||
|
|
||||||
|
|
||||||
|
// Debug check - Is the received Attributes Name not the same as the requested one?
|
||||||
|
if (foo.name == name) return foo.value;
|
||||||
|
Debug.LogError("Requested Attribute Name and Received Attribute Name do not match!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to get a List Client Attribute while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Functions - Lobby Attributes ----------------------------------------------------------------------------
|
||||||
|
/// <summary>
|
||||||
|
/// Get a Lobby Client's Attribute straight from the Lobby Server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">What Lobby Client to find the Attribute in</param>
|
||||||
|
/// <param name="name">Name of the Attribute to get.</param>
|
||||||
|
/// <returns>The value of the Attribute. Returns null on failure.</returns>
|
||||||
|
public static string GetLobbyAttributeDirect(string name)
|
||||||
|
{
|
||||||
|
if (lobbyClient != null)
|
||||||
|
{
|
||||||
|
if (lobbyClient.IsConnected)
|
||||||
|
{
|
||||||
|
var foo = ClientSend.GetClientAttribute(lobbyClient, lobbyClient.myId, name);
|
||||||
|
|
||||||
|
|
||||||
|
// Debug check - Is the received Attributes Name not the same as the requested one?
|
||||||
|
if (foo.name == name) return foo.value;
|
||||||
|
Debug.LogError("Requested Attribute Name and Received Attribute Name do not match!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to get a Lobby Client's Attribute *directly* while lobbyClient is null. Please set Matchmaker.lobbyClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a Attribute on the Lobby Client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Name of the Attribute to set.</param>
|
||||||
|
/// <param name="value">What value to set the Attribute to.</param>
|
||||||
|
public static bool SetLobbyAttribute(string name, string value)
|
||||||
|
{
|
||||||
|
if (lobbyClient != null)
|
||||||
|
{
|
||||||
|
if (lobbyClient.IsConnected)
|
||||||
|
{
|
||||||
|
return ClientSend.SetClientAttribute(lobbyClient, name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to set a Lobby Client Attribute while lobbyClient is null. Please set Matchmaker.lobbyClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a Lobby Client Attribute by passing through the List Server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">What Lobby Client to find the Attribute in</param>
|
||||||
|
/// <param name="name">Name of the Attribute to get.</param>
|
||||||
|
/// <returns>The value of the Attribute. Returns null on failure.</returns>
|
||||||
|
public static string GetLobbyAttribute(int clientId, string name)
|
||||||
|
{
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
if (listClient.IsConnected)
|
||||||
|
{
|
||||||
|
var foo = API.APIClientPackets.GetLobbyAttribute(listClient, clientId, name);
|
||||||
|
|
||||||
|
if (foo == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Received Lobby Attribute is null!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug check - Is the received Attributes Name not the same as the requested one?
|
||||||
|
if (foo.name == name) return foo.value;
|
||||||
|
Debug.LogError(
|
||||||
|
$"Requested Attribute Name {foo.name} and Received Attribute Name {name} do not match!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to get a Lobby Client's Attribute while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Functions - Lobby Discovery -----------------------------------------------------------------------------
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a Lobby Client's ID by its UUID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uuid">The UUID to find a Lobby Client with.</param>
|
||||||
|
/// <returns>A Lobby Client's ID with matching UUID.</returns>
|
||||||
|
public static int GetLobbyIdByUuid(string uuid)
|
||||||
|
{
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
if (listClient.IsConnected)
|
||||||
|
{
|
||||||
|
API.APIPacketHandlers.lobbyIds.Clear();
|
||||||
|
var foo = GetAllLobbyIdsByAttribute("_auto_uuid", uuid);
|
||||||
|
if(foo.Count > 1)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Found more than one lobby with UUID!");
|
||||||
|
}
|
||||||
|
if (foo.Count <= 0)
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Failed to find Lobby IDs with matching UUID!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return foo[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to get a Lobby Client's ID through a UUID while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all Lobby IDs by matching Attribute
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="attribName">The name of the Attribute</param>
|
||||||
|
/// <param name="attribValue">The value of the Attribute</param>
|
||||||
|
public static List<int> GetAllLobbyIdsByAttribute(string attribName, string attribValue)
|
||||||
|
{
|
||||||
|
API.APIPacketHandlers.lobbyIds.Clear();
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
if (!listClient.IsConnected) return API.APIPacketHandlers.lobbyIds;
|
||||||
|
|
||||||
|
var foo = API.APIClientPackets.RequestLobbyIdsWithMatchingAttribute(listClient, attribName, attribValue);
|
||||||
|
if (!foo)
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Failed to find Lobby IDs with matching Attrbute!");
|
||||||
|
|
||||||
|
}
|
||||||
|
return API.APIPacketHandlers.lobbyIds;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to get all lobby IDs with matching Attribute while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return API.APIPacketHandlers.lobbyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of the IDs of all connected Lobby Clients. Also calls a callback at Matchmaker.API.lobbyCallback for every ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List of Lobby Client IDs</returns>
|
||||||
|
[CanBeNull]
|
||||||
|
public static List<int> GetAllLobbyIds()
|
||||||
|
{
|
||||||
|
if (listClient != null)
|
||||||
|
{
|
||||||
|
API.APIPacketHandlers.lobbyIds.Clear();
|
||||||
|
|
||||||
|
Debug.Log("GetAllLobbyIds() called.");
|
||||||
|
if (listClient.IsConnected)
|
||||||
|
{
|
||||||
|
API.APIClientPackets.RequestAllLobbyIds(listClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogWarning("Attempted to request all Lobby IDs while Client is disconnected!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning(
|
||||||
|
"Attempted to get a list of all Lobby IDs while listClient is null. Please set Matchmaker.listClient first!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"Lobby ID list length: {API.APIPacketHandlers.lobbyIds.Count}");
|
||||||
|
return API.APIPacketHandlers.lobbyIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************************************************
|
||||||
|
* Matchmaker Programming Interface - Internal
|
||||||
|
**************************************************************************************************************/
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the internal functionality for the Matchmaker API
|
||||||
|
/// </summary>
|
||||||
|
public static class API
|
||||||
|
{
|
||||||
|
// API Internals - Global Variables ------------------------------------------------------------------------
|
||||||
|
public delegate void UserCallbackDelegate(int userId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets called back whenever a Lobby Client ID is received by GetAllLobbyIds().
|
||||||
|
/// </summary>
|
||||||
|
public static UserCallbackDelegate lobbyCallback;
|
||||||
|
|
||||||
|
private static readonly List<Action> ExecuteOnApiThread = new();
|
||||||
|
private static readonly List<Action> ExecuteCopiedOnApiThread = new();
|
||||||
|
private static bool _actionToExecuteOnApiThread;
|
||||||
|
|
||||||
|
private const int TicksPerSec = 30;
|
||||||
|
private const float MsPerTick = 1000f / TicksPerSec;
|
||||||
|
private static bool _isRunning = true;
|
||||||
|
|
||||||
|
private static Thread _apiThread;
|
||||||
|
|
||||||
|
// API Internals - API Thread Manager ----------------------------------------------------------------------
|
||||||
|
private static void APIThread(object data)
|
||||||
|
{
|
||||||
|
Debug.Log($"MatchmakerBehaviour thread started. Running at {TicksPerSec} ticks per second.");
|
||||||
|
var nextLoop = DateTime.Now;
|
||||||
|
|
||||||
|
while (_isRunning)
|
||||||
|
{
|
||||||
|
while (nextLoop < DateTime.Now)
|
||||||
|
{
|
||||||
|
UpdateAPI();
|
||||||
|
|
||||||
|
nextLoop = nextLoop.AddMilliseconds(MsPerTick);
|
||||||
|
|
||||||
|
if (nextLoop > DateTime.Now)
|
||||||
|
{
|
||||||
|
Thread.Sleep(nextLoop - DateTime.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StartThreads()
|
||||||
|
{
|
||||||
|
// if (apiThread != null || _isRunning != false) return;
|
||||||
|
|
||||||
|
_isRunning = true;
|
||||||
|
_apiThread = new Thread(APIThread);
|
||||||
|
_apiThread.Start(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void StopThreads()
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Sets an action to be executed on the main thread.</summary>
|
||||||
|
/// <param name="action">The action to be executed on the main thread.</param>
|
||||||
|
public static void ExecuteOnAPIThread(Action action)
|
||||||
|
{
|
||||||
|
if (action == null)
|
||||||
|
{
|
||||||
|
Debug.Log("No action to execute on MatchmakerBehaviour thread!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (ExecuteOnApiThread)
|
||||||
|
{
|
||||||
|
ExecuteOnApiThread.Add(action);
|
||||||
|
_actionToExecuteOnApiThread = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes all code meant to run on the main thread. NOTE: Call this ONLY from the main thread.
|
||||||
|
/// </summary>
|
||||||
|
private static void UpdateAPI()
|
||||||
|
{
|
||||||
|
if (_actionToExecuteOnApiThread)
|
||||||
|
{
|
||||||
|
ExecuteCopiedOnApiThread.Clear();
|
||||||
|
lock (ExecuteOnApiThread)
|
||||||
|
{
|
||||||
|
ExecuteCopiedOnApiThread.AddRange(ExecuteOnApiThread);
|
||||||
|
ExecuteOnApiThread.Clear();
|
||||||
|
_actionToExecuteOnApiThread = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var t in ExecuteCopiedOnApiThread)
|
||||||
|
{
|
||||||
|
t();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Internals - Packet Handling -------------------------------------------------------------------------
|
||||||
|
public static class APIPacketHandlers
|
||||||
|
{
|
||||||
|
public static readonly List<int> lobbyIds = new();
|
||||||
|
public static bool finishedSendingIds;
|
||||||
|
public static bool receivedId;
|
||||||
|
|
||||||
|
public static void ReceiveLobbyId(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
receivedId = true;
|
||||||
|
int userId = packet.ReadInt();
|
||||||
|
int mod = packet.ReadInt();
|
||||||
|
Debug.Log("Received lobby id: " + userId.ToString());
|
||||||
|
lobbyIds.Add(userId);
|
||||||
|
switch (mod)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
if (API.lobbyCallback != null)
|
||||||
|
{
|
||||||
|
API.lobbyCallback.Invoke(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
client.lastReceivedInt = userId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void GetLobbyAttributesReceived(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
int requestedId = packet.ReadInt();
|
||||||
|
string name = packet.ReadString();
|
||||||
|
string value = packet.ReadString();
|
||||||
|
|
||||||
|
Debug.Log("Received lobby attributes (Obj. ID: " + requestedId.ToString() + "): '" + name + "', '" +
|
||||||
|
value + "'.");
|
||||||
|
|
||||||
|
client.lastReceivedAttrib = new ClientHandle.ClientAttrib(requestedId, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FinishedSendingLobbyIds(Client client, Packet packet)
|
||||||
|
{
|
||||||
|
Debug.Log("Finished receiving Lobby IDs.");
|
||||||
|
finishedSendingIds = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// API Internals - Packet Sending --------------------------------------------------------------------------
|
||||||
|
public static class APIClientPackets
|
||||||
|
{
|
||||||
|
// ReSharper disable once FieldCanBeMadeReadOnly.Global
|
||||||
|
// ReSharper disable once ConvertToConstant.Global
|
||||||
|
// ReSharper disable once MemberCanBePrivate.Global
|
||||||
|
public static long timeOutTime = 300000000;
|
||||||
|
|
||||||
|
public static bool RequestAllLobbyIds(Client client)
|
||||||
|
{
|
||||||
|
if (client.IsConnected)
|
||||||
|
{
|
||||||
|
APIPacketHandlers.finishedSendingIds = false;
|
||||||
|
Debug.Log("Getting all the connected lobby IDs...");
|
||||||
|
using var packet = new Packet(10);
|
||||||
|
packet.Write(client.myId);
|
||||||
|
ClientSend.SendTcpData(client, packet);
|
||||||
|
|
||||||
|
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
while (!APIPacketHandlers.finishedSendingIds &&
|
||||||
|
((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
if (!APIPacketHandlers.receivedId) continue;
|
||||||
|
startTime = DateTime.Now.Ticks;
|
||||||
|
APIPacketHandlers.receivedId = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
Debug.LogError(
|
||||||
|
$"RequestAllLobbyIds: Request timed out! Returning {client.lastReceivedInt}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Tried to request all Lobby IDs while the Client is disconnected!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static bool RequestLobbyIdsWithMatchingAttribute(Client client, string attribName,
|
||||||
|
string attribValue)
|
||||||
|
{
|
||||||
|
Debug.Log("Getting a lobby ID by UUID...");
|
||||||
|
|
||||||
|
using var packet = new Packet(11);
|
||||||
|
APIPacketHandlers.finishedSendingIds = false;
|
||||||
|
|
||||||
|
packet.Write(client.myId);
|
||||||
|
packet.Write(attribName);
|
||||||
|
packet.Write(attribValue);
|
||||||
|
|
||||||
|
ClientSend.SendTcpData(client, packet);
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
while (!APIPacketHandlers.finishedSendingIds &&
|
||||||
|
((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
// if (!APIPacketHandlers.receivedId) continue;
|
||||||
|
// startTime = DateTime.Now.Ticks;
|
||||||
|
// APIPacketHandlers.receivedId = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
Debug.LogError(
|
||||||
|
$"RequestLobbyIdsWithMatchingAttribute: Request timed out!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ClientHandle.ClientAttrib GetLobbyAttribute(Client client, int requestedId, string name)
|
||||||
|
{
|
||||||
|
Debug.Log("Getting a lobby attribute...");
|
||||||
|
using var packet = new Packet(12);
|
||||||
|
packet.Write(client.myId);
|
||||||
|
packet.Write(requestedId);
|
||||||
|
packet.Write(name);
|
||||||
|
|
||||||
|
ClientSend.SendTcpData(client, packet);
|
||||||
|
var startTime = DateTime.Now.Ticks;
|
||||||
|
|
||||||
|
client.lastReceivedAttrib = null;
|
||||||
|
|
||||||
|
while (client.lastReceivedAttrib == null &&
|
||||||
|
((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!((DateTime.Now.Ticks - startTime) < timeOutTime))
|
||||||
|
{
|
||||||
|
Debug.LogError($"GetLobbyAttribute: Request timed out!.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.lastReceivedAttrib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
README.md
Executable file
40
README.md
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
# Matchmaker-API - Unity3D Client
|
||||||
|
<br />
|
||||||
|
|
||||||
|
![Unity](https://img.shields.io/badge/unity-%23000000.svg?style=for-the-badge&logo=unity&logoColor=white)
|
||||||
|
![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)
|
||||||
|
![Windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white)
|
||||||
|
![macOS](https://img.shields.io/badge/mac%20os-000000?style=for-the-badge&logo=macos&logoColor=F0F0F0)
|
||||||
|
|
||||||
|
|
||||||
|
![GitHub forks](https://img.shields.io/github/forks/Techiesplash/Matchmaker-API-Client-Unity3d)
|
||||||
|
![GitHub repo size](https://img.shields.io/github/repo-size/Techiesplash/Matchmaker-API-Client-Unity3d)
|
||||||
|
![GitHub all releases](https://img.shields.io/github/downloads/Techiesplash/Matchmaker-API-Client-Unity3d/total)
|
||||||
|
![GitHub issues](https://img.shields.io/github/issues/Techiesplash/Matchmaker-API-Client-Unity3d)
|
||||||
|
|
||||||
|
![GitHub](https://img.shields.io/github/license/Techiesplash/Matchmaker-API-Client-Unity3d)
|
||||||
|
![GitHub release (latest by date)](https://img.shields.io/github/v/release/Techiesplash/Matchmaker-API-Client-Unity3d)
|
||||||
|
|
||||||
|
<h2>Introduction</h2>
|
||||||
|
This is a project for implementing a Matchmaker API into Unity3D.
|
||||||
|
<br />
|
||||||
|
It can be expanded with custom packets as needed.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
It depends on another project to be used in Unity. https://github.com/Techiesplash/Matchmaker-API-Server
|
||||||
|
<br /><br />
|
||||||
|
This project is built upon MIT-Licensed code by Tom Weiland meant for a tutorial series.
|
||||||
|
Please check out his work: https://github.com/tom-weiland/tcp-udp-networking
|
||||||
|
<br />
|
||||||
|
|
||||||
|
![UVS Preview](./Images/preview.png)
|
||||||
|
|
||||||
|
<h2>Setup</h2>
|
||||||
|
To start, you can either download this project as a .ZIP file and place the files in your project,
|
||||||
|
or you can click on your Project files in Unity Editor and use Import > Custom Package and import a package from Releases.
|
||||||
|
<br /><br />
|
||||||
|
This includes Visual Scripting support. If you want to use this feature, ensure VIsual Scripting is enabled in Project Settings > Visual Scripting. You may also need to regenerate the nodes in Project Settings > Visual Scripting > Regenerate Nodes.
|
||||||
|
<br /><br />
|
||||||
|
<h3>Anyone is free to use, copy, modify, merge, publish, distribute, sublicense, or and/or sell copies of the software.</h3>
|
19
ThreadObject.cs
Executable file
19
ThreadObject.cs
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
using Scripts.CustomServer;
|
||||||
|
|
||||||
|
public class ThreadObject : MonoBehaviour
|
||||||
|
{
|
||||||
|
// Start is called before the first frame update
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
ThreadManager.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called once per frame
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
ThreadManager.UpdateMain();
|
||||||
|
}
|
||||||
|
}
|
7
Visual Scripting/DummyScript_Icon.cs
Executable file
7
Visual Scripting/DummyScript_Icon.cs
Executable file
|
@ -0,0 +1,7 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class DummyScript_Icon : MonoBehaviour
|
||||||
|
{
|
||||||
|
}
|
42
Visual Scripting/MatchmakerNode_Address.cs
Executable file
42
Visual Scripting/MatchmakerNode_Address.cs
Executable file
|
@ -0,0 +1,42 @@
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Matchmaking Server Address")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_Address : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputTrigger;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput set;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput get;
|
||||||
|
|
||||||
|
|
||||||
|
private bool state;
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
IMatchmaker.ip = flow.GetValue<string>(set);
|
||||||
|
return outputTrigger;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputTrigger = ControlOutput("");
|
||||||
|
|
||||||
|
set = ValueInput<string>("set");
|
||||||
|
get = ValueOutput<string>("get", (flow) => IMatchmaker.ip);
|
||||||
|
Succession(inputTrigger, outputTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Visual Scripting/MatchmakerNode_Connect.cs
Executable file
39
Visual Scripting/MatchmakerNode_Connect.cs
Executable file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
using Scripts.Matchmaker;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Connect to List Server")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_Connect : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess; //Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail; //Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
private bool resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("",
|
||||||
|
(flow) => { return IMatchmaker.Connect() ? outputOnSuccess : outputOnFail; });
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("onSuccess");
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
Visual Scripting/MatchmakerNode_ConnectLobby.cs
Executable file
47
Visual Scripting/MatchmakerNode_ConnectLobby.cs
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Scripts.Matchmaker;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Connect to Lobby Server")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_ConnectLobby : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess; //Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail; //Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
private bool resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
if (IMatchmaker.ConnectLobby())
|
||||||
|
{
|
||||||
|
return outputOnSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputOnFail;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("onSuccess");
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Visual Scripting/MatchmakerNode_Disconnect.cs
Executable file
37
Visual Scripting/MatchmakerNode_Disconnect.cs
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Disconnect from List Server")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_Disconnect : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputTrigger;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
|
||||||
|
private bool resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
IMatchmaker.Disconnect();
|
||||||
|
|
||||||
|
return outputTrigger;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputTrigger = ControlOutput("");
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputTrigger);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
Visual Scripting/MatchmakerNode_DisconnectLobby.cs
Executable file
37
Visual Scripting/MatchmakerNode_DisconnectLobby.cs
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Disconnect from Lobby Server")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_DisconnectLobby : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputTrigger;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
|
||||||
|
private bool resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
IMatchmaker.DisconnectLobby();
|
||||||
|
|
||||||
|
return outputTrigger;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputTrigger = ControlOutput("");
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputTrigger);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
77
Visual Scripting/MatchmakerNode_GetAllLobbiesWithAttribute.cs
Executable file
77
Visual Scripting/MatchmakerNode_GetAllLobbiesWithAttribute.cs
Executable file
|
@ -0,0 +1,77 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Mirror;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Get All Lobby IDs with Attribute")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_GetAllLobbiesWithAttribute : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputIfSome;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputIfNone;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput idList;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput attribName;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput attribValue;
|
||||||
|
|
||||||
|
public List<int> resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultValue = IMatchmaker.GetAllLobbyIdsByAttribute(flow.GetValue<string>(attribName),
|
||||||
|
flow.GetValue<string>(attribValue));
|
||||||
|
; if (resultValue is { Count: > 0 })
|
||||||
|
{
|
||||||
|
return outputIfSome;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputIfNone;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Get All Lobby IDs node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputIfNone;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputIfNone = ControlOutput("ifNone");
|
||||||
|
|
||||||
|
outputIfSome = ControlOutput("ifSome");
|
||||||
|
|
||||||
|
idList = ValueOutput<List<int>>("result", (flow) => resultValue);
|
||||||
|
|
||||||
|
attribName = ValueInput<string>("attributeName");
|
||||||
|
|
||||||
|
attribValue = ValueInput<string>("attributeValue");
|
||||||
|
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputIfSome);
|
||||||
|
Succession(inputTrigger, outputIfNone);
|
||||||
|
|
||||||
|
Assignment(inputTrigger, idList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Visual Scripting/MatchmakerNode_GetAllLobbyIds.cs
Executable file
65
Visual Scripting/MatchmakerNode_GetAllLobbyIds.cs
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Mirror;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Get All Lobby IDs")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_GetAllLobbyIds : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputIfSome;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputIfNone;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput idList;
|
||||||
|
|
||||||
|
public List<int> resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultValue = IMatchmaker.GetAllLobbyIds();
|
||||||
|
if (resultValue is { Count: > 0 })
|
||||||
|
{
|
||||||
|
return outputIfSome;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputIfNone;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Get All Lobby IDs node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputIfNone;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputIfNone = ControlOutput("ifNone");
|
||||||
|
|
||||||
|
outputIfSome = ControlOutput("ifSome");
|
||||||
|
|
||||||
|
idList = ValueOutput<List<int>>("result", (flow) => resultValue);
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputIfSome);
|
||||||
|
Succession(inputTrigger, outputIfNone);
|
||||||
|
|
||||||
|
Assignment(inputTrigger, idList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
Visual Scripting/MatchmakerNode_GetClientAttribute.cs
Executable file
76
Visual Scripting/MatchmakerNode_GetClientAttribute.cs
Executable file
|
@ -0,0 +1,76 @@
|
||||||
|
using System;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Get Client Attribute")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_GetClientAttribute : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput lobbyId;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput name;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput value;
|
||||||
|
|
||||||
|
private string resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultValue =
|
||||||
|
IMatchmaker.GetLobbyAttribute(flow.GetValue<int>(lobbyId), flow.GetValue<string>(name));
|
||||||
|
|
||||||
|
if (resultValue != null)
|
||||||
|
{
|
||||||
|
return outputOnSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultValue = "";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Get Lobby Attribute node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
resultValue = "";
|
||||||
|
return outputOnFail;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("onSuccess");
|
||||||
|
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
value = ValueOutput<string>("value", (flow) => resultValue);
|
||||||
|
|
||||||
|
name = ValueInput<string>("Name", "");
|
||||||
|
|
||||||
|
lobbyId = ValueInput<int>("clientId");
|
||||||
|
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
76
Visual Scripting/MatchmakerNode_GetLobbyAttribute.cs
Executable file
76
Visual Scripting/MatchmakerNode_GetLobbyAttribute.cs
Executable file
|
@ -0,0 +1,76 @@
|
||||||
|
using System;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Get Lobby Attribute")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_GetLobbyAttribute : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput clientId;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput name;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput value;
|
||||||
|
|
||||||
|
private string resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultValue =
|
||||||
|
IMatchmaker.GetClientAttribute(flow.GetValue<int>(clientId), flow.GetValue<string>(name));
|
||||||
|
|
||||||
|
if (resultValue != null)
|
||||||
|
{
|
||||||
|
return outputOnSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultValue = "";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Get Client Attribute node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
resultValue = "";
|
||||||
|
return outputOnFail;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("onSuccess");
|
||||||
|
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
value = ValueOutput<string>("value", (flow) => resultValue);
|
||||||
|
|
||||||
|
name = ValueInput<string>("Name", "");
|
||||||
|
|
||||||
|
clientId = ValueInput<int>("lobbyId");
|
||||||
|
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
Visual Scripting/MatchmakerNode_GetLobbyAttributeDirect.cs
Executable file
72
Visual Scripting/MatchmakerNode_GetLobbyAttributeDirect.cs
Executable file
|
@ -0,0 +1,72 @@
|
||||||
|
using System;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Get Lobby Attribute Directly")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_GetLobbyAttributeDirect : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput id;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput name;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput value;
|
||||||
|
|
||||||
|
private string resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultValue =
|
||||||
|
IMatchmaker.GetLobbyAttributeDirect(flow.GetValue<string>(name));
|
||||||
|
|
||||||
|
if (resultValue != null)
|
||||||
|
{
|
||||||
|
return outputOnSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Get Lobby Attribute Direct node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputOnFail;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("onSuccess");
|
||||||
|
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
id = ValueInput<int>("lobbyId");
|
||||||
|
|
||||||
|
name = ValueInput<string>("Name", "");
|
||||||
|
|
||||||
|
value = ValueOutput<string>("Value", (flow) => resultValue);
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Visual Scripting/MatchmakerNode_GetLobbyByUuid.cs
Executable file
65
Visual Scripting/MatchmakerNode_GetLobbyByUuid.cs
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Get Lobby By UUID")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_GetLobbyByUuid : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput uuid;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput id;
|
||||||
|
|
||||||
|
public int resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
resultValue = IMatchmaker.GetLobbyIdByUuid(flow.GetValue<string>(uuid).ToUpper());
|
||||||
|
if (resultValue > 0)
|
||||||
|
{
|
||||||
|
return outputOnSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Get Lobby By Uuid node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputOnFail;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("onSuccess");
|
||||||
|
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
uuid = ValueInput<string>("uuid", "");
|
||||||
|
id = ValueOutput<int>("result", (flow) => resultValue);
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
|
||||||
|
Assignment(inputTrigger, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Visual Scripting/MatchmakerNode_IsClientConnected.cs
Executable file
31
Visual Scripting/MatchmakerNode_IsClientConnected.cs
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
using Mirror;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Is Client Connected")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_IsClientConnected : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput get;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
get = ValueOutput<bool>("get", (flow) =>
|
||||||
|
{
|
||||||
|
if (IMatchmaker.listClient == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Error in Is Client Connected node: listClient is null!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IMatchmaker.listClient.IsConnected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Visual Scripting/MatchmakerNode_IsLobbyConnected.cs
Executable file
31
Visual Scripting/MatchmakerNode_IsLobbyConnected.cs
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
using Mirror;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Is Lobby Connected")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_IsLobbyConnected : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput get;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
get = ValueOutput<bool>("get", (flow) =>
|
||||||
|
{
|
||||||
|
if (IMatchmaker.lobbyClient == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Error in Is Lobby Connected node: listClient is null!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IMatchmaker.lobbyClient.IsConnected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
Visual Scripting/MatchmakerNode_Port.cs
Executable file
44
Visual Scripting/MatchmakerNode_Port.cs
Executable file
|
@ -0,0 +1,44 @@
|
||||||
|
using Mirror;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("Matchmaking Server Port")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_Port : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputTrigger;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput set;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput get;
|
||||||
|
|
||||||
|
|
||||||
|
private bool state;
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
IMatchmaker.port = flow.GetValue<int>(set);
|
||||||
|
return outputTrigger;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputTrigger = ControlOutput("");
|
||||||
|
|
||||||
|
set = ValueInput<int>("set");
|
||||||
|
get = ValueOutput<int>("get", (flow) => IMatchmaker.port);
|
||||||
|
Succession(inputTrigger, outputTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Visual Scripting/MatchmakerNode_SetClientAttribute.cs
Executable file
65
Visual Scripting/MatchmakerNode_SetClientAttribute.cs
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Set Client Attribute")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_SetClientAttribute : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput name;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput value;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result =
|
||||||
|
IMatchmaker.SetClientAttribute(flow.GetValue<string>(name), flow.GetValue<string>(value));
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return outputOnSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Set Client Attribute node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputOnFail;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("oOnSuccess");
|
||||||
|
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
name = ValueInput<string>("Name", "");
|
||||||
|
|
||||||
|
value = ValueInput<string>("value", "");
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
Visual Scripting/MatchmakerNode_SetClientObjects.cs
Executable file
55
Visual Scripting/MatchmakerNode_SetClientObjects.cs
Executable file
|
@ -0,0 +1,55 @@
|
||||||
|
using Mirror;
|
||||||
|
using Scripts.CustomServer;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Set Client Objects")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_SetClientObjects : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputTrigger;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput listclient;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput lobbyclient;
|
||||||
|
|
||||||
|
private bool resultValue;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
if (flow.GetValue<Client>(listclient) != null)
|
||||||
|
{
|
||||||
|
IMatchmaker.listClient = flow.GetValue<Client>(listclient);
|
||||||
|
}
|
||||||
|
if (flow.GetValue<Client>(lobbyclient) != null)
|
||||||
|
{
|
||||||
|
IMatchmaker.lobbyClient = flow.GetValue<Client>(lobbyclient);
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputTrigger;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputTrigger = ControlOutput("");
|
||||||
|
|
||||||
|
listclient = ValueInput<Client>("listClient", null);
|
||||||
|
|
||||||
|
lobbyclient = ValueInput<Client>("lobbyClient", null);
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Visual Scripting/MatchmakerNode_SetLobbyAttribute.cs
Executable file
65
Visual Scripting/MatchmakerNode_SetLobbyAttribute.cs
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
using System;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
|
||||||
|
[UnitTitle("Set Lobby Attribute")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_SetLobbyAttribute : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlInput inputTrigger; //Adding the ControlInput port variable
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnSuccess;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports.
|
||||||
|
public ControlOutput outputOnFail;//Adding the ControlOutput port variable.
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput name;
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueInput value;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
//Making the ControlInput port visible, setting its key and running the anonymous action method to pass the flow to the outputTrigger port.
|
||||||
|
inputTrigger = ControlInput("", (flow) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result =
|
||||||
|
IMatchmaker.SetLobbyAttribute(flow.GetValue<string>(name), flow.GetValue<string>(value));
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return outputOnSuccess;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error in Set Client Attribute node: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputOnFail;
|
||||||
|
});
|
||||||
|
//Making the ControlOutput port visible and setting its key.
|
||||||
|
outputOnSuccess = ControlOutput("oOnSuccess");
|
||||||
|
|
||||||
|
outputOnFail = ControlOutput("onFail");
|
||||||
|
|
||||||
|
name = ValueInput<string>("Name", "");
|
||||||
|
|
||||||
|
value = ValueInput<string>("value", "");
|
||||||
|
|
||||||
|
Succession(inputTrigger, outputOnFail);
|
||||||
|
Succession(inputTrigger, outputOnSuccess);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Visual Scripting/MatchmakerNode_ThisClient.cs
Executable file
31
Visual Scripting/MatchmakerNode_ThisClient.cs
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
using Mirror;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("This Client's ID")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_ThisClient : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput get;
|
||||||
|
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
get = ValueOutput<int>("get", (flow) =>
|
||||||
|
{
|
||||||
|
if (IMatchmaker.listClient == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Error in This Client's ID node: listClient is null!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IMatchmaker.listClient.myId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
Visual Scripting/MatchmakerNode_ThisLobby.cs
Executable file
35
Visual Scripting/MatchmakerNode_ThisLobby.cs
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
using Mirror;
|
||||||
|
using Unity.VisualScripting;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace Scripts.Matchmaker.Visual_Scripting
|
||||||
|
{
|
||||||
|
[UnitTitle("This Lobby's ID")]
|
||||||
|
[UnitSubtitle("Matchmaker API")]
|
||||||
|
[UnitCategory("Matchmaking")]
|
||||||
|
[TypeIcon(typeof(DummyScript_Icon))]
|
||||||
|
public class MatchmakerNode_ThisLobby : Unit, IMatchmaker
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[DoNotSerialize] // No need to serialize ports
|
||||||
|
public ValueOutput get;
|
||||||
|
|
||||||
|
|
||||||
|
private bool state;
|
||||||
|
protected override void Definition()
|
||||||
|
{
|
||||||
|
get = ValueOutput<int>("get", (flow) =>
|
||||||
|
{
|
||||||
|
if (IMatchmaker.lobbyClient == null)
|
||||||
|
{
|
||||||
|
Debug.LogError("Error in This Lobby's ID node: lobbyClient is null!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return IMatchmaker.lobbyClient.myId;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Visual Scripting/MirrorNodeHelper.cs
Executable file
37
Visual Scripting/MirrorNodeHelper.cs
Executable file
|
@ -0,0 +1,37 @@
|
||||||
|
using Mirror;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace Scripts.Matchmaker
|
||||||
|
{
|
||||||
|
public class MirrorNodeHelper : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[Client]
|
||||||
|
public GameObject SpawnObject(GameObject iG, Vector3 iPos, Quaternion iRot, Transform iParent, Vector3 iScale)
|
||||||
|
{
|
||||||
|
|
||||||
|
SpawnObjectCmd(NetworkManager.singleton.spawnPrefabs.IndexOf(iG), iPos, iRot, iParent, iScale);
|
||||||
|
return GameObject.FindGameObjectWithTag("MirNodeHelperSpawned");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command(requiresAuthority = false)]
|
||||||
|
void SpawnObjectCmd(int iG, Vector3 iPos, Quaternion iRot, Transform iParent, Vector3 iScale)
|
||||||
|
{
|
||||||
|
GameObject go;
|
||||||
|
Debug.Log("Creating object...");
|
||||||
|
if (iParent != null)
|
||||||
|
{
|
||||||
|
go = Instantiate(NetworkManager.singleton.spawnPrefabs[iG], iPos, iRot, iParent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
go = Instantiate(NetworkManager.singleton.spawnPrefabs[iG], iPos, iRot, iParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
go.transform.localScale = iScale;
|
||||||
|
go.gameObject.tag = "MirNodeHelperSpawned";
|
||||||
|
NetworkServer.Spawn(go);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Visual Scripting/icon.png
Executable file
BIN
Visual Scripting/icon.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Loading…
Add table
Reference in a new issue