From decc2116cac8d414ab29f081089cd1394bbbffd2 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Mon, 15 May 2017 22:28:50 +1000 Subject: [PATCH] Move network stuff to abstract socket --- MCGalaxy/Blocks/ExtBlock.cs | 85 ++++++++++++++ MCGalaxy/MCGalaxy_.csproj | 3 + MCGalaxy/Network/Player.Networking.cs | 93 +--------------- MCGalaxy/Network/Socket/INetworkSocket.cs | 37 +++++++ MCGalaxy/Network/Socket/TcpSocket.cs | 129 ++++++++++++++++++++++ MCGalaxy/Player/Player.Fields.cs | 7 +- MCGalaxy/Player/Player.Handlers.cs | 4 +- MCGalaxy/Player/Player.cs | 6 +- 8 files changed, 266 insertions(+), 98 deletions(-) create mode 100644 MCGalaxy/Blocks/ExtBlock.cs create mode 100644 MCGalaxy/Network/Socket/INetworkSocket.cs create mode 100644 MCGalaxy/Network/Socket/TcpSocket.cs diff --git a/MCGalaxy/Blocks/ExtBlock.cs b/MCGalaxy/Blocks/ExtBlock.cs new file mode 100644 index 000000000..f5338fee5 --- /dev/null +++ b/MCGalaxy/Blocks/ExtBlock.cs @@ -0,0 +1,85 @@ +/* + Copyright 2015 MCGalaxy + + Dual-licensed under the Educational Community License, Version 2.0 and + the GNU General Public License, Version 3 (the "Licenses"); you may + not use this file except in compliance with the Licenses. You may + obtain a copy of the Licenses at + + http://www.opensource.org/licenses/ecl2.php + http://www.gnu.org/licenses/gpl-3.0.html + + Unless required by applicable law or agreed to in writing, + software distributed under the Licenses are distributed on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + or implied. See the Licenses for the specific language governing + permissions and limitations under the Licenses. + */ +using System; + +namespace MCGalaxy { + + /// Describes an extended block. (Meaning either a core, physics, or custom block) + public struct ExtBlock : IEquatable { + public byte BlockID, ExtID; + + /// Constructs an extended block. + public ExtBlock(byte block, byte extBlock) { + BlockID = block; ExtID = extBlock; + } + + public static ExtBlock Air = new ExtBlock(Block.air, 0); + public static ExtBlock Invalid = new ExtBlock(Block.Invalid, 0); + + + /// Returns whether the type of this extended block is a physics block. + public bool IsPhysicsType { + get { return BlockID >= Block.CpeCount && BlockID != Block.custom_block; } + } + + /// Returns whether the type of this extended block is an invalid block. + public bool IsInvalidType { get { return BlockID == Block.Invalid; } } + + /// Returns the raw (for client side) block ID of this block. + public byte RawID { + get { return BlockID == Block.custom_block ? ExtID : BlockID; } + } + + public static ExtBlock FromRaw(byte raw) { + ExtBlock block = default(ExtBlock); + block.BlockID = raw; + if (raw < Block.CpeCount) return block; + + block.BlockID = Block.custom_block; + block.ExtID = raw; + return block; + } + + /// Constructs an extended block. + public static explicit operator ExtBlock(byte block) { return new ExtBlock(block, 0); } + + + public override bool Equals(object obj) { + return (obj is ExtBlock) && Equals((ExtBlock)obj); + } + + public override int GetHashCode() { + return BlockID | (ExtID << 8); + } + + public bool Equals(ExtBlock other) { + return (BlockID == Block.custom_block && other.BlockID == Block.custom_block) + ? ExtID == other.ExtID : BlockID == other.BlockID; + } + + public static bool operator == (ExtBlock a, ExtBlock b) { + return (a.BlockID == Block.custom_block && b.BlockID == Block.custom_block) + ? a.ExtID == b.ExtID : a.BlockID == b.BlockID; + } + + public static bool operator != (ExtBlock a, ExtBlock b) { + return (a.BlockID == Block.custom_block && b.BlockID == Block.custom_block) + ? a.ExtID != b.ExtID : a.BlockID != b.BlockID; + } + } +} diff --git a/MCGalaxy/MCGalaxy_.csproj b/MCGalaxy/MCGalaxy_.csproj index ef0d96688..b57bd3cda 100644 --- a/MCGalaxy/MCGalaxy_.csproj +++ b/MCGalaxy/MCGalaxy_.csproj @@ -567,6 +567,8 @@ + + @@ -714,6 +716,7 @@ + diff --git a/MCGalaxy/Network/Player.Networking.cs b/MCGalaxy/Network/Player.Networking.cs index a74687a34..250ac017e 100644 --- a/MCGalaxy/Network/Player.Networking.cs +++ b/MCGalaxy/Network/Player.Networking.cs @@ -23,43 +23,6 @@ using MCGalaxy.Network; namespace MCGalaxy { public sealed partial class Player : IDisposable { - static void Receive(IAsyncResult result) { - //Server.s.Log(result.AsyncState.ToString()); - Player p = (Player)result.AsyncState; - if (p.disconnected || p.socket == null) return; - - try { - int length = p.socket.EndReceive(result); - if (length == 0) { p.Disconnect(); return; } - - byte[] allData = new byte[p.leftBuffer.Length + length]; - Buffer.BlockCopy(p.leftBuffer, 0, allData, 0, p.leftBuffer.Length); - Buffer.BlockCopy(p.tempbuffer, 0, allData, p.leftBuffer.Length, length); - p.leftBuffer = p.ProcessReceived(allData); - - if (p.dontmindme && p.leftBuffer.Length == 0) { - Server.s.Log("Disconnected"); - p.socket.Close(); - p.disconnected = true; - return; - } - if ( !p.disconnected ) - p.socket.BeginReceive(p.tempbuffer, 0, p.tempbuffer.Length, SocketFlags.None, - new AsyncCallback(Receive), p); - } catch ( SocketException ) { - p.Disconnect(); - } catch ( ObjectDisposedException ) { - // Player is no longer connected, socket was closed - // Mark this as disconnected and remove them from active connection list - connections.Remove(p); - p.RemoveFromPending(); - p.disconnected = true; - } catch ( Exception e ) { - Server.ErrorLog(e); - p.Leave("Error!"); - } - } - public bool hasCpe, finishedCpeLogin = false; public string appName; public int extensionCount; @@ -103,27 +66,7 @@ namespace MCGalaxy { } } - public void Send(byte[] buffer, bool sync = false) { - // Abort if socket has been closed - if (disconnected || socket == null || !socket.Connected) return; - - try { - if (sync) - socket.Send(buffer, 0, buffer.Length, SocketFlags.None); - else - socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, delegate(IAsyncResult result) { }, null); - buffer = null; - } catch (SocketException e) { - buffer = null; - Disconnect(); - #if DEBUG - Server.ErrorLog(e); - #endif - } catch (ObjectDisposedException) { - // socket was already closed by another thread. - buffer = null; - } - } + public void Send(byte[] buffer, bool sync = false) { socket.Send(buffer, sync); } public static void MessageLines(Player p, IEnumerable lines) { foreach (string line in lines) @@ -349,37 +292,9 @@ namespace MCGalaxy { Send(Packet.ExtAddPlayerName(id, listName, displayName, grp, grpRank, hasCP437)); } - internal void CloseSocket() { - // Try to close the socket. - // Sometimes its already closed so these lines will cause an error - // We just trap them and hide them from view :P - try { - // Close the damn socket connection! - socket.Shutdown(SocketShutdown.Both); - #if DEBUG - Server.s.Log("Socket was shutdown for " + name ?? ip); - #endif - } - catch ( Exception e ) { - #if DEBUG - Exception ex = new Exception("Failed to shutdown socket for " + name ?? ip, e); - Server.ErrorLog(ex); - #endif - } - - try { - socket.Close(); - #if DEBUG - Server.s.Log("Socket was closed for " + name ?? ip); - #endif - } - catch ( Exception e ) { - #if DEBUG - Exception ex = new Exception("Failed to close socket for " + name ?? ip, e); - Server.ErrorLog(ex); - #endif - } - RemoveFromPending(); + internal void CloseSocket() { + socket.Close(); + RemoveFromPending(); } } } diff --git a/MCGalaxy/Network/Socket/INetworkSocket.cs b/MCGalaxy/Network/Socket/INetworkSocket.cs new file mode 100644 index 000000000..e7eddd66f --- /dev/null +++ b/MCGalaxy/Network/Socket/INetworkSocket.cs @@ -0,0 +1,37 @@ +/* + Copyright 2015 MCGalaxy + + Dual-licensed under the Educational Community License, Version 2.0 and + the GNU General Public License, Version 3 (the "Licenses"); you may + not use this file except in compliance with the Licenses. You may + obtain a copy of the Licenses at + + http://www.opensource.org/licenses/ecl2.php + http://www.gnu.org/licenses/gpl-3.0.html + + Unless required by applicable law or agreed to in writing, + software distributed under the Licenses are distributed on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + or implied. See the Licenses for the specific language governing + permissions and limitations under the Licenses. + */ +using System; + +namespace MCGalaxy.Network { + + /// Abstracts sending to/receiving from a network socket. + public interface INetworkSocket { + + /// Gets the remote IP of this socket. + string RemoteIP { get; } + + /// Receives next block of received data, asynchronously. + void ReceiveNextAsync(); + + /// Sends a block of data, either synchronously or asynchronously. + void Send(byte[] buffer, bool sync = false); + + /// Closes this network socket. + void Close(); + } +} diff --git a/MCGalaxy/Network/Socket/TcpSocket.cs b/MCGalaxy/Network/Socket/TcpSocket.cs new file mode 100644 index 000000000..a7f645b38 --- /dev/null +++ b/MCGalaxy/Network/Socket/TcpSocket.cs @@ -0,0 +1,129 @@ +/* +Copyright 2010 MCSharp team (Modified for use with MCZall/MCLawl/MCGalaxy) +Dual-licensed under the Educational Community License, Version 2.0 and +the GNU General Public License, Version 3 (the "Licenses"); you may +not use this file except in compliance with the Licenses. You may +obtain a copy of the Licenses at +http://www.opensource.org/licenses/ecl2.php +http://www.gnu.org/licenses/gpl-3.0.html +Unless required by applicable law or agreed to in writing, +software distributed under the Licenses are distributed on an "AS IS" +BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the Licenses for the specific language governing +permissions and limitations under the Licenses. + */ +using System; +using System.Net; +using System.Net.Sockets; + +namespace MCGalaxy.Network { + + /// Abstracts sending to/receiving from a TCP socket. + public sealed class TcpSocket : INetworkSocket { + readonly Player player; + readonly Socket socket; + byte[] leftBuffer = new byte[0]; + byte[] tempbuffer = new byte[0xFF]; + + public TcpSocket(Player p, Socket s) { + player = p; socket = s; + } + + public string RemoteIP { + // TODO: This is very icky + get { return socket.RemoteEndPoint.ToString().Split(':')[0]; } + } + + + public void ReceiveNextAsync() { + socket.BeginReceive(tempbuffer, 0, tempbuffer.Length, SocketFlags.None, new AsyncCallback(Receive), this); + } + + static void Receive(IAsyncResult result) { + TcpSocket s = (TcpSocket)result.AsyncState; + Player p = s.player; + if (p.disconnected) return; + + try { + int length = s.socket.EndReceive(result); + if (length == 0) { p.Disconnect(); return; } + + byte[] allData = new byte[s.leftBuffer.Length + length]; + Buffer.BlockCopy(s.leftBuffer, 0, allData, 0, s.leftBuffer.Length); + Buffer.BlockCopy(s.tempbuffer, 0, allData, s.leftBuffer.Length, length); + s.leftBuffer = p.ProcessReceived(allData); + + if (p.dontmindme && s.leftBuffer.Length == 0) { + Server.s.Log("Disconnected"); + s.Close(); + p.disconnected = true; + return; + } + if (!p.disconnected) s.ReceiveNextAsync(); + } catch (SocketException) { + p.Disconnect(); + } catch (ObjectDisposedException) { + // Player is no longer connected, socket was closed + // Mark this as disconnected and remove them from active connection list + Player.connections.Remove(p); + p.RemoveFromPending(); + p.disconnected = true; + } catch (Exception e) { + Server.ErrorLog(e); + p.Leave("Error!"); + } + } + + public void Send(byte[] buffer, bool sync = false) { + // Abort if socket has been closed + if (player.disconnected || !socket.Connected) return; + + try { + if (sync) + socket.Send(buffer, 0, buffer.Length, SocketFlags.None); + else + socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, delegate(IAsyncResult result) { }, null); + buffer = null; + } catch (SocketException e) { + buffer = null; + player.Disconnect(); + #if DEBUG + Server.ErrorLog(e); + #endif + } catch (ObjectDisposedException) { + // socket was already closed by another thread. + buffer = null; + } + } + + public void Close() { + // Try to close the socket. + // Sometimes its already closed so these lines will cause an error + // We just trap them and hide them from view :P + try { + // Close the damn socket connection! + socket.Shutdown(SocketShutdown.Both); + #if DEBUG + Server.s.Log("Socket was shutdown for " + name ?? ip); + #endif + } catch (Exception e) { + #if DEBUG + Exception ex = new Exception("Failed to shutdown socket for " + name ?? ip, e); + Server.ErrorLog(ex); + #endif + } + + try { + socket.Close(); + #if DEBUG + Server.s.Log("Socket was closed for " + name ?? ip); + #endif + } catch (Exception e) { + #if DEBUG + Exception ex = new Exception("Failed to close socket for " + name ?? ip, e); + Server.ErrorLog(ex); + #endif + } + } + } +} diff --git a/MCGalaxy/Player/Player.Fields.cs b/MCGalaxy/Player/Player.Fields.cs index dc7361894..158543851 100644 --- a/MCGalaxy/Player/Player.Fields.cs +++ b/MCGalaxy/Player/Player.Fields.cs @@ -22,6 +22,7 @@ using MCGalaxy.Games; using MCGalaxy.Undo; using MCGalaxy.Maths; using MCGalaxy.Events; +using MCGalaxy.Network; namespace MCGalaxy { @@ -58,7 +59,8 @@ namespace MCGalaxy { public static string storedHelp = ""; public string truename; internal bool dontmindme = false; - public Socket socket; + INetworkSocket socket; + public DateTime LastAction, AFKCooldown; public bool IsAfk = false, AutoAfk; public bool cmdTimer = false; @@ -66,9 +68,6 @@ namespace MCGalaxy { public string BrushName = "normal", DefaultBrushArgs = ""; public Transform Transform = NoTransform.Instance; public string afkMessage; - - byte[] leftBuffer = new byte[0]; - byte[] tempbuffer = new byte[0xFF]; public bool disconnected = false; DateTime startTime; diff --git a/MCGalaxy/Player/Player.Handlers.cs b/MCGalaxy/Player/Player.Handlers.cs index 05aba0b12..8b1fe32d3 100644 --- a/MCGalaxy/Player/Player.Handlers.cs +++ b/MCGalaxy/Player/Player.Handlers.cs @@ -33,7 +33,7 @@ namespace MCGalaxy { public sealed partial class Player : IDisposable { bool removedFromPending = false; - void RemoveFromPending() { + internal void RemoveFromPending() { if (removedFromPending) return; removedFromPending = true; @@ -195,7 +195,7 @@ namespace MCGalaxy { return block == Block.air || (block >= Block.water && block <= Block.lavastill); } - byte[] ProcessReceived(byte[] buffer) { + internal byte[] ProcessReceived(byte[] buffer) { try { int size = PacketSize(buffer); if (size == -2) return new byte[1]; // WoM get request diff --git a/MCGalaxy/Player/Player.cs b/MCGalaxy/Player/Player.cs index bdee5b2ef..18f8ffcdf 100644 --- a/MCGalaxy/Player/Player.cs +++ b/MCGalaxy/Player/Player.cs @@ -50,14 +50,14 @@ namespace MCGalaxy { public Player(Socket s) { spamChecker = new SpamChecker(this); try { - socket = s; - ip = socket.RemoteEndPoint.ToString().Split(':')[0]; + socket = new TcpSocket(this, s); + ip = socket.RemoteIP; SessionID = Interlocked.Increment(ref sessionCounter) & SessionIDMask; Server.s.Log(ip + " connected to the server."); for (byte i = 0; i < Block.CpeCount; i++) bindings[i] = i; - socket.BeginReceive(tempbuffer, 0, tempbuffer.Length, SocketFlags.None, new AsyncCallback(Receive), this); + socket.ReceiveNextAsync(); connections.Add(this); } catch ( Exception e ) { Leave("Login failed!"); Server.ErrorLog(e); }