Skip to content

Commit

Permalink
Fix can't connect to on Windows 98
Browse files Browse the repository at this point in the history
  • Loading branch information
UnknownShadow200 committed Feb 1, 2023
1 parent 9650b5b commit 1d82be4
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 6 deletions.
3 changes: 3 additions & 0 deletions GUI/GuiUtils.cs
Expand Up @@ -22,6 +22,9 @@

namespace MCGalaxy.Gui
{
// NET 2.0 doesn't have Action delegate defined
public delegate void UIAction();

/// <summary> Shortcuts for MessageBox.Show </summary>
public static class Popup
{
Expand Down
2 changes: 1 addition & 1 deletion GUI/PropertyWindow/PropertyWindow.cs
Expand Up @@ -32,7 +32,7 @@ public partial class PropertyWindow : Form
propsZG.SelectedObject = zsSettings;
}

public void RunOnUI_Async(Action act) { BeginInvoke(act); }
public void RunOnUI_Async(UIAction act) { BeginInvoke(act); }

void PropertyWindow_Load(object sender, EventArgs e) {
// try to use same icon as main window
Expand Down
8 changes: 5 additions & 3 deletions GUI/Window/Window.cs
Expand Up @@ -28,8 +28,10 @@
using MCGalaxy.Gui.Popups;
using MCGalaxy.Tasks;

namespace MCGalaxy.Gui {
public partial class Window : Form {
namespace MCGalaxy.Gui
{
public partial class Window : Form
{
// for cross thread use
delegate void StringCallback(string s);
delegate void PlayerListCallback(List<Player> players);
Expand Down Expand Up @@ -237,7 +239,7 @@ public partial class Window : Form {
RunOnUI_Async(() => main_btnProps.Enabled = true);
}

public void RunOnUI_Async(Action act) { BeginInvoke(act); }
public void RunOnUI_Async(UIAction act) { BeginInvoke(act); }

void Player_PlayerConnect(Player p) {
RunOnUI_Async(() => {
Expand Down
10 changes: 8 additions & 2 deletions MCGalaxy/Network/Listeners.cs
Expand Up @@ -115,7 +115,7 @@ public sealed class TcpListen : INetListen
static void AcceptCallback(IAsyncResult result) {
if (Server.shuttingDown) return;
TcpListen listen = (TcpListen)result.AsyncState;
TcpSocket s = null;
INetSocket s = null;

try {
Socket raw = listen.socket.EndAccept(result);
Expand All @@ -126,7 +126,13 @@ public sealed class TcpListen : INetListen
// intentionally non-clean connection close
try { raw.Close(); } catch { }
} else {
#if NET_20
// TODO better non-hardcoded detection? move to OperatingSystem?
s = Environment.OSVersion.Platform == PlatformID.Win32Windows ? (INetSocket)(new TcpLegacySocket(raw)) : (INetSocket)(new TcpSocket(raw));
#else
s = new TcpSocket(raw);
#endif

if (announce) Logger.Log(LogType.UserActivity, s.IP + " connected to the server.");
s.Init();
}
Expand All @@ -139,7 +145,7 @@ public sealed class TcpListen : INetListen

public override void Close() {
try {
Listening = false;
Listening = false;
if (socket != null) socket.Close();
} catch (Exception ex) {
Logger.LogError(ex);
Expand Down
131 changes: 131 additions & 0 deletions MCGalaxy/Network/Sockets.cs
Expand Up @@ -327,4 +327,135 @@ public sealed class WebSocket : ServerWebSocket
return IPAddress.IsLoopback(ip) || ip.Equals(ccnetIP);
}
}

// TODO avoid copying so much of TcpSocket
#if NET_20
/// <summary> Backwards compatible socket for older Windows versions where Recv/SendAsync doesn't work </summary>
public sealed class TcpLegacySocket : INetSocket
{
readonly Socket socket;
byte[] recvBuffer = new byte[256];

byte[] sendBuffer = new byte[4096];
readonly object sendLock = new object();
readonly Queue<byte[]> sendQueue = new Queue<byte[]>(64);
volatile bool sendInProgress;

public TcpLegacySocket(Socket s) { socket = s; }

public override void Init() {
ReceiveNextAsync();
}

public override IPAddress IP {
get { return SocketUtil.GetIP(socket); }
}
public override bool LowLatency { set { socket.NoDelay = value; } }


static AsyncCallback recvCallback = RecvCallback;
void ReceiveNextAsync() {
socket.BeginReceive(recvBuffer, 0, recvBuffer.Length, 0, recvCallback, this);
}

static void RecvCallback(IAsyncResult result) {
TcpLegacySocket s = (TcpLegacySocket)result.AsyncState;
if (s.Disconnected) return;

try {
// If received 0, means socket was closed
int recvLen = s.socket.EndReceive(result);
if (recvLen == 0) { s.Disconnect(); return; }

s.HandleReceived(s.recvBuffer, recvLen);
if (!s.Disconnected) s.ReceiveNextAsync();
} catch (SocketException) {
s.Disconnect();
} catch (ObjectDisposedException) {
// Socket was closed by another thread, mark as disconnected
} catch (Exception ex) {
Logger.LogError(ex);
s.Disconnect();
}
}


static AsyncCallback sendCallback = SendCallback;
public override void Send(byte[] buffer, SendFlags flags) {
if (Disconnected || !socket.Connected) return;

// TODO: Low priority sending support
try {
if ((flags & SendFlags.Synchronous) != 0) {
socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
return;
}

lock (sendLock) {
if (sendInProgress) {
sendQueue.Enqueue(buffer);
} else {
TrySendAsync(buffer);
}
}
} catch (SocketException) {
Disconnect();
} catch (ObjectDisposedException) {
// Socket was already closed by another thread
}
}

void TrySendAsync(byte[] buffer) {
// BlockCopy has some overhead, not worth it for very small data
if (buffer.Length <= 16) {
for (int i = 0; i < buffer.Length; i++) {
sendBuffer[i] = buffer[i];
}
} else {
Buffer.BlockCopy(buffer, 0, sendBuffer, 0, buffer.Length);
}

sendInProgress = true;
socket.BeginSend(sendBuffer, 0, buffer.Length, 0, sendCallback, this);
}

static void SendCallback(IAsyncResult result) {
TcpLegacySocket s = (TcpLegacySocket)result.AsyncState;
try {
lock (s.sendLock) {
s.socket.EndSend(result);
s.sendInProgress = false;

if (s.sendQueue.Count > 0) {
s.TrySendAsync(s.sendQueue.Dequeue());
if (s.Disconnected) s.sendQueue.Clear();
}
}
} catch (SocketException) {
s.Disconnect();
} catch (ObjectDisposedException) {
// Socket was already closed by another thread
} catch (Exception ex) {
Logger.LogError(ex);
}
}

// Close while also notifying higher level (i.e. show 'X disconnected' in chat)
void Disconnect() {
if (protocol != null) protocol.Disconnect();
Close();
}

public override void Close() {
Disconnected = true;
pending.Remove(this);

// swallow errors as connection is being closed anyways
try { socket.Shutdown(SocketShutdown.Both); } catch { }
try { socket.Close(); } catch { }

lock (sendLock) { sendQueue.Clear(); }
}
}
#endif
}
2 changes: 2 additions & 0 deletions MCGalaxy/Scripting/Scripting.cs
Expand Up @@ -44,6 +44,7 @@ public static class IScripting

// only used for resolving plugin DLLs depending on other plugin DLLs
static Assembly ResolvePluginAssembly(object sender, ResolveEventArgs args) {
#if !NET_20
if (args.RequestingAssembly == null) return null;
if (!IsPluginDLL(args.RequestingAssembly)) return null;

Expand All @@ -57,6 +58,7 @@ public static class IScripting

Logger.Log(LogType.Warning, "Custom command/plugin [{0}] tried to load [{1}], but it could not be found",
args.RequestingAssembly.FullName, args.Name);
#endif
return null;
}

Expand Down

0 comments on commit 1d82be4

Please sign in to comment.