first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
using System;
using System.Net;
using LiteNetLib;
using LiteNetLib.Utils;
using NitroxModel.Networking;
using NitroxModel.Packets;
namespace NitroxServer.Communication.LiteNetLib;
public class LiteNetLibConnection : INitroxConnection, IEquatable<LiteNetLibConnection>
{
private readonly NetDataWriter dataWriter = new();
private readonly NetPeer peer;
public IPEndPoint Endpoint => peer;
public NitroxConnectionState State => peer.ConnectionState.ToNitrox();
public LiteNetLibConnection(NetPeer peer)
{
this.peer = peer;
}
public void SendPacket(Packet packet)
{
if (peer.ConnectionState == ConnectionState.Connected)
{
byte[] packetData = packet.Serialize();
dataWriter.Reset();
dataWriter.Put(packetData.Length);
dataWriter.Put(packetData);
peer.Send(dataWriter, (byte)packet.UdpChannel, NitroxDeliveryMethod.ToLiteNetLib(packet.DeliveryMethod));
}
else
{
Log.Warn($"Cannot send packet {packet?.GetType()} to a closed connection {peer as IPEndPoint}");
}
}
public static bool operator ==(LiteNetLibConnection left, LiteNetLibConnection right)
{
return Equals(left, right);
}
public static bool operator !=(LiteNetLibConnection left, LiteNetLibConnection right)
{
return !Equals(left, right);
}
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((LiteNetLibConnection)obj);
}
public override int GetHashCode()
{
return peer?.Id.GetHashCode() ?? 0;
}
public bool Equals(LiteNetLibConnection other)
{
return peer?.Id == other?.peer?.Id;
}
}

View File

@@ -0,0 +1,166 @@
using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
using LiteNetLib;
using LiteNetLib.Utils;
using Mono.Nat;
using NitroxModel.Helper;
using NitroxModel.Packets;
using NitroxModel.Serialization;
using NitroxServer.Communication.Packets;
using NitroxServer.GameLogic;
using NitroxServer.GameLogic.Entities;
namespace NitroxServer.Communication.LiteNetLib;
public class LiteNetLibServer : NitroxServer
{
private readonly EventBasedNetListener listener;
private readonly NetManager server;
public LiteNetLibServer(PacketHandler packetHandler, PlayerManager playerManager, EntitySimulation entitySimulation, SubnauticaServerConfig serverConfig) : base(packetHandler, playerManager, entitySimulation, serverConfig)
{
listener = new EventBasedNetListener();
server = new NetManager(listener);
}
public override bool Start(CancellationToken ct = default)
{
listener.PeerConnectedEvent += PeerConnected;
listener.PeerDisconnectedEvent += PeerDisconnected;
listener.NetworkReceiveEvent += NetworkDataReceived;
listener.ConnectionRequestEvent += OnConnectionRequest;
server.ChannelsCount = (byte)typeof(Packet.UdpChannelId).GetEnumValues().Length;
server.BroadcastReceiveEnabled = true;
server.UnconnectedMessagesEnabled = true;
server.UpdateTime = 15;
server.UnsyncedEvents = true;
#if DEBUG
server.DisconnectTimeout = 300000; //Disables Timeout (for 5 min) for debug purpose (like if you jump though the server code)
#endif
if (!server.Start(portNumber))
{
return false;
}
if (useUpnpPortForwarding)
{
_ = PortForwardAsync((ushort)portNumber, ct);
}
if (useLANBroadcast)
{
LANBroadcastServer.Start(ct);
}
return true;
}
private async Task PortForwardAsync(ushort port, CancellationToken ct = default)
{
if (await NatHelper.GetPortMappingAsync(port, Protocol.Udp, ct) != null)
{
Log.Info($"Port {port} UDP is already port forwarded");
return;
}
NatHelper.ResultCodes mappingResult = await NatHelper.AddPortMappingAsync(port, Protocol.Udp, ct);
if (!ct.IsCancellationRequested)
{
switch (mappingResult)
{
case NatHelper.ResultCodes.SUCCESS:
Log.Info($"Server port {port} UDP has been automatically opened on your router (port is closed when server closes)");
break;
case NatHelper.ResultCodes.CONFLICT_IN_MAPPING_ENTRY:
Log.Warn($"Port forward for {port} UDP failed. It appears to already be port forwarded or it conflicts with another port forward rule.");
break;
case NatHelper.ResultCodes.UNKNOWN_ERROR:
Log.Warn($"Failed to port forward {port} UDP through UPnP. If using Hamachi or you've manually port-forwarded, please disregard this warning. To disable this feature you can go into the server settings.");
break;
}
}
}
public override void Stop()
{
if (!server.IsRunning)
{
return;
}
playerManager.SendPacketToAllPlayers(new ServerStopped());
// We want every player to receive this packet
Thread.Sleep(500);
server.Stop();
if (useUpnpPortForwarding)
{
if (NatHelper.DeletePortMappingAsync((ushort)portNumber, Protocol.Udp, CancellationToken.None).GetAwaiter().GetResult())
{
Log.Debug($"Port forward rule removed for {portNumber} UDP");
}
else
{
Log.Warn($"Failed to remove port forward rule {portNumber} UDP");
}
}
if (useLANBroadcast)
{
LANBroadcastServer.Stop();
}
}
public void OnConnectionRequest(ConnectionRequest request)
{
if (server.ConnectedPeersCount < maxConnections)
{
request.AcceptIfKey("nitrox");
}
else
{
request.Reject();
}
}
private void PeerConnected(NetPeer peer)
{
LiteNetLibConnection connection = new(peer);
lock (connectionsByRemoteIdentifier)
{
connectionsByRemoteIdentifier[peer.Id] = connection;
}
}
private void PeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
{
ClientDisconnected(GetConnection(peer.Id));
}
private void NetworkDataReceived(NetPeer peer, NetDataReader reader, byte channel, DeliveryMethod deliveryMethod)
{
int packetDataLength = reader.GetInt();
byte[] packetData = ArrayPool<byte>.Shared.Rent(packetDataLength);
try
{
reader.GetBytes(packetData, packetDataLength);
Packet packet = Packet.Deserialize(packetData);
INitroxConnection connection = GetConnection(peer.Id);
ProcessIncomingData(connection, packet);
}
finally
{
ArrayPool<byte>.Shared.Return(packetData, true);
}
}
private INitroxConnection GetConnection(int remoteIdentifier)
{
INitroxConnection connection;
lock (connectionsByRemoteIdentifier)
{
connectionsByRemoteIdentifier.TryGetValue(remoteIdentifier, out connection);
}
return connection;
}
}