Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Source/Client/MultiplayerStatic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using HarmonyLib;
using LiteNetLib;
using Multiplayer.Client.Networking;
using Multiplayer.Client.Patches;
using Multiplayer.Client.Util;
using Multiplayer.Common;
Expand Down Expand Up @@ -60,12 +60,15 @@ static MultiplayerStatic()
// UnityEngine.Debug.Log instead of Verse.Log.Message because the server runs on its own thread
ServerLog.info = str => Debug.Log($"MpServerLog: {str}");
ServerLog.error = str => Debug.Log($"MpServerLog Error: {str}");
NetDebug.Logger = new ServerLog();
LiteNetLogger.Install();

SetUsername();

if (SteamManager.Initialized)
{
SteamIntegration.InitCallbacks();
SteamP2PIntegration.InitCallbacks();
}

Log.Message($"Multiplayer version {MpVersion.Version}");
Log.Message($"Player's username: {Multiplayer.username}");
Expand Down Expand Up @@ -305,7 +308,7 @@ void TryPatch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod po
Assembly.GetCallingAssembly().GetTypes().Do(type => {
// EarlyPatches are handled in MultiplayerMod.EarlyPatches
if (type.IsDefined(typeof(EarlyPatchAttribute))) return;

var harmonyAttributes = HarmonyMethodExtensions.GetFromType(type);
if (harmonyAttributes is null || harmonyAttributes.Count == 0) return;

Expand Down Expand Up @@ -333,7 +336,7 @@ void TryPatch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod po
foreach (Type t in typeof(Designator).AllSubtypesAndSelf()
// Designator_MechControlGroup Opens float menu, sync that instead
// Designator_Plan_CopySelection creates the placement gizmo, this shouldn't be synced
.Except([typeof(Designator_MechControlGroup), typeof(Designator_Plan_CopySelection)]))
.Except([typeof(Designator_MechControlGroup), typeof(Designator_Plan_CopySelection)]))
{
foreach ((string m, Type[] args) in designatorMethods)
{
Expand Down
41 changes: 9 additions & 32 deletions Source/Client/Networking/ClientUtil.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using LiteNetLib;
using Multiplayer.Common;
using Steamworks;
using System;
using Verse;
using Multiplayer.Client.Networking;

namespace Multiplayer.Client
{
public interface ITickableConnection
{
public void Tick();
}

public static class ClientUtil
{
public static void TryConnectWithWindow(string address, int port, bool returnToServerBrowser = true)
Expand All @@ -19,18 +22,10 @@ public static void TryConnectWithWindow(string address, int port, bool returnToS
port = port
};

NetManager netClient = new NetManager(new MpClientNetListener())
{
EnableStatistics = true,
IPv6Enabled = MpUtil.SupportsIPv6() ? IPv6Mode.SeparateSocket : IPv6Mode.Disabled
};

netClient.Start();
netClient.ReconnectDelay = 300;
netClient.MaxConnectAttempts = 8;

Multiplayer.session.netClient = netClient;
netClient.Connect(address, port, "");
var conn = ClientLiteNetConnection.Connect(address, port);
conn.username = Multiplayer.username;
Multiplayer.session.client = conn;
Multiplayer.session.ReapplyPrefs();
}

public static void TrySteamConnectWithWindow(CSteamID user, bool returnToServerBrowser = true)
Expand All @@ -48,23 +43,5 @@ public static void TrySteamConnectWithWindow(CSteamID user, bool returnToServerB
Multiplayer.session.ReapplyPrefs();
Multiplayer.Client.ChangeState(ConnectionStateEnum.ClientSteam);
}

public static void HandleReceive(ByteReader data, bool reliable)
{
try
{
Multiplayer.Client.HandleReceiveRaw(data, reliable);
}
catch (Exception e)
{
Log.Error($"Exception handling packet by {Multiplayer.Client}: {e}");

Multiplayer.session.disconnectInfo.titleTranslated = "MpPacketErrorLocal".Translate();

ConnectionStatusListeners.TryNotifyAll_Disconnected();
Multiplayer.StopMultiplayer();
}
}
}

}
2 changes: 1 addition & 1 deletion Source/Client/Networking/HostUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private static void PrepareLocalServer(ServerSettings settings, bool fromReplay)
localServer.worldData.spectatorFactionId = Multiplayer.WorldComp.spectatorFaction.loadID;

if (settings.steam)
localServer.TickEvent += SteamIntegration.ServerSteamNetTick;
localServer.TickEvent += SteamP2PIntegration.ServerSteamNetTick;

if (fromReplay)
{
Expand Down
135 changes: 100 additions & 35 deletions Source/Client/Networking/NetworkingLiteNet.cs
Original file line number Diff line number Diff line change
@@ -1,68 +1,133 @@
using System;
using LiteNetLib;
using Multiplayer.Common;
using System.Net;
using System.Net.Sockets;
using Multiplayer.Client.Util;
using Verse;

namespace Multiplayer.Client.Networking
{

public class MpClientNetListener : INetEventListener
public class ClientLiteNetConnection : LiteNetConnection, ITickableConnection
{
public void OnPeerConnected(NetPeer peer)
{
ConnectionBase conn = new LiteNetConnection(peer);
conn.username = Multiplayer.username;
conn.ChangeState(ConnectionStateEnum.ClientJoining);
private readonly NetManager netManager;

Multiplayer.session.client = conn;
Multiplayer.session.ReapplyPrefs();
private ClientLiteNetConnection(NetPeer peer, NetManager netManager) : base(peer) =>
this.netManager = netManager;

MpLog.Log("Net client connected");
~ClientLiteNetConnection()
{
if (netManager.IsRunning)
{
Log.Error("[ClientLiteNetConnection] NetManager did not get stopped");
netManager.Stop();
}
}

public void OnNetworkError(IPEndPoint endPoint, SocketError error)
public static ClientLiteNetConnection Connect(string address, int port)
{
MpLog.Warn($"Net client error {error}");
var netClient = new NetManager(new NetListener())
{
EnableStatistics = true,
IPv6Enabled = MpUtil.SupportsIPv6() ? IPv6Mode.SeparateSocket : IPv6Mode.Disabled,
ReconnectDelay = 300,
MaxConnectAttempts = 8
};
netClient.Start();
var peer = netClient.Connect(address, port, "");
var conn = new ClientLiteNetConnection(peer, netClient);
peer.SetConnection(conn);
return conn;
}

public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod method)
public void Tick() => netManager.PollEvents();

public void OnDisconnect(MpDisconnectReason reason, ByteReader data)
{
byte[] data = reader.GetRemainingBytes();
ClientUtil.HandleReceive(new ByteReader(data), method == DeliveryMethod.ReliableOrdered);
Multiplayer.session.ProcessDisconnectPacket(reason, data);
ConnectionStatusListeners.TryNotifyAll_Disconnected();
Multiplayer.StopMultiplayer();
}

public void OnPeerDisconnected(NetPeer peer, DisconnectInfo info)
public override void Close(MpDisconnectReason reason, byte[] data)
{
MpDisconnectReason reason;
ByteReader reader;
base.Close(reason, data);
netManager.Stop();
}

if (info.AdditionalData.IsNull)
private class NetListener : INetEventListener
{
private ClientLiteNetConnection GetConnection(NetPeer peer) =>
peer.GetConnection() as ClientLiteNetConnection ?? throw new Exception("Can't get connection");

public void OnPeerConnected(NetPeer peer)
{
GetConnection(peer).ChangeState(ConnectionStateEnum.ClientJoining);
MpLog.Log("Net client connected");
}

public void OnNetworkError(IPEndPoint endPoint, SocketError error)
{
if (info.Reason is DisconnectReason.DisconnectPeerCalled or DisconnectReason.RemoteConnectionClose)
reason = MpDisconnectReason.Generic;
else if (Multiplayer.Client == null)
reason = MpDisconnectReason.ConnectingFailed;
MpLog.Warn($"Net client error {error}");
}

public void OnNetworkReceive(NetPeer peer, NetPacketReader reader, DeliveryMethod method)
{
byte[] data = reader.GetRemainingBytes();
GetConnection(peer).HandleReceiveRaw(new ByteReader(data), method == DeliveryMethod.ReliableOrdered);
}

public void OnPeerDisconnected(NetPeer peer, DisconnectInfo info)
{
MpDisconnectReason reason;
ByteReader reader;

if (info.AdditionalData.IsNull)
{
if (info.Reason is DisconnectReason.DisconnectPeerCalled or DisconnectReason.RemoteConnectionClose)
reason = MpDisconnectReason.Generic;
else if (Multiplayer.Client == null)
reason = MpDisconnectReason.ConnectingFailed;
else
reason = MpDisconnectReason.NetFailed;

reader = new ByteReader(ByteWriter.GetBytes(info.Reason));
}
else
reason = MpDisconnectReason.NetFailed;
{
reader = new ByteReader(info.AdditionalData.GetRemainingBytes());
reason = reader.ReadEnum<MpDisconnectReason>();
}

reader = new ByteReader(ByteWriter.GetBytes(info.Reason));
GetConnection(peer).OnDisconnect(reason, reader);
MpLog.Log($"Net client disconnected {info.Reason}");
}
else

public void OnConnectionRequest(ConnectionRequest request)
{
reader = new ByteReader(info.AdditionalData.GetRemainingBytes());
reason = reader.ReadEnum<MpDisconnectReason>();
}

Multiplayer.session.ProcessDisconnectPacket(reason, reader);
ConnectionStatusListeners.TryNotifyAll_Disconnected();
public void OnNetworkLatencyUpdate(NetPeer peer, int latency)
{
}

Multiplayer.StopMultiplayer();
MpLog.Log($"Net client disconnected {info.Reason}");
public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader,
UnconnectedMessageType messageType)
{
}
}
}

public void OnConnectionRequest(ConnectionRequest request) { }
public void OnNetworkLatencyUpdate(NetPeer peer, int latency) { }
public void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType) { }
public class LiteNetLogger : INetLogger
{
public static void Install() => NetDebug.Logger = new LiteNetLogger();

public void WriteNet(NetLogLevel level, string str, params object[] args)
{
if (level == NetLogLevel.Error)
ServerLog.Error(string.Format(str, args));
else
ServerLog.Log(string.Format(str, args));
}
}
}
Loading
Loading