Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop
This commit is contained in:
@@ -7,22 +7,25 @@ namespace Steamworks
|
||||
public byte[] Data;
|
||||
public uint Handle;
|
||||
|
||||
/// <summary>
|
||||
/// Cancels a ticket.
|
||||
/// You should cancel your ticket when you close the game or leave a server.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
if ( Handle != 0 )
|
||||
{
|
||||
SteamUser.Internal.CancelAuthTicket( Handle );
|
||||
}
|
||||
public bool Canceled { get; private set; }
|
||||
|
||||
Handle = 0;
|
||||
Data = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Cancels a ticket.
|
||||
/// You should cancel your ticket when you close the game or leave a server.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
if (Handle != 0)
|
||||
{
|
||||
SteamUser.Internal.CancelAuthTicket(Handle);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
Handle = 0;
|
||||
Data = null;
|
||||
Canceled = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2019 Riley Labrecque
|
||||
Copyright (c) 2013-2022 Riley Labrecque
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,48 +25,60 @@ THE SOFTWARE.
|
||||
**/
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Steamworks
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: Callback interface for receiving responses after pinging an individual server
|
||||
// Purpose: Callback interface for receiving responses after requesting rules
|
||||
// details on a particular server.
|
||||
//
|
||||
// These callbacks all occur in response to querying an individual server
|
||||
// via the ISteamMatchmakingServers()->PingServer() call below. If you are
|
||||
// destructing an object that implements this interface then you should call
|
||||
// via the ISteamMatchmakingServers()->ServerRules() call below. If you are
|
||||
// destructing an object that implements this interface then you should call
|
||||
// ISteamMatchmakingServers()->CancelServerQuery() passing in the handle to the query
|
||||
// which is in progress. Failure to cancel in progress queries when destructing
|
||||
// a callback handler may result in a crash when a callback later occurs.
|
||||
//-----------------------------------------------------------------------------
|
||||
public class SteamMatchmakingPingResponse
|
||||
public class SteamMatchmakingRulesResponse
|
||||
{
|
||||
// Server has responded successfully and has updated data
|
||||
public delegate void ServerResponded(Steamworks.Data.ServerInfo server);
|
||||
// Got data on a rule on the server -- you'll get one of these per rule defined on
|
||||
// the server you are querying
|
||||
public delegate void RulesResponded(string pchRule, string pchValue);
|
||||
|
||||
// Server failed to respond to the ping request
|
||||
public delegate void ServerFailedToRespond();
|
||||
// The server failed to respond to the request for rule details
|
||||
public delegate void RulesFailedToRespond();
|
||||
|
||||
private VTable m_VTable;
|
||||
private IntPtr m_pVTable;
|
||||
// The server has finished responding to the rule details request
|
||||
// (ie, you won't get anymore RulesResponded callbacks)
|
||||
public delegate void RulesRefreshComplete();
|
||||
|
||||
private readonly VTable m_VTable;
|
||||
private readonly IntPtr m_pVTable;
|
||||
private GCHandle m_pGCHandle;
|
||||
private ServerResponded m_ServerResponded;
|
||||
private ServerFailedToRespond m_ServerFailedToRespond;
|
||||
private readonly RulesResponded m_RulesResponded;
|
||||
private readonly RulesFailedToRespond m_RulesFailedToRespond;
|
||||
private readonly RulesRefreshComplete m_RulesRefreshComplete;
|
||||
|
||||
public SteamMatchmakingPingResponse(ServerResponded onServerResponded, ServerFailedToRespond onServerFailedToRespond)
|
||||
public SteamMatchmakingRulesResponse(
|
||||
RulesResponded onRulesResponded,
|
||||
RulesFailedToRespond onRulesFailedToRespond,
|
||||
RulesRefreshComplete onRulesRefreshComplete)
|
||||
{
|
||||
if (onServerResponded == null || onServerFailedToRespond == null)
|
||||
if (onRulesResponded == null || onRulesFailedToRespond == null || onRulesRefreshComplete == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
m_ServerResponded = onServerResponded;
|
||||
m_ServerFailedToRespond = onServerFailedToRespond;
|
||||
|
||||
m_RulesResponded = onRulesResponded;
|
||||
m_RulesFailedToRespond = onRulesFailedToRespond;
|
||||
m_RulesRefreshComplete = onRulesRefreshComplete;
|
||||
|
||||
m_VTable = new VTable()
|
||||
{
|
||||
m_VTServerResponded = InternalOnServerResponded,
|
||||
m_VTServerFailedToRespond = InternalOnServerFailedToRespond,
|
||||
m_VTRulesResponded = InternalOnRulesResponded,
|
||||
m_VTRulesFailedToRespond = InternalOnRulesFailedToRespond,
|
||||
m_VTRulesRefreshComplete = InternalOnRulesRefreshComplete
|
||||
};
|
||||
m_pVTable = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VTable)));
|
||||
Marshal.StructureToPtr(m_VTable, m_pVTable, false);
|
||||
@@ -74,21 +86,7 @@ namespace Steamworks
|
||||
m_pGCHandle = GCHandle.Alloc(m_pVTable, GCHandleType.Pinned);
|
||||
}
|
||||
|
||||
private Data.HServerQuery hserverPing = 0;
|
||||
public bool QueryActive { get { return hserverPing != 0; } }
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
if (hserverPing != 0) { ServerList.Base.Internal.CancelServerQuery(hserverPing); }
|
||||
hserverPing = 0;
|
||||
}
|
||||
|
||||
public void HQueryPing(IPAddress ip, int port)
|
||||
{
|
||||
hserverPing = ServerList.Base.Internal.PingServer(ip.IpToInt32(), (ushort)port, (IntPtr)this);
|
||||
}
|
||||
|
||||
~SteamMatchmakingPingResponse()
|
||||
~SteamMatchmakingRulesResponse()
|
||||
{
|
||||
if (m_pVTable != IntPtr.Zero)
|
||||
{
|
||||
@@ -102,48 +100,69 @@ namespace Steamworks
|
||||
}
|
||||
|
||||
#if NOTHISPTR
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
private delegate void InternalServerResponded(gameserveritem_t server);
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
private delegate void InternalServerFailedToRespond();
|
||||
private void InternalOnServerResponded(gameserveritem_t server) {
|
||||
m_ServerResponded(server);
|
||||
}
|
||||
private void InternalOnServerFailedToRespond() {
|
||||
m_ServerFailedToRespond();
|
||||
}
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
public delegate void InternalRulesResponded(IntPtr pchRule, IntPtr pchValue);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
public delegate void InternalRulesFailedToRespond();
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
public delegate void InternalRulesRefreshComplete();
|
||||
|
||||
private void InternalOnRulesResponded(IntPtr pchRule, IntPtr pchValue)
|
||||
{
|
||||
m_RulesResponded(Helpers.MemoryToString(pchRule), Helpers.MemoryToString(pchValue));
|
||||
}
|
||||
|
||||
private void InternalOnRulesFailedToRespond()
|
||||
{
|
||||
m_RulesFailedToRespond();
|
||||
}
|
||||
|
||||
private void InternalOnRulesRefreshComplete()
|
||||
{
|
||||
m_RulesRefreshComplete();
|
||||
}
|
||||
#else
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate void InternalServerResponded(IntPtr thisptr, Steamworks.Data.gameserveritem_t server);
|
||||
public delegate void InternalRulesResponded(IntPtr thisptr, IntPtr pchRule, IntPtr pchValue);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate void InternalServerFailedToRespond(IntPtr thisptr);
|
||||
private void InternalOnServerResponded(IntPtr thisptr, Steamworks.Data.gameserveritem_t server)
|
||||
{
|
||||
hserverPing = 0;
|
||||
public delegate void InternalRulesFailedToRespond(IntPtr thisptr);
|
||||
|
||||
m_ServerResponded(Steamworks.Data.ServerInfo.From(server));
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
public delegate void InternalRulesRefreshComplete(IntPtr thisptr);
|
||||
|
||||
private void InternalOnRulesResponded(IntPtr thisptr, IntPtr pchRule, IntPtr pchValue)
|
||||
{
|
||||
m_RulesResponded(Helpers.MemoryToString(pchRule), Helpers.MemoryToString(pchValue));
|
||||
}
|
||||
private void InternalOnServerFailedToRespond(IntPtr thisptr)
|
||||
{
|
||||
hserverPing = 0;
|
||||
|
||||
m_ServerFailedToRespond();
|
||||
private void InternalOnRulesFailedToRespond(IntPtr thisptr)
|
||||
{
|
||||
m_RulesFailedToRespond();
|
||||
}
|
||||
|
||||
private void InternalOnRulesRefreshComplete(IntPtr thisptr)
|
||||
{
|
||||
m_RulesRefreshComplete();
|
||||
}
|
||||
#endif
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private class VTable
|
||||
{
|
||||
[NonSerialized]
|
||||
[MarshalAs(UnmanagedType.FunctionPtr)]
|
||||
public InternalServerResponded m_VTServerResponded;
|
||||
[NonSerialized] [MarshalAs(UnmanagedType.FunctionPtr)]
|
||||
public InternalRulesResponded m_VTRulesResponded;
|
||||
|
||||
[NonSerialized]
|
||||
[MarshalAs(UnmanagedType.FunctionPtr)]
|
||||
public InternalServerFailedToRespond m_VTServerFailedToRespond;
|
||||
[NonSerialized] [MarshalAs(UnmanagedType.FunctionPtr)]
|
||||
public InternalRulesFailedToRespond m_VTRulesFailedToRespond;
|
||||
|
||||
[NonSerialized] [MarshalAs(UnmanagedType.FunctionPtr)]
|
||||
public InternalRulesRefreshComplete m_VTRulesRefreshComplete;
|
||||
}
|
||||
|
||||
public static explicit operator System.IntPtr(SteamMatchmakingPingResponse that)
|
||||
public static explicit operator System.IntPtr(SteamMatchmakingRulesResponse that)
|
||||
{
|
||||
return that.m_pGCHandle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
@@ -1,209 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Steamworks.Data;
|
||||
|
||||
namespace Steamworks
|
||||
{
|
||||
internal static class SourceServerQuery
|
||||
{
|
||||
private static readonly byte[] A2S_SERVERQUERY_GETCHALLENGE = { 0x55, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
// private static readonly byte A2S_PLAYER = 0x55;
|
||||
private const byte A2S_RULES = 0x56;
|
||||
|
||||
private static readonly Dictionary<IPEndPoint, Task<Dictionary<string, string>>> PendingQueries =
|
||||
new Dictionary<IPEndPoint, Task<Dictionary<string, string>>>();
|
||||
|
||||
private static HashSet<int> activeRequests = new HashSet<int>();
|
||||
private static int lastRequestId = 0;
|
||||
|
||||
internal static Task<Dictionary<string, string>> GetRules( ServerInfo server )
|
||||
{
|
||||
var endpoint = new IPEndPoint(server.Address, server.QueryPort);
|
||||
|
||||
lock (PendingQueries)
|
||||
{
|
||||
if (PendingQueries.TryGetValue(endpoint, out var pending))
|
||||
return pending;
|
||||
|
||||
var task = GetRulesImpl( endpoint )
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
lock (PendingQueries)
|
||||
{
|
||||
PendingQueries.Remove(endpoint);
|
||||
}
|
||||
|
||||
return t;
|
||||
})
|
||||
.Unwrap();
|
||||
|
||||
PendingQueries.Add(endpoint, task);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<Dictionary<string, string>> GetRulesImpl( IPEndPoint endpoint )
|
||||
{
|
||||
int currId;
|
||||
lock (activeRequests)
|
||||
{
|
||||
lastRequestId++;
|
||||
currId = lastRequestId;
|
||||
activeRequests.Add(currId);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Yield();
|
||||
while (true)
|
||||
{
|
||||
lock (activeRequests)
|
||||
{
|
||||
if (!activeRequests.Any() || (currId - activeRequests.Min()) < 25) { break; }
|
||||
}
|
||||
await Task.Delay(25);
|
||||
}
|
||||
|
||||
using (var client = new UdpClient())
|
||||
{
|
||||
client.Client.SendTimeout = 3000;
|
||||
client.Client.ReceiveTimeout = 3000;
|
||||
client.Connect(endpoint);
|
||||
|
||||
return await GetRules(client);
|
||||
}
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
//Console.Error.WriteLine( e.Message );
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (activeRequests)
|
||||
{
|
||||
activeRequests.Remove(currId);
|
||||
}
|
||||
}
|
||||
private enum Status
|
||||
{
|
||||
Pending,
|
||||
Failure,
|
||||
Success
|
||||
}
|
||||
|
||||
static async Task<Dictionary<string, string>> GetRules( UdpClient client )
|
||||
private static readonly HashSet<SteamMatchmakingRulesResponse> ruleResponseHandlers
|
||||
= new HashSet<SteamMatchmakingRulesResponse>();
|
||||
|
||||
internal static async Task<Dictionary<string, string>> GetRules(Steamworks.Data.ServerInfo server)
|
||||
{
|
||||
var challengeBytes = await GetChallengeData( client );
|
||||
challengeBytes[0] = A2S_RULES;
|
||||
await Send( client, challengeBytes );
|
||||
var ruleData = await Receive( client );
|
||||
Status status = Status.Pending;
|
||||
|
||||
var rules = new Dictionary<string, string>();
|
||||
|
||||
using ( var br = new BinaryReader( new MemoryStream( ruleData ) ) )
|
||||
{
|
||||
if ( br.ReadByte() != 0x45 )
|
||||
throw new Exception( "Invalid data received in response to A2S_RULES request" );
|
||||
SteamMatchmakingRulesResponse responseHandler = null;
|
||||
|
||||
var numRules = br.ReadUInt16();
|
||||
for ( int index = 0; index < numRules; index++ )
|
||||
{
|
||||
rules.Add( br.ReadNullTerminatedUTF8String(), br.ReadNullTerminatedUTF8String() );
|
||||
}
|
||||
void onRulesResponded(string key, string value)
|
||||
=> rules.Add(key, value);
|
||||
|
||||
void onRulesFailToRespond()
|
||||
{
|
||||
finish(Status.Failure);
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static async Task<byte[]> Receive( UdpClient client )
|
||||
{
|
||||
byte[][] packets = null;
|
||||
byte packetNumber = 0, packetCount = 1;
|
||||
|
||||
do
|
||||
void onRulesRefreshComplete()
|
||||
{
|
||||
Task<UdpReceiveResult> result = client.ReceiveAsync();
|
||||
await Task.WhenAny(result, Task.Delay(3000));
|
||||
if (!result.IsCompleted)
|
||||
{
|
||||
throw new Exception("Receive timed out");
|
||||
}
|
||||
var buffer = result.Result.Buffer;
|
||||
|
||||
using ( var br = new BinaryReader( new MemoryStream( buffer ) ) )
|
||||
{
|
||||
var header = br.ReadInt32();
|
||||
|
||||
if ( header == -1 )
|
||||
{
|
||||
var unsplitdata = new byte[buffer.Length - br.BaseStream.Position];
|
||||
Buffer.BlockCopy( buffer, (int)br.BaseStream.Position, unsplitdata, 0, unsplitdata.Length );
|
||||
return unsplitdata;
|
||||
}
|
||||
else if ( header == -2 )
|
||||
{
|
||||
int requestId = br.ReadInt32();
|
||||
packetNumber = br.ReadByte();
|
||||
packetCount = br.ReadByte();
|
||||
int splitSize = br.ReadInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new System.Exception( "Invalid Header" );
|
||||
}
|
||||
|
||||
if ( packets == null ) packets = new byte[packetCount][];
|
||||
|
||||
var data = new byte[buffer.Length - br.BaseStream.Position];
|
||||
Buffer.BlockCopy( buffer, (int)br.BaseStream.Position, data, 0, data.Length );
|
||||
packets[packetNumber] = data;
|
||||
}
|
||||
finish(Status.Success);
|
||||
}
|
||||
while ( packets.Any( p => p == null ) );
|
||||
|
||||
var combinedData = Combine( packets );
|
||||
return combinedData;
|
||||
}
|
||||
|
||||
private static async Task<byte[]> GetChallengeData( UdpClient client )
|
||||
{
|
||||
await Send( client, A2S_SERVERQUERY_GETCHALLENGE );
|
||||
|
||||
var challengeData = await Receive( client );
|
||||
|
||||
if ( challengeData[0] != 0x41 )
|
||||
throw new Exception( "Invalid Challenge" );
|
||||
|
||||
return challengeData;
|
||||
}
|
||||
|
||||
static async Task Send( UdpClient client, byte[] message )
|
||||
{
|
||||
var sendBuffer = new byte[message.Length + 4];
|
||||
|
||||
sendBuffer[0] = 0xFF;
|
||||
sendBuffer[1] = 0xFF;
|
||||
sendBuffer[2] = 0xFF;
|
||||
sendBuffer[3] = 0xFF;
|
||||
|
||||
Buffer.BlockCopy( message, 0, sendBuffer, 4, message.Length );
|
||||
|
||||
await client.SendAsync( sendBuffer, message.Length + 4 );
|
||||
}
|
||||
|
||||
static byte[] Combine( byte[][] arrays )
|
||||
{
|
||||
var rv = new byte[arrays.Sum( a => a.Length )];
|
||||
int offset = 0;
|
||||
foreach ( byte[] array in arrays )
|
||||
void finish(Status stat)
|
||||
{
|
||||
Buffer.BlockCopy( array, 0, rv, offset, array.Length );
|
||||
offset += array.Length;
|
||||
if (status == Status.Pending) { status = stat; }
|
||||
|
||||
var handler = responseHandler;
|
||||
if (handler is null) { return; }
|
||||
|
||||
lock (ruleResponseHandlers)
|
||||
{
|
||||
ruleResponseHandlers.Remove(handler);
|
||||
}
|
||||
responseHandler = null;
|
||||
}
|
||||
return rv;
|
||||
|
||||
responseHandler = new SteamMatchmakingRulesResponse(
|
||||
onRulesResponded,
|
||||
onRulesFailToRespond,
|
||||
onRulesRefreshComplete);
|
||||
lock (ruleResponseHandlers)
|
||||
{
|
||||
ruleResponseHandlers.Add(responseHandler);
|
||||
}
|
||||
|
||||
var query = SteamMatchmakingServers.Internal.ServerRules(
|
||||
server.AddressRaw, (ushort)server.QueryPort, (IntPtr)responseHandler);
|
||||
|
||||
while (status == Status.Pending)
|
||||
{
|
||||
await Task.Delay(25);
|
||||
}
|
||||
|
||||
SteamMatchmakingServers.Internal.CancelServerQuery(query);
|
||||
|
||||
return status == Status.Success ? rules : null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -124,7 +124,24 @@ namespace Lidgren.Network
|
||||
m_lastSocketBind = now;
|
||||
|
||||
if (m_socket == null)
|
||||
m_socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
|
||||
}
|
||||
catch (SocketException socketException)
|
||||
{
|
||||
if (socketException.SocketErrorCode == SocketError.AddressFamilyNotSupported)
|
||||
{
|
||||
// Fall back to IPv4 if IPv6 is unsupported
|
||||
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reBind)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1);
|
||||
@@ -132,9 +149,9 @@ namespace Lidgren.Network
|
||||
m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize;
|
||||
m_socket.SendBufferSize = m_configuration.SendBufferSize;
|
||||
m_socket.Blocking = false;
|
||||
m_socket.DualMode = m_configuration.UseDualModeSockets;
|
||||
if (m_socket.AddressFamily == AddressFamily.InterNetworkV6) { m_socket.DualMode = m_configuration.UseDualModeSockets; }
|
||||
|
||||
var ep = (EndPoint)new NetEndPoint(m_configuration.LocalAddress.MapToIPv6(), reBind ? m_listenPort : m_configuration.Port);
|
||||
var ep = (EndPoint)new NetEndPoint(m_configuration.LocalAddress.MapToFamily(m_socket.AddressFamily), reBind ? m_listenPort : m_configuration.Port);
|
||||
m_socket.Bind(ep);
|
||||
|
||||
try
|
||||
@@ -413,6 +430,10 @@ namespace Lidgren.Network
|
||||
int bytesReceived = 0;
|
||||
try
|
||||
{
|
||||
if (m_senderRemote is IPEndPoint ipEndpoint && ipEndpoint.AddressFamily != m_socket.AddressFamily)
|
||||
{
|
||||
m_senderRemote = ipEndpoint.MapToFamily(m_socket.AddressFamily);
|
||||
}
|
||||
bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote);
|
||||
}
|
||||
catch (SocketException sx)
|
||||
|
||||
@@ -136,7 +136,7 @@ namespace Lidgren.Network
|
||||
{
|
||||
connectionReset = false;
|
||||
|
||||
target = NetUtility.MapToIPv6(target);
|
||||
target = target.MapToFamily(m_socket.AddressFamily);
|
||||
|
||||
IPAddress ba = default(IPAddress);
|
||||
try
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace Lidgren.Network
|
||||
if (remoteEndPoint == null)
|
||||
throw new ArgumentNullException("remoteEndPoint");
|
||||
|
||||
remoteEndPoint = NetUtility.MapToIPv6(remoteEndPoint);
|
||||
remoteEndPoint = remoteEndPoint.MapToFamily(m_socket.AddressFamily);
|
||||
|
||||
lock (m_connections)
|
||||
{
|
||||
|
||||
@@ -454,16 +454,24 @@ namespace Lidgren.Network
|
||||
return ComputeSHAHash(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the IPEndPoint object to an IPv6 address, if it is currently mapped to an IPv4 address.
|
||||
/// </summary>
|
||||
internal static IPEndPoint MapToIPv6(IPEndPoint endPoint)
|
||||
{
|
||||
if (endPoint.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
return new IPEndPoint(endPoint.Address.MapToIPv6(), endPoint.Port);
|
||||
}
|
||||
return endPoint;
|
||||
}
|
||||
internal static IPAddress MapToFamily(this IPAddress address, AddressFamily family)
|
||||
{
|
||||
switch (family)
|
||||
{
|
||||
case AddressFamily.InterNetworkV6:
|
||||
return address.MapToIPv6();
|
||||
case AddressFamily.InterNetwork:
|
||||
return address.MapToIPv4();
|
||||
default:
|
||||
throw new Exception($"Unsupported address family: {family}");
|
||||
}
|
||||
}
|
||||
|
||||
internal static IPEndPoint MapToFamily(this IPEndPoint endpoint, AddressFamily family)
|
||||
{
|
||||
return endpoint.Address.AddressFamily == family
|
||||
? endpoint
|
||||
: new IPEndPoint(endpoint.Address.MapToFamily(family), endpoint.Port);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,22 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
public interface ISpriteBatch
|
||||
{
|
||||
public void Draw(Texture2D texture,
|
||||
Vector2 position,
|
||||
Rectangle? sourceRectangle,
|
||||
Color color,
|
||||
float rotation,
|
||||
Vector2 origin,
|
||||
Vector2 scale,
|
||||
SpriteEffects effects,
|
||||
float layerDepth);
|
||||
public void Draw(
|
||||
Texture2D texture,
|
||||
Vector2 position,
|
||||
Rectangle? sourceRectangle,
|
||||
Color color,
|
||||
float rotation,
|
||||
Vector2 origin,
|
||||
Vector2 scale,
|
||||
SpriteEffects effects,
|
||||
float layerDepth);
|
||||
|
||||
public void Draw(
|
||||
Texture2D texture,
|
||||
VertexPositionColorTexture[] vertices,
|
||||
float layerDepth,
|
||||
int? count = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user