diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6bad850a9cf7b56857a19af20bbd139ea2ebdb62..bc9b599f04da6d49a158e42294f79b496a8eaad6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -8,6 +8,7 @@ set(SRB2_CORE_SOURCES
 	comptime.c
 	console.c
 	d_clisrv.c
+	d_datawrap.c
 	d_enet.c
 	d_main.c
 	d_netcmd.c
@@ -52,6 +53,7 @@ set(SRB2_CORE_HEADERS
 	command.h
 	console.h
 	d_clisrv.h
+	d_datawrap.h
 	d_enet.h
 	d_event.h
 	d_main.h
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 55d1880232e3a2a2eb1774e882c6fb6e78bae35f..0e6a9999f7ed3d3d2a1dc37caa9e766f389e12f6 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -481,6 +481,7 @@ static boolean CL_SendJoin(void)
 		return true;
 	}
 	// NET TODO
+	Net_SendJoin();
 	return true;
 }
 
@@ -643,6 +644,12 @@ static void CL_LoadReceivedSavegame(void)
 #ifndef NONET
 static void SendAskInfo(INT32 node, boolean viams)
 {
+	//if (server)
+	{// I'm the server, skip this.
+		// I'm the server, skip this.
+		cl_mode = cl_askjoin;
+		return;
+	}
 	// NET TODO
 }
 
@@ -722,10 +729,7 @@ static void CL_ConnectToServer(boolean viams)
 	sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
 #endif
 
-	if (servernode == 0)
-		cl_mode = cl_askjoin;
-	else
-		cl_mode = cl_searching;
+	cl_mode = cl_searching;
 
 #ifdef CLIENT_LOADINGSCREEN
 	lastfilenum = 0;
@@ -891,15 +895,9 @@ static void CL_ConnectToServer(boolean viams)
 
 				cl_mode = cl_askjoin; // don't break case continue to cljoin request now
 			case cl_askjoin:
-				if (!server)
+				if (!server) // the server already has their files loaded, duh!
 					CL_LoadServerFiles();
-#ifdef JOININGAME
-				// prepare structures to save the file
-				// WARNING: this can be useless in case of server not in GS_LEVEL
-				// but since the network layer doesn't provide ordered packets...
-				CL_PrepareDownloadSaveGame(tmpsave);
-#endif
-				if (CL_SendJoin())
+				if (CL_SendJoin()) // Send join request, server instantly connects.
 					cl_mode = server ? cl_connected : cl_waitjoinresponse;
 				break;
 #ifdef JOININGAME
diff --git a/src/d_datawrap.c b/src/d_datawrap.c
new file mode 100644
index 0000000000000000000000000000000000000000..1db92357cae6fa64d5a25fef0f46ffcd24318ffb
--- /dev/null
+++ b/src/d_datawrap.c
@@ -0,0 +1,55 @@
+#include "doomdef.h"
+#include "doomstat.h"
+#include "byteptr.h"
+#include "d_datawrap.h"
+#include "z_zone.h"
+
+static void CheckEOF(DataWrap dw, size_t l)
+{
+	if (dw->p - dw->data + l > dw->len)
+	{
+		Z_Free(dw);
+		longjmp(*dw->eofjmp, 1);
+	}
+}
+
+static UINT8 ReadUINT8(DataWrap dw)
+{
+	CheckEOF(dw, 1);
+	return READUINT8(dw->p);
+}
+
+static UINT16 ReadUINT16(DataWrap dw)
+{
+	CheckEOF(dw, 2);
+	return READUINT16(dw->p);
+}
+
+static char *ReadStringn(DataWrap dw, size_t n)
+{
+	char *string = ZZ_Alloc(n+1);
+	char *p = string;
+	int i;
+	for (i = 0; i < n; i++, p++)
+	{
+		CheckEOF(dw,1);
+		*p = READUINT8(dw->p);
+		if (!*p)
+			break;
+	}
+	*p = '\0';
+	return string;
+}
+
+DataWrap D_NewDataWrap(const void *data, size_t len, jmp_buf *eofjmp)
+{
+	DataWrap dw = ZZ_Alloc(sizeof(struct DataWrap_s));
+	dw->data = dw->p = data;
+	dw->len = len;
+	dw->eofjmp = eofjmp;
+
+	dw->ReadUINT8 = ReadUINT8;
+	dw->ReadUINT16 = ReadUINT16;
+	dw->ReadStringn = ReadStringn;
+	return dw;
+}
diff --git a/src/d_datawrap.h b/src/d_datawrap.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e7374de4249c293b89f33be265c57d395fc1455
--- /dev/null
+++ b/src/d_datawrap.h
@@ -0,0 +1,14 @@
+// Basically SDL_RWops I guess.
+#include <setjmp.h>
+
+typedef struct DataWrap_s {
+	const void *data, *p;
+	size_t len;
+	jmp_buf	*eofjmp;
+
+	UINT8 (*ReadUINT8)(struct DataWrap_s *);
+	UINT16 (*ReadUINT16)(struct DataWrap_s *);
+	char *(*ReadStringn)(struct DataWrap_s *, size_t n);
+} *DataWrap;
+
+DataWrap D_NewDataWrap(const void *data, size_t len, jmp_buf *eofjmp);
diff --git a/src/d_enet.c b/src/d_enet.c
index 5db2b8ffc88eb9f4ef0ef0f991fbe98a2c741512..fd3b2b04a4d041a36b49c8e0a13ec53ba1c2bdae 100644
--- a/src/d_enet.c
+++ b/src/d_enet.c
@@ -2,9 +2,11 @@
 
 #include "doomdef.h"
 #include "doomstat.h"
