Fix can't connect to on Windows 98

This commit is contained in:
UnknownShadow200 2023-02-01 22:02:09 +11:00
parent 9650b5bf2a
commit 1d82be4654
6 changed files with 150 additions and 6 deletions

View File

@ -22,6 +22,9 @@ using System.Windows.Forms;
namespace MCGalaxy.Gui namespace MCGalaxy.Gui
{ {
// NET 2.0 doesn't have Action delegate defined
public delegate void UIAction();
/// <summary> Shortcuts for MessageBox.Show </summary> /// <summary> Shortcuts for MessageBox.Show </summary>
public static class Popup public static class Popup
{ {

View File

@ -32,7 +32,7 @@ namespace MCGalaxy.Gui
propsZG.SelectedObject = zsSettings; 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) { void PropertyWindow_Load(object sender, EventArgs e) {
// try to use same icon as main window // try to use same icon as main window

View File

@ -28,8 +28,10 @@ using MCGalaxy.Generator;
using MCGalaxy.Gui.Popups; using MCGalaxy.Gui.Popups;
using MCGalaxy.Tasks; using MCGalaxy.Tasks;
namespace MCGalaxy.Gui { namespace MCGalaxy.Gui
public partial class Window : Form { {
public partial class Window : Form
{
// for cross thread use // for cross thread use
delegate void StringCallback(string s); delegate void StringCallback(string s);
delegate void PlayerListCallback(List<Player> players); delegate void PlayerListCallback(List<Player> players);
@ -237,7 +239,7 @@ Trying to mix two versions is unsupported - you may experience issues";
RunOnUI_Async(() => main_btnProps.Enabled = true); 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) { void Player_PlayerConnect(Player p) {
RunOnUI_Async(() => { RunOnUI_Async(() => {

View File

@ -115,7 +115,7 @@ namespace MCGalaxy.Network
static void AcceptCallback(IAsyncResult result) { static void AcceptCallback(IAsyncResult result) {
if (Server.shuttingDown) return; if (Server.shuttingDown) return;
TcpListen listen = (TcpListen)result.AsyncState; TcpListen listen = (TcpListen)result.AsyncState;
TcpSocket s = null; INetSocket s = null;
try { try {
Socket raw = listen.socket.EndAccept(result); Socket raw = listen.socket.EndAccept(result);
@ -126,7 +126,13 @@ namespace MCGalaxy.Network
// intentionally non-clean connection close // intentionally non-clean connection close
try { raw.Close(); } catch { } try { raw.Close(); } catch { }
} else { } 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); s = new TcpSocket(raw);
#endif
if (announce) Logger.Log(LogType.UserActivity, s.IP + " connected to the server."); if (announce) Logger.Log(LogType.UserActivity, s.IP + " connected to the server.");
s.Init(); s.Init();
} }
@ -139,7 +145,7 @@ namespace MCGalaxy.Network
public override void Close() { public override void Close() {
try { try {
Listening = false; Listening = false;
if (socket != null) socket.Close(); if (socket != null) socket.Close();
} catch (Exception ex) { } catch (Exception ex) {
Logger.LogError(ex); Logger.LogError(ex);

View File

@ -327,4 +327,135 @@ namespace MCGalaxy.Network
return IPAddress.IsLoopback(ip) || ip.Equals(ccnetIP); 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
} }

View File

@ -44,6 +44,7 @@ namespace MCGalaxy.Scripting
// only used for resolving plugin DLLs depending on other plugin DLLs // only used for resolving plugin DLLs depending on other plugin DLLs
static Assembly ResolvePluginAssembly(object sender, ResolveEventArgs args) { static Assembly ResolvePluginAssembly(object sender, ResolveEventArgs args) {
#if !NET_20
if (args.RequestingAssembly == null) return null; if (args.RequestingAssembly == null) return null;
if (!IsPluginDLL(args.RequestingAssembly)) return null; if (!IsPluginDLL(args.RequestingAssembly)) return null;
@ -57,6 +58,7 @@ namespace MCGalaxy.Scripting
Logger.Log(LogType.Warning, "Custom command/plugin [{0}] tried to load [{1}], but it could not be found", Logger.Log(LogType.Warning, "Custom command/plugin [{0}] tried to load [{1}], but it could not be found",
args.RequestingAssembly.FullName, args.Name); args.RequestingAssembly.FullName, args.Name);
#endif
return null; return null;
} }