您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

461 行
15 KiB

using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using UnityEngine;
namespace SQP
{
[Flags]
public enum SQPChunkType
{
ServerInfo = 1,
ServerRules = 2,
PlayerInfo = 4,
TeamInfo = 8
}
public enum SQPMessageType
{
ChallangeRequest = 0,
ChallangeResponse = 0,
QueryRequest = 1,
QueryResponse = 1
}
public interface ISQPMessage
{
void ToStream(ref ByteOutputStream writer);
void FromStream(ref ByteInputStream reader);
}
public struct SQPHeader : ISQPMessage
{
public byte Type { get; internal set; }
public uint ChallangeId;
public void ToStream(ref ByteOutputStream writer)
{
writer.WriteUInt8(Type);
writer.WriteUInt32_NBO(ChallangeId);
}
public void FromStream(ref ByteInputStream reader)
{
Type = reader.ReadUInt8();
ChallangeId = reader.ReadUInt32_NBO();
}
}
public struct ChallangeRequest : ISQPMessage
{
public SQPHeader Header;
public void ToStream(ref ByteOutputStream writer)
{
Header.Type = (byte)SQPMessageType.ChallangeRequest;
Header.ToStream(ref writer);
}
public void FromStream(ref ByteInputStream reader)
{
Header.FromStream(ref reader);
}
}
public struct ChallangeResponse
{
public SQPHeader Header;
public void ToStream(ref ByteOutputStream writer)
{
Header.Type = (byte)SQPMessageType.ChallangeResponse;
Header.ToStream(ref writer);
}
public void FromStream(ref ByteInputStream reader)
{
Header.FromStream(ref reader);
}
}
public struct QueryRequest
{
public SQPHeader Header;
public ushort Version;
public byte RequestedChunks;
public void ToStream(ref ByteOutputStream writer)
{
Header.Type = (byte)SQPMessageType.QueryRequest;
Header.ToStream(ref writer);
writer.WriteUInt16_NBO(Version);
writer.WriteUInt8(RequestedChunks);
}
public void FromStream(ref ByteInputStream reader)
{
Header.FromStream(ref reader);
Version = reader.ReadUInt16_NBO();
RequestedChunks = reader.ReadUInt8();
}
}
public struct QueryResponseHeader
{
public SQPHeader Header;
public ushort Version;
public byte CurrentPacket;
public byte LastPacket;
public ushort Length;
public void ToStream(ref ByteOutputStream writer)
{
Header.Type = (byte)SQPMessageType.QueryResponse;
Header.ToStream(ref writer);
writer.WriteUInt16_NBO(Version);
writer.WriteUInt8(CurrentPacket);
writer.WriteUInt8(LastPacket);
writer.WriteUInt16_NBO(Length);
}
public void FromStream(ref ByteInputStream reader)
{
Header.FromStream(ref reader);
Version = reader.ReadUInt16_NBO();
CurrentPacket = reader.ReadUInt8();
LastPacket = reader.ReadUInt8();
Length = reader.ReadUInt16_NBO();
}
}
public class ServerInfo
{
public QueryResponseHeader QueryHeader;
public uint ChunkLen;
public Data ServerInfoData;
public ServerInfo()
{
ServerInfoData = new Data();
}
public class Data
{
public ushort CurrentPlayers;
public ushort MaxPlayers;
public string ServerName = "";
public string GameType = "";
public string BuildId = "";
public string Map = "";
public ushort Port;
public void ToStream(ref ByteOutputStream writer)
{
writer.WriteUInt16_NBO(CurrentPlayers);
writer.WriteUInt16_NBO(MaxPlayers);
writer.WriteString(ServerName, encoding);
writer.WriteString(GameType, encoding);
writer.WriteString(BuildId, encoding);
writer.WriteString(Map, encoding);
writer.WriteUInt16_NBO(Port);
}
public void FromStream(ref ByteInputStream reader)
{
CurrentPlayers = reader.ReadUInt16_NBO();
MaxPlayers = reader.ReadUInt16_NBO();
ServerName = reader.ReadString(encoding);
GameType = reader.ReadString(encoding);
BuildId = reader.ReadString(encoding);
Map = reader.ReadString(encoding);
Port = reader.ReadUInt16_NBO();
}
}
public void ToStream(ref ByteOutputStream writer)
{
QueryHeader.ToStream(ref writer);
var start = (ushort)writer.GetBytePosition();
writer.WriteUInt32_NBO(0); // ChunkLen
var chunkStart = (uint)writer.GetBytePosition();
ServerInfoData.ToStream(ref writer);
ChunkLen = (uint)writer.GetBytePosition() - chunkStart;
QueryHeader.Length = (ushort)(writer.GetBytePosition() - start);
const int LengthSize = 2;
const int LengthOffset = 9;
const int ChunkLenSize = 4;
const int ChunkLenOffset = 11;
var length = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)QueryHeader.Length);
var chunkLen = (uint)System.Net.IPAddress.HostToNetworkOrder((int)ChunkLen);
writer.WriteBytesOffset(new byte[] { (byte)length, (byte)(length >> 8) }, 0, LengthOffset, LengthSize);
writer.WriteBytesOffset(new byte[] { (byte)chunkLen, (byte)(chunkLen >> 8), (byte)(chunkLen >> 16), (byte)(chunkLen >> 24) }, 0, ChunkLenOffset, ChunkLenSize);
}
public void FromStream(ref ByteInputStream reader)
{
QueryHeader.FromStream(ref reader);
ChunkLen = reader.ReadUInt32_NBO();
ServerInfoData.FromStream(ref reader);
}
static private Encoding encoding = new UTF8Encoding();
}
public static class UdpExtensions
{
public static SocketError SetupAndBind(this Socket socket, int port = 0)
{
SocketError error = SocketError.Success;
socket.Blocking = false;
var ep = new IPEndPoint(IPAddress.Any, port);
try
{
socket.Bind(ep);
}
catch (SocketException e)
{
error = e.SocketErrorCode;
throw e;
}
return error;
}
}
public class SQPClient
{
Socket m_Socket;
IPEndPoint m_Server;
byte[] m_Buffer = new byte[1472];
System.Net.EndPoint endpoint = new System.Net.IPEndPoint(0, 0);
uint ChallangeId;
long StartTime;
public enum SQPClientState
{
Idle,
WaitingForChallange,
WaitingForResponse,
Success,
Failure
}
SQPClientState m_State;
public SQPClientState ClientState
{
get { return m_State; }
}
public SQPClient(IPEndPoint server)
{
m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_Socket.SetupAndBind(0);
m_Server = server;
m_State = new SQPClientState();
}
public void StartInfoQuery()
{
Debug.Assert(m_State == SQPClientState.Idle);
StartTime = NetworkUtils.stopwatch.ElapsedMilliseconds;
var writer = new ByteOutputStream(m_Buffer);
var req = new ChallangeRequest();
req.ToStream(ref writer);
m_Socket.SendTo(m_Buffer, writer.GetBytePosition(), SocketFlags.None, m_Server);
m_State = SQPClientState.WaitingForChallange;
}
void SendServerInfoQuery()
{
StartTime = NetworkUtils.stopwatch.ElapsedMilliseconds;
var req = new QueryRequest();
req.Header.ChallangeId = ChallangeId;
req.RequestedChunks = (byte)SQPChunkType.ServerInfo;
var writer = new ByteOutputStream(m_Buffer);
req.ToStream(ref writer);
m_State = SQPClientState.WaitingForResponse;
m_Socket.SendTo(m_Buffer, writer.GetBytePosition(), SocketFlags.None, m_Server);
}
public void Update()
{
if (m_Socket.Poll(0, SelectMode.SelectRead))
{
int read = m_Socket.ReceiveFrom(m_Buffer, m_Buffer.Length, SocketFlags.None, ref endpoint);
if (read > 0)
{
var reader = new ByteInputStream(m_Buffer);
var header = new SQPHeader();
header.FromStream(ref reader);
switch (m_State)
{
case SQPClientState.Idle:
break;
case SQPClientState.WaitingForChallange:
if ((SQPMessageType)header.Type == SQPMessageType.ChallangeResponse)
{
if (endpoint.Equals(m_Server))
{
ChallangeId = header.ChallangeId;
SendServerInfoQuery();
}
}
break;
case SQPClientState.WaitingForResponse:
if ((SQPMessageType)header.Type == SQPMessageType.QueryResponse)
{
reader.Reset();
var rsp = new SQP.ServerInfo();
rsp.FromStream(ref reader);
Debug.Log(string.Format("ServerName: {0}, BuildId: {1}, Current Players: {2}, Max Players: {3}, GameType: {4}, Map: {5}, Port: {6}",
rsp.ServerInfoData.ServerName,
rsp.ServerInfoData.BuildId,
(ushort)rsp.ServerInfoData.CurrentPlayers,
(ushort)rsp.ServerInfoData.MaxPlayers,
rsp.ServerInfoData.GameType,
rsp.ServerInfoData.Map,
(ushort)rsp.ServerInfoData.Port));
m_State = SQPClientState.Success;
StartTime = NetworkUtils.stopwatch.ElapsedMilliseconds;
}
break;
default:
break;
}
}
}
var now = NetworkUtils.stopwatch.ElapsedMilliseconds;
if (now - StartTime > 1000000)
{
Debug.Log("Failed");
m_State = SQPClientState.Failure;
}
}
}
public class SQPServer
{
Socket m_Socket;
System.Random m_Random;
SQP.ServerInfo m_ServerInfo = new ServerInfo();
public SQP.ServerInfo.Data ServerInfoData { get; set; }
byte[] m_Buffer = new byte[1472];
System.Net.EndPoint endpoint = new System.Net.IPEndPoint(0, 0);
Dictionary<EndPoint, uint> m_OutstandingTokens = new Dictionary<EndPoint, uint>();
public SQPServer(int port)
{
m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_Socket.SetupAndBind(port);
m_Random = new System.Random();
ServerInfoData = new ServerInfo.Data();
m_ServerInfo.ServerInfoData = ServerInfoData;
}
public void Update()
{
if (m_Socket.Poll(0, SelectMode.SelectRead))
{
int read = m_Socket.ReceiveFrom(m_Buffer, m_Buffer.Length, SocketFlags.None, ref endpoint);
if (read > 0)
{
var reader = new ByteInputStream(m_Buffer);
var header = new SQPHeader();
header.FromStream(ref reader);
SQPMessageType type = (SQPMessageType)header.Type;
switch (type)
{
case SQPMessageType.ChallangeRequest:
{
if (!m_OutstandingTokens.ContainsKey(endpoint))
{
uint token = GetNextToken();
Debug.Log("token generated: " + token);
var writer = new ByteOutputStream(m_Buffer);
var rsp = new ChallangeResponse();
rsp.Header.ChallangeId = token;
rsp.ToStream(ref writer);
m_Socket.SendTo(m_Buffer, writer.GetBytePosition(), SocketFlags.None, endpoint);
m_OutstandingTokens.Add(endpoint, token);
}
}
break;
case SQPMessageType.QueryRequest:
{
uint token;
if (!m_OutstandingTokens.TryGetValue(endpoint, out token))
{
Debug.Log("Failed to find token!");
return;
}
m_OutstandingTokens.Remove(endpoint);
reader.Reset();
var req = new QueryRequest();
req.FromStream(ref reader);
if ((SQPChunkType)req.RequestedChunks == SQPChunkType.ServerInfo)
{
var rsp = m_ServerInfo;
var writer = new ByteOutputStream(m_Buffer);
rsp.QueryHeader.Header.ChallangeId = token;
rsp.ToStream(ref writer);
m_Socket.SendTo(m_Buffer, writer.GetBytePosition(), SocketFlags.None, endpoint);
}
}
break;
default:
break;
}
}
}
}
uint GetNextToken()
{
uint thirtyBits = (uint)m_Random.Next(1 << 30);
uint twoBits = (uint)m_Random.Next(1 << 2);
return (thirtyBits << 2) | twoBits;
}
}
}