+#include "byteptr.h"
 #include "d_enet.h"
 #include "z_zone.h"
 #include "m_menu.h"
+#include "d_datawrap.h"
 
 UINT8 net_nodecount, net_playercount;
 UINT8 playernode[MAXPLAYERS];
@@ -15,7 +17,12 @@ boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
 
 #define NETCHANNELS 4
 
-#define DISCONNECT_SHUTDOWN 1
+enum {
+	DISCONNECT_UNKNOWN = 0,
+	DISCONNECT_SHUTDOWN,
+
+	CLIENT_JOIN = 0
+};
 
 static ENetHost *ServerHost = NULL,
 	*ClientHost = NULL;
@@ -24,7 +31,7 @@ static UINT8 nodeleaving[MAXNETNODES];
 
 typedef struct PeerData_s {
 	UINT8 node;
-} PeerData_t;
+} PeerData;
 
 boolean Net_GetNetStat(void)
 {
@@ -32,17 +39,36 @@ boolean Net_GetNetStat(void)
 	return false;
 }
 
+static void ServerHandlePacket(UINT8 node, DataWrap data)
+{
+	switch(data->ReadUINT8(data))
+	{
+	case CLIENT_JOIN:
+	{
+		UINT16 version = data->ReadUINT16(data);
+		UINT16 subversion = data->ReadUINT16(data);
+		if (version != VERSION || subversion != SUBVERSION)
+			CONS_Printf("NETWORK: Version mismatch!?\n");
+		char *name = data->ReadStringn(data, MAXPLAYERNAME);
+		CONS_Printf("NETWORK: Player '%s' joining...\n", name);
+		break;
+	}
+	default:
+		CONS_Printf("NETWORK: Unknown message type recieved from node %u!\n", node);
+		break;
+	}
+}
+
 void Net_AckTicker(void)
 {
 	ENetEvent e;
 	UINT8 i;
-	PeerData_t *pdata;
+	PeerData *pdata;
+	jmp_buf safety;
 
 	while (ClientHost && enet_host_service(ClientHost, &e, 0) > 0)
 		switch (e.type)
 		{
-		case ENET_EVENT_TYPE_CONNECT:
-			break;
 		case ENET_EVENT_TYPE_DISCONNECT:
 			if (!server)
 			{
@@ -54,9 +80,11 @@ void Net_AckTicker(void)
 					M_StartMessage(M_GetText("Disconnected from server.\n\nPress ESC\n"), NULL, MM_NOTHING);
 			}
 			break;
+
 		case ENET_EVENT_TYPE_RECEIVE:
 			enet_packet_destroy(e.packet);
 			break;
+
 		default:
 			break;
 		}
@@ -65,16 +93,19 @@ void Net_AckTicker(void)
 		switch (e.type)
 		{
 		case ENET_EVENT_TYPE_CONNECT:
-			for (i = 0; i < MAXNETNODES && nodetopeer[i]; i++)
+			for (i = 0; i < MAXNETNODES && nodeingame[i]; i++)
 				;
 			I_Assert(i < MAXNETNODES); // ENet should not be able to send connect events when nodes are full.
+			nodeingame[i] = true;
 			nodetopeer[i] = e.peer;
 			pdata = ZZ_Alloc(sizeof(*pdata));
 			pdata->node = i;
 			e.peer->data = pdata;
+			CONS_Printf("NETWORK: Node %u connected.\n", i);
 			break;
+
 		case ENET_EVENT_TYPE_DISCONNECT:
-			pdata = (PeerData_t *)e.peer->data;
+			pdata = (PeerData *)e.peer->data;
 			if (!nodeleaving[pdata->node])
 			{
 				XBOXSTATIC UINT8 buf[2];
@@ -93,9 +124,16 @@ void Net_AckTicker(void)
 			Z_Free(pdata);
 			e.peer->data = NULL;
 			break;
+
 		case ENET_EVENT_TYPE_RECEIVE:
+			pdata = (PeerData *)e.peer->data;
+			if (setjmp(safety))
+				CONS_Printf("NETWORK: There was an EOF error in a recieved packet! Node %u, len %u\n", pdata->node, e.packet->dataLength);
+			else
+				ServerHandlePacket(pdata->node, D_NewDataWrap(e.packet->data, e.packet->dataLength, &safety));
 			enet_packet_destroy(e.packet);
 			break;
+
 		default:
 			break;
 		}
@@ -118,7 +156,9 @@ void D_NetOpen(void)
 	ServerHost = enet_host_create(&address, MAXNETNODES, NETCHANNELS, 0, 0);
 	if (!ServerHost)
 		I_Error("ENet failed to open server host. (Check if the port is in use?)");
+
 	servernode = 0;
+	nodeingame[servernode] = true;
 }
 
 void D_NetConnect(const char *hostname, const char *port)
@@ -131,7 +171,7 @@ void D_NetConnect(const char *hostname, const char *port)
 		I_Error("ENet failed to initialize client host.");
 
 	netgame = multiplayer = true;
-	servernode = 1;
+	servernode = 0;
 
 	enet_address_set_host(&address, hostname);
 	address.port = 5029;
@@ -141,6 +181,7 @@ void D_NetConnect(const char *hostname, const char *port)
 	nodetopeer[servernode] = enet_host_connect(ClientHost, &address, NETCHANNELS, 0);
 	if (!nodetopeer[servernode])
 		I_Error("Failed to allocate ENet peer for connecting ???");
+	nodeingame[servernode] = true;
 
 	if (enet_host_service(ClientHost, &e, 5000) > 0
 	&& e.type == ENET_EVENT_TYPE_CONNECT)
@@ -175,13 +216,16 @@ void D_CloseConnection(void)
 	ENetEvent e;
 	if (ServerHost)
 	{
-		UINT8 i;
+		UINT8 i, waiting=0;
 		// tell everyone to go away
 		for (i = 0; i < MAXNETNODES; i++)
 			if (nodeingame[i])
+			{
 				enet_peer_disconnect(nodetopeer[i], DISCONNECT_SHUTDOWN);
+				waiting++;
+			}
 		// wait for messages to go through.
-		while (enet_host_service(ServerHost, &e, 3000) > 0)
+		while (waiting > 0 && enet_host_service(ServerHost, &e, 3000) > 0)
 			switch (e.type)
 			{
 			// i don't care, shut up.
@@ -190,6 +234,7 @@ void D_CloseConnection(void)
 				break;
 			// good, go away.
 			case ENET_EVENT_TYPE_DISCONNECT:
+				waiting--;
 				break;
 			// no, we're shutting down.
 			case ENET_EVENT_TYPE_CONNECT:
@@ -199,24 +244,28 @@ void D_CloseConnection(void)
 		// alright, we're finished.
 		enet_host_destroy(ServerHost);
 	}
+
 	if (ClientHost)
 	{
 		enet_peer_disconnect(nodetopeer[servernode], 0);
 		while (enet_host_service(ServerHost, &e, 3000) > 0)
-			switch (e.type)
+		{
+			if (e.type == ENET_EVENT_TYPE_DISCONNECT)
+				break;
+			else switch (e.type)
 			{
 			case ENET_EVENT_TYPE_RECEIVE:
 				enet_packet_destroy(e.packet);
 				break;
-			case ENET_EVENT_TYPE_DISCONNECT:
-				break;
 			case ENET_EVENT_TYPE_CONNECT:
 				// how the what ???
 				enet_peer_reset(e.peer);
 				break;
 			}
+		}
 		enet_host_destroy(ClientHost);
 	}
+
 	netgame = false;
 	addedtogame = false;
 	servernode = 0;
@@ -234,14 +283,26 @@ void Net_CloseConnection(INT32 node)
 	enet_peer_disconnect(nodetopeer[node], 0);
 }
 
-void Net_AbortPacketType(UINT8 packettype)
+void Net_SendAcks(INT32 node)
 {
 }
 
-void Net_SendAcks(INT32 node)
+void Net_WaitAllAckReceived(UINT32 timeout)
 {
 }
 
-void Net_WaitAllAckReceived(UINT32 timeout)
+// Client: Can I play? =3 My name is Player so-and-so!
+void Net_SendJoin(void)
 {
+	ENetPacket *packet;
+	UINT8 data[5+MAXPLAYERNAME];
+	UINT8 *buf = data;
+
+	WRITEUINT8(buf, CLIENT_JOIN);
+	WRITEUINT16(buf, VERSION);
+	WRITEUINT16(buf, SUBVERSION);
+	WRITESTRINGN(buf, cv_playername.string, MAXPLAYERNAME);
+
+	packet = enet_packet_create(data, buf-data, ENET_PACKET_FLAG_RELIABLE);
+	enet_peer_send(nodetopeer[servernode], 0, packet);
 }
diff --git a/src/d_enet.h b/src/d_enet.h
index 6cc954a6da29f572434652ca5cbee63d0673f88f..6d249e52d93bb33cbc1913748966afd0e6b25d38 100644
--- a/src/d_enet.h
+++ b/src/d_enet.h
@@ -17,6 +17,7 @@ boolean D_CheckNetGame(void);
 void D_CloseConnection(void);
 void Net_UnAcknowledgPacket(INT32 node);
 void Net_CloseConnection(INT32 node);
-void Net_AbortPacketType(UINT8 packettype);
 void Net_SendAcks(INT32 node);
 void Net_WaitAllAckReceived(UINT32 timeout);
+
+void Net_SendJoin(void);