diff --git a/GUI/GuiUtils.cs b/GUI/GuiUtils.cs
index 7b0eb65d0..5b33b6080 100644
--- a/GUI/GuiUtils.cs
+++ b/GUI/GuiUtils.cs
@@ -22,6 +22,9 @@ using System.Windows.Forms;
namespace MCGalaxy.Gui
{
+ // NET 2.0 doesn't have Action delegate defined
+ public delegate void UIAction();
+
/// Shortcuts for MessageBox.Show
public static class Popup
{
diff --git a/GUI/PropertyWindow/PropertyWindow.cs b/GUI/PropertyWindow/PropertyWindow.cs
index f68f02ed6..b5ad679e1 100644
--- a/GUI/PropertyWindow/PropertyWindow.cs
+++ b/GUI/PropertyWindow/PropertyWindow.cs
@@ -32,7 +32,7 @@ namespace MCGalaxy.Gui
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
diff --git a/GUI/Window/Window.cs b/GUI/Window/Window.cs
index 61b8411a0..4f1831f77 100644
--- a/GUI/Window/Window.cs
+++ b/GUI/Window/Window.cs
@@ -28,8 +28,10 @@ using MCGalaxy.Generator;
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 players);
@@ -237,7 +239,7 @@ Trying to mix two versions is unsupported - you may experience issues";
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(() => {
diff --git a/MCGalaxy/Network/Listeners.cs b/MCGalaxy/Network/Listeners.cs
index ac945dcff..88f3993e9 100644
--- a/MCGalaxy/Network/Listeners.cs
+++ b/MCGalaxy/Network/Listeners.cs
@@ -115,7 +115,7 @@ namespace MCGalaxy.Network
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);
@@ -126,7 +126,13 @@ namespace MCGalaxy.Network
// 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();
}
@@ -139,7 +145,7 @@ namespace MCGalaxy.Network
public override void Close() {
try {
- Listening = false;
+ Listening = false;
if (socket != null) socket.Close();
} catch (Exception ex) {
Logger.LogError(ex);
diff --git a/MCGalaxy/Network/Sockets.cs b/MCGalaxy/Network/Sockets.cs
index dac49740b..48a0d3bd3 100644
--- a/MCGalaxy/Network/Sockets.cs
+++ b/MCGalaxy/Network/Sockets.cs
@@ -327,4 +327,135 @@ namespace MCGalaxy.Network
return IPAddress.IsLoopback(ip) || ip.Equals(ccnetIP);
}
}
+
+ // TODO avoid copying so much of TcpSocket
+ #if NET_20
+ /// Backwards compatible socket for older Windows versions where Recv/SendAsync doesn't work
+ 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 sendQueue = new Queue(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
}
diff --git a/MCGalaxy/Scripting/Scripting.cs b/MCGalaxy/Scripting/Scripting.cs
index 0723eff1c..a8e4b8a88 100644
--- a/MCGalaxy/Scripting/Scripting.cs
+++ b/MCGalaxy/Scripting/Scripting.cs
@@ -44,6 +44,7 @@ namespace MCGalaxy.Scripting
// 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;
@@ -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",
args.RequestingAssembly.FullName, args.Name);
+#endif
return null;
}