Move network stuff to abstract socket

This commit is contained in:
UnknownShadow200 2017-05-15 22:28:50 +10:00
parent 54a194c12f
commit decc2116ca
8 changed files with 266 additions and 98 deletions

View File

@ -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 {
/// <summary> Describes an extended block. (Meaning either a core, physics, or custom block) </summary>
public struct ExtBlock : IEquatable<ExtBlock> {
public byte BlockID, ExtID;
/// <summary> Constructs an extended block. </summary>
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);
/// <summary> Returns whether the type of this extended block is a physics block. </summary>
public bool IsPhysicsType {
get { return BlockID >= Block.CpeCount && BlockID != Block.custom_block; }
}
/// <summary> Returns whether the type of this extended block is an invalid block. </summary>
public bool IsInvalidType { get { return BlockID == Block.Invalid; } }
/// <summary> Returns the raw (for client side) block ID of this block. </summary>
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;
}
/// <summary> Constructs an extended block. </summary>
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;
}
}
}

View File

@ -567,6 +567,8 @@
<Compile Include="Network\Packets\Packet.BlockDefs.cs" />
<Compile Include="Network\Packets\Packet.CPE.cs" />
<Compile Include="Network\Packets\Packet.cs" />
<Compile Include="Network\Socket\INetworkSocket.cs" />
<Compile Include="Network\Socket\TcpSocket.cs" />
<Compile Include="Network\Utils\BufferedBlockSender.cs" />
<Compile Include="Network\Utils\LevelChunkStream.cs" />
<Compile Include="Network\Utils\NetUtils.cs" />
@ -714,6 +716,7 @@
<Folder Include="Database\Stats" />
<Folder Include="Network\Heartbeat" />
<Folder Include="Network\IRCPlugin" />
<Folder Include="Network\Socket" />
<Folder Include="Network\Utils" />
<Folder Include="Network\Packets" />
<Folder Include="Entity" />

View File

@ -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<string> 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();
}
}
}

View File

@ -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 {
/// <summary> Abstracts sending to/receiving from a network socket. </summary>
public interface INetworkSocket {
/// <summary> Gets the remote IP of this socket. </summary>
string RemoteIP { get; }
/// <summary> Receives next block of received data, asynchronously. </summary>
void ReceiveNextAsync();
/// <summary> Sends a block of data, either synchronously or asynchronously. </summary>
void Send(byte[] buffer, bool sync = false);
/// <summary> Closes this network socket. </summary>
void Close();
}
}

View File

@ -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 {
/// <summary> Abstracts sending to/receiving from a TCP socket. </summary>
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
}
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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); }