[Scummvm-git-logs] scummvm master -> 82bc36db8de4b73a1cb9a1549e45d07eead6a66d

sev- noreply at scummvm.org
Sun Mar 5 23:07:48 UTC 2023


This automated email contains information about 69 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
ade9fab8d6 CONFIGURE: Add ENet library.
68425432ab BACKENDS: ENET: Preliminary ENet backend.
129b56cd3e BACKENDS: ENET: Host creation.
7bc36d05fc BACKENDS: ENET: Client host creation.
64200c8643 BACKENDS: ENET: Raw UDP socket creation.
6305d74464 BACKENDS: ENET: Host data send and retrieval.
619daae936 BACKENDS: ENET: Code formatting.
5f5de8122b BACKENDS: ENET: Fix pointer formatting.
8a673b4a0c BACKENDS: ENET: Ability to disconnect peers.
747b28504a BACKENDS: ENET: Use delayMillis for disconnection.
1a50e10a83 BACKENDS: ENET: Now compiles and works on MSVC.
06b69d3480 BACKENDS: ENET: Remove trailing whitespace.
f9ec21c27a BACKENDS: ENET: Fix naming convention
9329f8c3d6 BACKENDS: ENET: Hosts can connect to peers.
ce45a4f4e6 SCUMM HE: Use new online networking code.
07fc39e26b SCUMM HE: Use new network code in Football.
70c94aced3 SCUMM HE: Command line host/join Moonbase games.
c16244a23a BACKENDS: ENET: Compile stable 1.3.17 source.
cef3054212 BACKENDS: ENET: Check and set additional defines.
bd1872c2ab BACKENDS: ENET: Now compiles on Windows.
cec2134a2d SCUMM HE: Network play options.
f42930ba66 BACKENDS: CURL: Ability to create raw sockets.
d06bac869c SCUMM HE: Initial Lobby (Boneyards) implementation
3e29d0bea4 BACKENDS: CURL: Fix compiling on Windows.
484a72bc4c SCUMM HE: Clean up socket on connection failure.
46828bdef5 SCUMM HE: Fix crash when starting Football 2002.
200402852e BACKENDS: ENET: Replace get_host_ip to get_host.
2697d213b0 SCUMM HE: Properly wait until "get_sessions_resp".
13616c37ec BACKENDS: ENET: Fix casting.
239f1f6760 BACKENDS: ENET: call both get_host and get_host_ip
7110659591 SCUMM HE: Handle registration URL.
4f23fe5c1f SCUMM HE: Proper server reset button
8a17fc4ebf SCUMM HE: Add Yes/No buttons to registration box.
446f95d26c SCUMM HE: Send version to lobby server.
91c6ddacba GUI: Online session selector for Football 2002.
893eb485f8 SCUMM HE: Use new online session dialog.
b8ac9ed46e BACKENDS: ENET: Revert back to using "get_host_ip"
c14481e534 SCUMM HE: Hack fix when connecting via domain.
cfc08b5d97 SCUMM HE: Always send football messages reliably.
5d79f212d2 SCUMM HE: Fix network not running on missing config.
1cf1f302ad BACKENDS: LIBCURL: URL Parsing.
5b92f8fe97 SCUMM HE: Configurable lobby server and checking.
4b544d3b5f SCUMM HE: Dispatch lobby opcodes to lobby code.
ad96e50283 SCUMM HE: Migrate more lobby code.
73cc6cc488 SCUMM HE: Finish mitgrating lobby code.
568f120784 SCUMM HE: Enable network play on Mac versions.
7fcf9e2440 SCUMM HE: Disable "InternetConnect" INI setting.
5114c0f438 SCUMM HE: Keep querying until the session we want exists.
d85c206714 SCUMM HE: Fix compiling without ENet.
4aede052e3 SCUMM HE: Migrate competitive mods.
7fb576decb SCUMM HE: Accept server address change mid-game.
653b1a19df SCUMM HE: Re-add audio override option if needed.
af2477f122 SCUMM HE: Make some network class members public.
84866701b7 BACKENDS: CURL: Timeout if send/recv takes too long
f9f2023b2a BACKENDS: ENET: Fix space indentation to tab.
af035e3535 SCUMM HE: Fix crash when loading options in-game.
eba89934c3 SCUMM HE: Unmark custom teams when disconnected.
edce46d28e SCUMM HE: Send max players to session server.
b733471691 SCUMM HE: Fix case error message.
11d9631003 BACKENDS: CURL: Add missing end lines.
3ce5203e52 SCUMM HE: Fix code formatting.
df90878e62 SCUMM HE: Remove unreachable code.
c7d16aea5c BACKENDS: ENET: Document code.
f2dac5e9b3 BACKENDS: CURL: Document URL methods.
27c66b6c34 SCUMM HE: Remove commented out code.
139a414d23 SCUMM HE: Document main network code.
af06a58c18 SCUMM HE: Fix compile error.
6642a24ea4 SCUMM HE: Re-add code lost during rebasing.
82bc36db8d SCUMM HE: Label SO_COMPLEX_ARRAY_MATH_OPERATION.


Commit: ade9fab8d6443621724995f2ce5da51089172a16
    https://github.com/scummvm/scummvm/commit/ade9fab8d6443621724995f2ce5da51089172a16
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
CONFIGURE: Add ENet library.

Changed paths:
    base/version.cpp
    configure


diff --git a/base/version.cpp b/base/version.cpp
index 71254e4cc6e..f4cf7e8bb31 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -211,6 +211,9 @@ const char gScummVMFeatures[] = ""
 	"SDL_net "
 #endif
 #endif
+#ifdef USE_ENET
+	"ENet "
+#endif
 #ifdef USE_TINYGL
 	"TinyGL "
 #endif
diff --git a/configure b/configure
index 425bf464297..21ae699d043 100755
--- a/configure
+++ b/configure
@@ -138,6 +138,7 @@ _ogg=auto
 _vorbis=auto
 _sdlnet=auto
 _libcurl=auto
+_enet=auto
 _tremor=auto
 _tremolo=no
 _flac=auto
@@ -1052,6 +1053,9 @@ Optional Libraries:
   --with-libcurl-prefix=DIR    prefix where libcurl is installed (optional)
   --disable-libcurl        disable libcurl networking library [autodetect]
 
+  --with-enet-prefix=DIR prefix where ENet is installed (optional)
+  --disable-enet         disable ENet networking library [autodetect]
+
   --with-discord-prefix=DIR   prefix where discord-rpc is installed (optional)
   --disable-discord       disable Discord rich presence integration [autodetect]
 
@@ -1189,6 +1193,8 @@ for ac_option in $@; do
 	--disable-sdlnet)             _sdlnet=no             ;;
 	--enable-libcurl)             _libcurl=yes           ;;
 	--disable-libcurl)            _libcurl=no            ;;
+	--enable-enet)                _enet=yes              ;;
+	--disable-enet)               _enet=no               ;;
 	--enable-cloud)               _cloud=yes             ;;
 	--disable-cloud)              _cloud=no              ;;
 	--enable-lld)                 _lld=yes               ;;
@@ -1367,6 +1373,11 @@ for ac_option in $@; do
 		SDL_NET_CFLAGS="-I$arg/include"
 		SDL_NET_LIBS="-L$arg/lib"
 		;;
+	--with-enet-prefix=*)
+		arg=`echo $ac_option | cut -d '=' -f 2`
+		ENET_CFLAGS="-I$arg/include"
+		ENET_LIBS="-L$arg/lib"
+		;;
 	--with-opengl-prefix=*)
 		arg=`echo $ac_option | cut -d '=' -f 2`
 		OPENGL_CFLAGS="-I$arg/include"
@@ -4174,6 +4185,29 @@ EOF
 	echo "$_sdlnet"
 fi
 
+if test "$_enet" = auto ; then
+	if test "$_pkg_config" = "yes" && $_pkgconfig --exists libenet; then
+		append_var ENET_LIBS "`$_pkgconfig --libs libenet`"
+		append_var ENET_CFLAGS "`$_pkgconfig --cflags libenet`"
+	else
+		append_var ENET_LIBS "-lenet"
+	fi
+
+	# Check for ENet
+	echocheck "ENet"
+	_enet=no
+	cat > $TMPC << EOF
+#include <enet/enet.h>
+int main(int argc, char *argv[]) { enet_initialize(); return 0; }
+EOF
+	cc_check $ENET_LIBS $LIBS $INCLUDES $ENET_CFLAGS && _enet=yes
+	if test "$_enet" = yes ; then
+		append_var LIBS "$ENET_LIBS"
+		append_var INCLUDES "$ENET_CFLAGS"
+	fi
+	define_in_config_if_yes "$_enet" 'USE_ENET'
+	echo "$_enet"
+fi
 
 #
 # Enable 16bit support only for backends which support it


Commit: 68425432ab25ad12537d96e0bbc19e2803986c40
    https://github.com/scummvm/scummvm/commit/68425432ab25ad12537d96e0bbc19e2803986c40
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Preliminary ENet backend.

Changed paths:
  A backends/networking/enet/enet.cpp
  A backends/networking/enet/enet.h
    backends/module.mk


diff --git a/backends/module.mk b/backends/module.mk
index 9cbb3f47a46..e580881267c 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -99,6 +99,11 @@ MODULE_OBJS += \
 	networking/sdl_net/uploadfileclienthandler.o
 endif
 
+ifdef USE_ENET
+MODULE_OBJS += \
+	networking/enet/enet.o
+endif
+
 ifdef USE_ELF_LOADER
 MODULE_OBJS += \
 	plugins/elf/arm-loader.o \
diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
new file mode 100644
index 00000000000..621d4eb68da
--- /dev/null
+++ b/backends/networking/enet/enet.cpp
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <enet/enet.h>
+#include "backends/networking/enet/enet.h"
+#include "common/debug.h"
+
+namespace Networking {
+
+ENet::ENet() {
+	_initialized = false;
+}
+
+ENet::~ENet() {
+	if (_initialized) {
+		// Deinitialize the library.
+		debug(1, "ENet: Deinitalizing.");
+		enet_deinitialize();
+	}
+}
+
+bool ENet::initalize() {
+	if (ENet::_initialized) {
+		return true;
+	}
+
+	if (enet_initialize() != 0) {
+		warning("ENet: ENet library failed to initalize.");
+		return false;
+	}
+	debug(1, "ENet: Initalized.");
+	_initialized = true;
+	return true;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
new file mode 100644
index 00000000000..680486bc9c3
--- /dev/null
+++ b/backends/networking/enet/enet.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_NETWORKING_ENET_ENET
+#define BACKENDS_NETWORKING_ENET_ENET
+
+namespace Networking {
+
+class ENet {
+public:
+	ENet();
+	~ENet();
+
+	bool initalize();
+protected:
+	bool _initialized;
+};
+	
+} // End of namespace Networking
+
+
+#endif


Commit: 129b56cd3ef3cba5117287cac747d6c81d40a7e6
    https://github.com/scummvm/scummvm/commit/129b56cd3ef3cba5117287cac747d6c81d40a7e6
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Host creation.

Changed paths:
  A backends/networking/enet/host.cpp
  A backends/networking/enet/host.h
    backends/module.mk
    backends/networking/enet/enet.cpp
    backends/networking/enet/enet.h


diff --git a/backends/module.mk b/backends/module.mk
index e580881267c..a1ad930aba7 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -101,7 +101,8 @@ endif
 
 ifdef USE_ENET
 MODULE_OBJS += \
-	networking/enet/enet.o
+	networking/enet/enet.o \
+	networking/enet/host.o
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index 621d4eb68da..9537fe818e6 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -23,6 +23,7 @@
 
 #include <enet/enet.h>
 #include "backends/networking/enet/enet.h"
+#include "backends/networking/enet/host.h"
 #include "common/debug.h"
 
 namespace Networking {
@@ -45,7 +46,7 @@ bool ENet::initalize() {
 	}
 
 	if (enet_initialize() != 0) {
-		warning("ENet: ENet library failed to initalize.");
+		warning("ENet: ENet library failed to initalize");
 		return false;
 	}
 	debug(1, "ENet: Initalized.");
@@ -53,4 +54,20 @@ bool ENet::initalize() {
 	return true;
 }
 
+Host* ENet::create_host(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand) {
+	ENetAddress _address;
+	ENetHost *_host;
+
+	enet_address_set_host(&_address, address.c_str());
+	_address.port = port;
+
+	_host = enet_host_create(&_address, numClients, numChannels, incBand, outBand);
+	if (_host == nullptr) {
+		warning("ENet: An error occured when trying to create host with address %s:%d", address.c_str(), port);
+		return nullptr;
+	}
+
+	return new Host(_host);
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index 680486bc9c3..720c30a41c1 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -19,18 +19,23 @@
  *
  */
 
-#ifndef BACKENDS_NETWORKING_ENET_ENET
-#define BACKENDS_NETWORKING_ENET_ENET
+#ifndef BACKENDS_NETWORKING_ENET_ENET_H
+#define BACKENDS_NETWORKING_ENET_ENET_H
+
+#include "common/str.h"
 
 namespace Networking {
 
+class Host;
+
 class ENet {
 public:
 	ENet();
 	~ENet();
 
 	bool initalize();
-protected:
+	Host* create_host(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand);
+private:
 	bool _initialized;
 };
 	
diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
new file mode 100644
index 00000000000..bd81cc9faf7
--- /dev/null
+++ b/backends/networking/enet/host.cpp
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <enet/enet.h>
+#include "backends/networking/enet/host.h"
+
+namespace Networking {
+
+Host::Host(ENetHost *host) {
+	_host = host;
+}
+
+Host::~Host() {
+	enet_host_destroy(_host);
+}
+
+ENetEvent Host::service(int timeout) {
+	ENetEvent event;
+	enet_host_service(_host, &event, timeout);
+	return event;
+}
+	
+} // End of namespace Networking
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
new file mode 100644
index 00000000000..e5cad6041e6
--- /dev/null
+++ b/backends/networking/enet/host.h
@@ -0,0 +1,48 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_NETWORKING_ENET_HOST_H
+#define BACKENDS_NETWORKING_ENET_HOST_H
+
+typedef struct _ENetHost ENetHost;
+typedef struct _ENetPeer ENetPeer;
+
+typedef struct _ENetAddress ENetAddress;
+typedef struct _ENetEvent ENetEvent;
+
+#include "common/str.h"
+
+namespace Networking {
+
+class Host {
+public:
+	Host(ENetHost *host);
+	~Host();
+
+	ENetEvent service(int timeout = 0);
+private:
+	ENetHost *_host;
+
+};
+	
+} // End of namespace Networking
+
+#endif


Commit: 7bc36d05fcdb75be8c0c5a425b13734e7a651f11
    https://github.com/scummvm/scummvm/commit/7bc36d05fcdb75be8c0c5a425b13734e7a651f11
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Client host creation.

Changed paths:
    backends/networking/enet/enet.cpp
    backends/networking/enet/enet.h
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h


diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index 9537fe818e6..0f1ac6b0e35 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -56,12 +56,11 @@ bool ENet::initalize() {
 
 Host* ENet::create_host(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand) {
 	ENetAddress _address;
-	ENetHost *_host;
-
+	// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
 	enet_address_set_host(&_address, address.c_str());
 	_address.port = port;
 
-	_host = enet_host_create(&_address, numClients, numChannels, incBand, outBand);
+	ENetHost *_host = enet_host_create(&_address, numClients, numChannels, incBand, outBand);
 	if (_host == nullptr) {
 		warning("ENet: An error occured when trying to create host with address %s:%d", address.c_str(), port);
 		return nullptr;
@@ -70,4 +69,38 @@ Host* ENet::create_host(Common::String address, int port, int numClients, int nu
 	return new Host(_host);
 }
 
+Host* ENet::connect_to_host(Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
+	// NOTE: Number of channels must match with the server's.
+	ENetHost *_host = enet_host_create(nullptr, 1, numChannels, incBand, outBand);
+	if (_host == nullptr) {
+		warning("ENet: An error occured when trying to create client host");
+		return nullptr;
+	}
+
+	ENetAddress _address;
+	if (address == "255.255.255.255") {
+		_address.host = ENET_HOST_BROADCAST;
+	} else {
+		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
+		enet_address_set_host(&_address, address.c_str());
+	}
+	_address.port = port;
+
+	// Connect to server address
+	ENetPeer *_peer = enet_host_connect(_host, &_address, numChannels, 0);
+
+	ENetEvent event;
+	if (enet_host_service(_host, &event, timeout) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
+		debug(1, "ENet: Connection to %s:%d succeeded.", address.c_str(), port);
+		
+		ENetPacket *packet = enet_packet_create("Hello, world!", strlen("Hello, world!") + 1, ENET_PACKET_FLAG_RELIABLE);
+		enet_peer_send(_peer, 0, packet);
+		enet_host_flush(_host);
+
+		return new Host(_host, _peer);
+	}
+	warning("ENet: Connection to %s:%d failed", address.c_str(), port);
+	return nullptr;
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index 720c30a41c1..a0304d7b66c 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -34,7 +34,8 @@ public:
 	~ENet();
 
 	bool initalize();
-	Host* create_host(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand);
+	Host* create_host(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
+	Host* connect_to_host(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
 private:
 	bool _initialized;
 };
diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index bd81cc9faf7..894b2a98e24 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -28,16 +28,57 @@ namespace Networking {
 
 Host::Host(ENetHost *host) {
 	_host = host;
+	_serverPeer = nullptr;
+	_recentEvent = nullptr;
+	_recentPacket = nullptr;
+}
+
+Host::Host(ENetHost *host, ENetPeer *serverPeer) {
+	_host = host;
+	_serverPeer = serverPeer;
+	_recentEvent = nullptr;
+	_recentPacket = nullptr;
 }
 
 Host::~Host() {
+	if (_recentPacket)
+		destroy_packet();
 	enet_host_destroy(_host);
 }
 
-ENetEvent Host::service(int timeout) {
+uint8 Host::service(int timeout) {
 	ENetEvent event;
 	enet_host_service(_host, &event, timeout);
-	return event;
+	_recentEvent = &event;
+	if (event.type == ENET_EVENT_TYPE_RECEIVE) {
+		if (_recentPacket)
+			destroy_packet();
+		_recentPacket = event.packet;
+	}
+	return event.type;
+}
+
+Common::String Host::get_host() {
+	if (!_recentEvent)
+		return "";
+
+	char _hostName[50];
+	if (enet_address_get_host_ip(&_recentEvent->peer->address, _hostName, 50) == 0)
+		return Common::String(_hostName);
+	return "";
+}
+
+int Host::get_port() {
+	if (!_recentEvent || !_recentEvent->peer)
+		return 0;
+	return _recentEvent->peer->address.port;
+}
+
+void Host::destroy_packet() {
+	if (!_recentPacket)
+		return;
+	enet_packet_destroy(_recentPacket);
+	_recentPacket = nullptr;
 }
 	
 } // End of namespace Networking
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index e5cad6041e6..557316a83b3 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -25,8 +25,15 @@
 typedef struct _ENetHost ENetHost;
 typedef struct _ENetPeer ENetPeer;
 
-typedef struct _ENetAddress ENetAddress;
 typedef struct _ENetEvent ENetEvent;
+typedef struct _ENetPacket ENetPacket;
+
+
+// Event types
+#define ENET_EVENT_TYPE_NONE 0
+#define ENET_EVENT_TYPE_CONNECT 1
+#define ENET_EVENT_TYPE_DISCONNECT 2
+#define ENET_EVENT_TYPE_RECEIVE 3
 
 #include "common/str.h"
 
@@ -35,11 +42,20 @@ namespace Networking {
 class Host {
 public:
 	Host(ENetHost *host);
+	Host(ENetHost *host, ENetPeer *serverPeer);
 	~Host();
 
-	ENetEvent service(int timeout = 0);
+	uint8 service(int timeout = 0);
+
+	Common::String get_host();
+	int get_port();
+	void destroy_packet();
 private:
 	ENetHost *_host;
+	ENetPeer *_serverPeer; // Only used for clients.
+	ENetEvent *_recentEvent;
+	ENetPacket *_recentPacket;
+
 
 };
 	


Commit: 64200c86435e853111caa8e410b155aa9f334295
    https://github.com/scummvm/scummvm/commit/64200c86435e853111caa8e410b155aa9f334295
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Raw UDP socket creation.

Changed paths:
  A backends/networking/enet/socket.cpp
  A backends/networking/enet/socket.h
    backends/module.mk
    backends/networking/enet/enet.cpp
    backends/networking/enet/enet.h


diff --git a/backends/module.mk b/backends/module.mk
index a1ad930aba7..76f92728568 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -102,7 +102,8 @@ endif
 ifdef USE_ENET
 MODULE_OBJS += \
 	networking/enet/enet.o \
-	networking/enet/host.o
+	networking/enet/host.o \
+	networking/enet/socket.o
 endif
 
 ifdef USE_ELF_LOADER
diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index 0f1ac6b0e35..62736ff9b5d 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -24,6 +24,7 @@
 #include <enet/enet.h>
 #include "backends/networking/enet/enet.h"
 #include "backends/networking/enet/host.h"
+#include "backends/networking/enet/socket.h"
 #include "common/debug.h"
 
 namespace Networking {
@@ -92,15 +93,39 @@ Host* ENet::connect_to_host(Common::String address, int port, int timeout, int n
 	ENetEvent event;
 	if (enet_host_service(_host, &event, timeout) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
 		debug(1, "ENet: Connection to %s:%d succeeded.", address.c_str(), port);
-		
-		ENetPacket *packet = enet_packet_create("Hello, world!", strlen("Hello, world!") + 1, ENET_PACKET_FLAG_RELIABLE);
-		enet_peer_send(_peer, 0, packet);
-		enet_host_flush(_host);
-
 		return new Host(_host, _peer);
 	}
 	warning("ENet: Connection to %s:%d failed", address.c_str(), port);
 	return nullptr;
 }
 
+Socket* ENet::create_socket(Common::String address, int port) {
+	ENetAddress _address;
+	if (address == "255.255.255.255") {
+		_address.host = ENET_HOST_BROADCAST;
+	} else {
+		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
+		enet_address_set_host(&_address, address.c_str());
+	}
+	_address.port = port;
+
+	ENetSocket _socket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
+	if (_socket == ENET_SOCKET_NULL) {
+		warning("ENet: Unable to create socket");
+		return nullptr;
+	}
+	if (enet_socket_bind(_socket, &_address) < 0) {
+		warning("ENet: Unable to bind socket to address %s:%d", address.c_str(), port);
+		enet_socket_destroy(_socket);
+		return nullptr;
+	}
+
+	enet_socket_set_option (_socket, ENET_SOCKOPT_NONBLOCK, 1);
+    enet_socket_set_option (_socket, ENET_SOCKOPT_BROADCAST, 1);
+	enet_socket_set_option (_socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
+    enet_socket_set_option (_socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
+
+	return new Socket(_socket);
+}
+
 } // End of namespace Networking
diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index a0304d7b66c..b54d380fb5c 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -27,6 +27,7 @@
 namespace Networking {
 
 class Host;
+class Socket;
 
 class ENet {
 public:
@@ -36,6 +37,7 @@ public:
 	bool initalize();
 	Host* create_host(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
 	Host* connect_to_host(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
+	Socket* create_socket(Common::String address, int port);
 private:
 	bool _initialized;
 };
diff --git a/backends/networking/enet/socket.cpp b/backends/networking/enet/socket.cpp
new file mode 100644
index 00000000000..ce36d5c064d
--- /dev/null
+++ b/backends/networking/enet/socket.cpp
@@ -0,0 +1,103 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include <enet/enet.h>
+#include "backends/networking/enet/socket.h"
+#include "common/debug.h"
+
+namespace Networking {
+
+Socket::Socket(ENetSocket socket) {
+	_socket = socket;
+	_recentData = Common::String();
+	_recentHost = Common::String();
+	_recentPort = 0;
+}
+Socket::~Socket() {
+	enet_socket_destroy(_socket);
+}
+
+bool Socket::send(Common::String address, int port, const char *data) {
+	ENetAddress _address;
+	if (address == "255.255.255.255") {
+		_address.host = ENET_HOST_BROADCAST;
+	} else {
+		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
+		enet_address_set_host(&_address, address.c_str());
+	}
+	_address.port = port;
+
+	ENetBuffer _buffer;
+	_buffer.data = const_cast<char *>(data);
+	_buffer.dataLength = strlen(data);
+
+	int sentLength = enet_socket_send(_socket, &_address, &_buffer, 1);
+	if (sentLength < 0)
+		return false;
+	
+	return true;
+}
+
+bool Socket::receive() {
+	ENetBuffer _buffer;
+
+	char data[4096]; // ENET_PROTOCOL_MAXIMUM_MTU
+	_buffer.data = data;
+	_buffer.dataLength = sizeof(data);
+
+	ENetAddress _address;
+
+	int receivedLength = enet_socket_receive(_socket, &_address, &_buffer, 1);
+	if (receivedLength < 0) {
+		warning("ENet: An error has occured when receiving data from socket");
+		return false;
+	}
+
+	if (receivedLength == 0)
+		return false;
+	
+	_recentData = Common::String((const char*)data, receivedLength);
+
+	char _hostName[15];
+	if (enet_address_get_host_ip(&_address, _hostName, sizeof(_hostName)) == 0)
+		_recentHost = _hostName;
+	else
+		_recentHost = "";
+	_recentPort = _address.port;
+
+	return true;
+}
+
+Common::String Socket::get_data() {
+	return _recentData;
+}
+
+Common::String Socket::get_host() {
+	return _recentHost;
+}
+
+int Socket::get_port() {
+	return _recentPort;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/enet/socket.h b/backends/networking/enet/socket.h
new file mode 100644
index 00000000000..47837ec6ba1
--- /dev/null
+++ b/backends/networking/enet/socket.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef BACKENDS_NETWORKING_ENET_SOCKET_H
+#define BACKENDS_NETWORKING_ENET_SOCKET_H
+
+#ifdef WIN32
+// TODO: Test me.
+#include <winsock2.h>
+typedef SOCKET ENetSocket;
+#else
+typedef int ENetSocket;
+#endif
+
+#include "common/str.h"
+
+namespace Networking {
+
+class Socket {
+public:
+	Socket(ENetSocket socket);
+	~Socket();
+
+	bool send(Common::String address, int port, const char *data);
+	bool receive();
+
+	Common::String get_data();
+
+	Common::String get_host();
+	int get_port();
+private:
+	ENetSocket _socket;
+	Common::String _recentData;
+	Common::String _recentHost;
+	int _recentPort;
+};
+
+} // End of namespace Networking
+
+#endif


Commit: 6305d74464e0deb47494226729e9b5d3b49a15a6
    https://github.com/scummvm/scummvm/commit/6305d74464e0deb47494226729e9b5d3b49a15a6
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Host data send and retrieval.

Changed paths:
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 894b2a98e24..981cd86b518 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -74,11 +74,70 @@ int Host::get_port() {
 	return _recentEvent->peer->address.port;
 }
 
+int Host::get_peer_index_from_host(Common::String host, int port) {
+	for (int i = 0; i < (int)_host->peerCount; i++) {
+		char _hostName[50];
+		if (enet_address_get_host_ip(&_host->peers[i].address, _hostName, 50) == 0) {
+			if (host == _hostName && port == _host->peers[i].address.port) {
+				return i;
+			}
+		}
+	}
+	return -1;
+}
+
+Common::String Host::get_packet_data() {
+	if (!_recentPacket)
+		return "";
+	return Common::String((const char*)_recentPacket->data, (uint32)_recentPacket->dataLength);
+}
+
 void Host::destroy_packet() {
 	if (!_recentPacket)
 		return;
 	enet_packet_destroy(_recentPacket);
 	_recentPacket = nullptr;
 }
+
+bool Host::send(const char *data, int peerIndex, int channel, bool reliable) {
+	ENetPeer *peer;
+	if (_serverPeer) {
+		peer = _serverPeer;
+	} else {
+		if (peerIndex > (int)_host->peerCount) {
+			warning("ENet: Peer index (%d) is too high", peerIndex);
+			return false;
+		}
+		peer = &_host->peers[peerIndex];
+	}
+
+	ENetPacket *packet = enet_packet_create(const_cast<char *>(data), strlen(data), (reliable) ? ENET_PACKET_FLAG_RELIABLE : 0);
+	enet_peer_send(peer, channel, packet);
+
+	enet_host_flush(_host);
+	return true;
+}
+
+bool Host::send_raw_data(Common::String address, int port, const char *data) {
+	ENetAddress _address;
+	if (address == "255.255.255.255") {
+		_address.host = ENET_HOST_BROADCAST;
+	} else {
+		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
+		enet_address_set_host(&_address, address.c_str());
+	}
+	_address.port = port;
+
+	ENetBuffer _buffer;
+	_buffer.data = const_cast<char *>(data);
+	_buffer.dataLength = strlen(data);
+
+	int sentLength = enet_socket_send(_host->socket, &_address, &_buffer, 1);
+	if (sentLength < 0)
+		return false;
+	
+	return true;
+
+}
 	
 } // End of namespace Networking
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index 557316a83b3..a991987df74 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -36,6 +36,7 @@ typedef struct _ENetPacket ENetPacket;
 #define ENET_EVENT_TYPE_RECEIVE 3
 
 #include "common/str.h"
+#include "common/debug.h"
 
 namespace Networking {
 
@@ -47,8 +48,15 @@ public:
 
 	uint8 service(int timeout = 0);
 
+	bool send(const char *data, int peerIndex, int channel = 0, bool reliable = true);
+	bool send_raw_data(Common::String address, int port, const char *data);
+
 	Common::String get_host();
 	int get_port();
+
+	int get_peer_index_from_host(Common::String host, int port);
+
+	Common::String get_packet_data();
 	void destroy_packet();
 private:
 	ENetHost *_host;


Commit: 619daae9361cb713336a935b15fcce544ffb9e45
    https://github.com/scummvm/scummvm/commit/619daae9361cb713336a935b15fcce544ffb9e45
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Code formatting.

Changed paths:
    backends/networking/enet/enet.cpp
    backends/networking/enet/enet.h
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h
    backends/networking/enet/socket.cpp
    backends/networking/enet/socket.h


diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index 62736ff9b5d..6df58b65308 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -55,13 +55,13 @@ bool ENet::initalize() {
 	return true;
 }
 
-Host* ENet::create_host(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand) {
-	ENetAddress _address;
+Host* ENet::createHost(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand) {
+	ENetAddress enetAddress;
 	// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
-	enet_address_set_host(&_address, address.c_str());
-	_address.port = port;
+	enet_address_set_host(&enetAddress, address.c_str());
+	enetAddress.port = port;
 
-	ENetHost *_host = enet_host_create(&_address, numClients, numChannels, incBand, outBand);
+	ENetHost *_host = enet_host_create(&enetAddress, numClients, numChannels, incBand, outBand);
 	if (_host == nullptr) {
 		warning("ENet: An error occured when trying to create host with address %s:%d", address.c_str(), port);
 		return nullptr;
@@ -70,62 +70,62 @@ Host* ENet::create_host(Common::String address, int port, int numClients, int nu
 	return new Host(_host);
 }
 
-Host* ENet::connect_to_host(Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
+Host* ENet::connectToHost(Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
 	// NOTE: Number of channels must match with the server's.
-	ENetHost *_host = enet_host_create(nullptr, 1, numChannels, incBand, outBand);
-	if (_host == nullptr) {
+	ENetHost *enetHost = enet_host_create(nullptr, 1, numChannels, incBand, outBand);
+	if (enetHost == nullptr) {
 		warning("ENet: An error occured when trying to create client host");
 		return nullptr;
 	}
 
-	ENetAddress _address;
+	ENetAddress enetAddress;
 	if (address == "255.255.255.255") {
-		_address.host = ENET_HOST_BROADCAST;
+		enetAddress.host = ENET_HOST_BROADCAST;
 	} else {
 		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
-		enet_address_set_host(&_address, address.c_str());
+		enet_address_set_host(&enetAddress, address.c_str());
 	}
-	_address.port = port;
+	enetAddress.port = port;
 
 	// Connect to server address
-	ENetPeer *_peer = enet_host_connect(_host, &_address, numChannels, 0);
+	ENetPeer *enetPeer = enet_host_connect(enetHost, &enetAddress, numChannels, 0);
 
 	ENetEvent event;
-	if (enet_host_service(_host, &event, timeout) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
+	if (enet_host_service(enetHost, &event, timeout) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
 		debug(1, "ENet: Connection to %s:%d succeeded.", address.c_str(), port);
-		return new Host(_host, _peer);
+		return new Host(enetHost, enetPeer);
 	}
 	warning("ENet: Connection to %s:%d failed", address.c_str(), port);
 	return nullptr;
 }
 
-Socket* ENet::create_socket(Common::String address, int port) {
-	ENetAddress _address;
+Socket* ENet::createSocket(Common::String address, int port) {
+	ENetAddress enetAddress;
 	if (address == "255.255.255.255") {
-		_address.host = ENET_HOST_BROADCAST;
+		enetAddress.host = ENET_HOST_BROADCAST;
 	} else {
 		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
-		enet_address_set_host(&_address, address.c_str());
+		enet_address_set_host(&enetAddress, address.c_str());
 	}
-	_address.port = port;
+	enetAddress.port = port;
 
-	ENetSocket _socket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
-	if (_socket == ENET_SOCKET_NULL) {
+	ENetSocket enetSocket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
+	if (enetSocket == ENET_SOCKET_NULL) {
 		warning("ENet: Unable to create socket");
 		return nullptr;
 	}
-	if (enet_socket_bind(_socket, &_address) < 0) {
+	if (enet_socket_bind(enetSocket, &enetAddress) < 0) {
 		warning("ENet: Unable to bind socket to address %s:%d", address.c_str(), port);
-		enet_socket_destroy(_socket);
+		enet_socket_destroy(enetSocket);
 		return nullptr;
 	}
 
-	enet_socket_set_option (_socket, ENET_SOCKOPT_NONBLOCK, 1);
-    enet_socket_set_option (_socket, ENET_SOCKOPT_BROADCAST, 1);
-	enet_socket_set_option (_socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
-    enet_socket_set_option (_socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
+	enet_socket_set_option (enetSocket, ENET_SOCKOPT_NONBLOCK, 1);
+    enet_socket_set_option (enetSocket, ENET_SOCKOPT_BROADCAST, 1);
+	enet_socket_set_option (enetSocket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
+    enet_socket_set_option (enetSocket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
 
-	return new Socket(_socket);
+	return new Socket(enetSocket);
 }
 
 } // End of namespace Networking
diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index b54d380fb5c..f9907bcb4fa 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -35,9 +35,9 @@ public:
 	~ENet();
 
 	bool initalize();
-	Host* create_host(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
-	Host* connect_to_host(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
-	Socket* create_socket(Common::String address, int port);
+	Host* createHost(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
+	Host* connectToHost(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
+	Socket* createSocket(Common::String address, int port);
 private:
 	bool _initialized;
 };
diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 981cd86b518..3bb407ba40f 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -42,7 +42,7 @@ Host::Host(ENetHost *host, ENetPeer *serverPeer) {
 
 Host::~Host() {
 	if (_recentPacket)
-		destroy_packet();
+		destroyPacket();
 	enet_host_destroy(_host);
 }
 
@@ -52,13 +52,13 @@ uint8 Host::service(int timeout) {
 	_recentEvent = &event;
 	if (event.type == ENET_EVENT_TYPE_RECEIVE) {
 		if (_recentPacket)
-			destroy_packet();
+			destroyPacket();
 		_recentPacket = event.packet;
 	}
 	return event.type;
 }
 
-Common::String Host::get_host() {
+Common::String Host::getHost() {
 	if (!_recentEvent)
 		return "";
 
@@ -68,13 +68,13 @@ Common::String Host::get_host() {
 	return "";
 }
 
-int Host::get_port() {
+int Host::getPort() {
 	if (!_recentEvent || !_recentEvent->peer)
 		return 0;
 	return _recentEvent->peer->address.port;
 }
 
-int Host::get_peer_index_from_host(Common::String host, int port) {
+int Host::getPeerIndexFromHost(Common::String host, int port) {
 	for (int i = 0; i < (int)_host->peerCount; i++) {
 		char _hostName[50];
 		if (enet_address_get_host_ip(&_host->peers[i].address, _hostName, 50) == 0) {
@@ -86,13 +86,13 @@ int Host::get_peer_index_from_host(Common::String host, int port) {
 	return -1;
 }
 
-Common::String Host::get_packet_data() {
+Common::String Host::getPacketData() {
 	if (!_recentPacket)
 		return "";
 	return Common::String((const char*)_recentPacket->data, (uint32)_recentPacket->dataLength);
 }
 
-void Host::destroy_packet() {
+void Host::destroyPacket() {
 	if (!_recentPacket)
 		return;
 	enet_packet_destroy(_recentPacket);
@@ -118,21 +118,21 @@ bool Host::send(const char *data, int peerIndex, int channel, bool reliable) {
 	return true;
 }
 
-bool Host::send_raw_data(Common::String address, int port, const char *data) {
-	ENetAddress _address;
+bool Host::sendRawData(Common::String address, int port, const char *data) {
+	ENetAddress enetAddress;
 	if (address == "255.255.255.255") {
-		_address.host = ENET_HOST_BROADCAST;
+		enetAddress.host = ENET_HOST_BROADCAST;
 	} else {
 		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
-		enet_address_set_host(&_address, address.c_str());
+		enet_address_set_host(&enetAddress, address.c_str());
 	}
-	_address.port = port;
+	enetAddress.port = port;
 
 	ENetBuffer _buffer;
 	_buffer.data = const_cast<char *>(data);
 	_buffer.dataLength = strlen(data);
 
-	int sentLength = enet_socket_send(_host->socket, &_address, &_buffer, 1);
+	int sentLength = enet_socket_send(_host->socket, &enetAddress, &_buffer, 1);
 	if (sentLength < 0)
 		return false;
 	
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index a991987df74..63ba579ccb6 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -49,15 +49,15 @@ public:
 	uint8 service(int timeout = 0);
 
 	bool send(const char *data, int peerIndex, int channel = 0, bool reliable = true);
-	bool send_raw_data(Common::String address, int port, const char *data);
+	bool sendRawData(Common::String address, int port, const char *data);
 
-	Common::String get_host();
-	int get_port();
+	Common::String getHost();
+	int getPort();
 
-	int get_peer_index_from_host(Common::String host, int port);
+	int getPeerIndexFromHost(Common::String host, int port);
 
-	Common::String get_packet_data();
-	void destroy_packet();
+	Common::String getPacketData();
+	void destroyPacket();
 private:
 	ENetHost *_host;
 	ENetPeer *_serverPeer; // Only used for clients.
diff --git a/backends/networking/enet/socket.cpp b/backends/networking/enet/socket.cpp
index ce36d5c064d..dcfc91e6529 100644
--- a/backends/networking/enet/socket.cpp
+++ b/backends/networking/enet/socket.cpp
@@ -38,20 +38,20 @@ Socket::~Socket() {
 }
 
 bool Socket::send(Common::String address, int port, const char *data) {
-	ENetAddress _address;
+	ENetAddress enetAddress;
 	if (address == "255.255.255.255") {
-		_address.host = ENET_HOST_BROADCAST;
+		enetAddress.host = ENET_HOST_BROADCAST;
 	} else {
 		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
-		enet_address_set_host(&_address, address.c_str());
+		enet_address_set_host(&enetAddress, address.c_str());
 	}
-	_address.port = port;
+	enetAddress.port = port;
 
-	ENetBuffer _buffer;
-	_buffer.data = const_cast<char *>(data);
-	_buffer.dataLength = strlen(data);
+	ENetBuffer buffer;
+	buffer.data = const_cast<char *>(data);
+	buffer.dataLength = strlen(data);
 
-	int sentLength = enet_socket_send(_socket, &_address, &_buffer, 1);
+	int sentLength = enet_socket_send(_socket, &enetAddress, &buffer, 1);
 	if (sentLength < 0)
 		return false;
 	
@@ -59,15 +59,15 @@ bool Socket::send(Common::String address, int port, const char *data) {
 }
 
 bool Socket::receive() {
-	ENetBuffer _buffer;
+	ENetBuffer buffer;
 
 	char data[4096]; // ENET_PROTOCOL_MAXIMUM_MTU
-	_buffer.data = data;
-	_buffer.dataLength = sizeof(data);
+	buffer.data = data;
+	buffer.dataLength = sizeof(data);
 
 	ENetAddress _address;
 
-	int receivedLength = enet_socket_receive(_socket, &_address, &_buffer, 1);
+	int receivedLength = enet_socket_receive(_socket, &_address, &buffer, 1);
 	if (receivedLength < 0) {
 		warning("ENet: An error has occured when receiving data from socket");
 		return false;
@@ -78,9 +78,9 @@ bool Socket::receive() {
 	
 	_recentData = Common::String((const char*)data, receivedLength);
 
-	char _hostName[15];
-	if (enet_address_get_host_ip(&_address, _hostName, sizeof(_hostName)) == 0)
-		_recentHost = _hostName;
+	char hostName[15];
+	if (enet_address_get_host_ip(&_address, hostName, sizeof(hostName)) == 0)
+		_recentHost = hostName;
 	else
 		_recentHost = "";
 	_recentPort = _address.port;
@@ -88,15 +88,15 @@ bool Socket::receive() {
 	return true;
 }
 
-Common::String Socket::get_data() {
+Common::String Socket::getData() {
 	return _recentData;
 }
 
-Common::String Socket::get_host() {
+Common::String Socket::getHost() {
 	return _recentHost;
 }
 
-int Socket::get_port() {
+int Socket::getPort() {
 	return _recentPort;
 }
 
diff --git a/backends/networking/enet/socket.h b/backends/networking/enet/socket.h
index 47837ec6ba1..beaef096529 100644
--- a/backends/networking/enet/socket.h
+++ b/backends/networking/enet/socket.h
@@ -42,10 +42,10 @@ public:
 	bool send(Common::String address, int port, const char *data);
 	bool receive();
 
-	Common::String get_data();
+	Common::String getData();
 
-	Common::String get_host();
-	int get_port();
+	Common::String getHost();
+	int getPort();
 private:
 	ENetSocket _socket;
 	Common::String _recentData;


Commit: 5f5de8122b4114f7b7c17b77507c09055af44dc9
    https://github.com/scummvm/scummvm/commit/5f5de8122b4114f7b7c17b77507c09055af44dc9
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Fix pointer formatting.

Changed paths:
    backends/networking/enet/enet.cpp
    backends/networking/enet/enet.h


diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index 6df58b65308..a701d63739e 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -55,7 +55,7 @@ bool ENet::initalize() {
 	return true;
 }
 
-Host* ENet::createHost(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand) {
+Host *ENet::createHost(Common::String address, int port, int numClients, int numChannels, int incBand, int outBand) {
 	ENetAddress enetAddress;
 	// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
 	enet_address_set_host(&enetAddress, address.c_str());
@@ -70,7 +70,7 @@ Host* ENet::createHost(Common::String address, int port, int numClients, int num
 	return new Host(_host);
 }
 
-Host* ENet::connectToHost(Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
+Host *ENet::connectToHost(Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
 	// NOTE: Number of channels must match with the server's.
 	ENetHost *enetHost = enet_host_create(nullptr, 1, numChannels, incBand, outBand);
 	if (enetHost == nullptr) {
@@ -99,7 +99,7 @@ Host* ENet::connectToHost(Common::String address, int port, int timeout, int num
 	return nullptr;
 }
 
-Socket* ENet::createSocket(Common::String address, int port) {
+Socket *ENet::createSocket(Common::String address, int port) {
 	ENetAddress enetAddress;
 	if (address == "255.255.255.255") {
 		enetAddress.host = ENET_HOST_BROADCAST;
diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index f9907bcb4fa..d9b08a82964 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -35,9 +35,9 @@ public:
 	~ENet();
 
 	bool initalize();
-	Host* createHost(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
-	Host* connectToHost(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
-	Socket* createSocket(Common::String address, int port);
+	Host *createHost(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
+	Host *connectToHost(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
+	Socket *createSocket(Common::String address, int port);
 private:
 	bool _initialized;
 };


Commit: 8a673b4a0cc7c6fca90b9b36b1135054e0332c05
    https://github.com/scummvm/scummvm/commit/8a673b4a0cc7c6fca90b9b36b1135054e0332c05
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Ability to disconnect peers.

Changed paths:
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 3bb407ba40f..05b85f7159f 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -58,6 +58,26 @@ uint8 Host::service(int timeout) {
 	return event.type;
 }
 
+void Host::disconnectPeer(int peerIndex) {
+	// calling _later will ensure that all the queued packets are sent before disconnecting.
+	enet_peer_disconnect_later(&_host->peers[peerIndex], 0);
+	enet_host_flush(_host);
+
+	if (_serverPeer) {
+		// Allow 3 second for disconnection to succeed and drop incoming packets
+		while(uint type = service(3000) > 0) {
+			switch(type) {
+			case ENET_EVENT_TYPE_RECEIVE:
+				destroyPacket();
+				break;
+			case ENET_EVENT_TYPE_DISCONNECT:
+				// Disconnect succeeded.
+				return;
+			}
+		}
+	}
+}
+
 Common::String Host::getHost() {
 	if (!_recentEvent)
 		return "";
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index 63ba579ccb6..bc83a0f1777 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -48,6 +48,8 @@ public:
 
 	uint8 service(int timeout = 0);
 
+	void disconnectPeer(int peerIndex);
+
 	bool send(const char *data, int peerIndex, int channel = 0, bool reliable = true);
 	bool sendRawData(Common::String address, int port, const char *data);
 


Commit: 747b28504ad769b96658b17b239fe99c6434ae08
    https://github.com/scummvm/scummvm/commit/747b28504ad769b96658b17b239fe99c6434ae08
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Use delayMillis for disconnection.

Changed paths:
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 05b85f7159f..ea62aa2fe4a 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -64,9 +64,10 @@ void Host::disconnectPeer(int peerIndex) {
 	enet_host_flush(_host);
 
 	if (_serverPeer) {
-		// Allow 3 second for disconnection to succeed and drop incoming packets
-		while(uint type = service(3000) > 0) {
-			switch(type) {
+		// Allow 3 seconds for disconnection to succeed and drop incoming packets
+		uint tickCount = 0;
+		while(tickCount < 3000) {
+			switch(service(0)) {
 			case ENET_EVENT_TYPE_RECEIVE:
 				destroyPacket();
 				break;
@@ -74,6 +75,8 @@ void Host::disconnectPeer(int peerIndex) {
 				// Disconnect succeeded.
 				return;
 			}
+			g_system->delayMillis(5);
+			tickCount += 1;
 		}
 	}
 }
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index bc83a0f1777..566a43f50e5 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -35,6 +35,7 @@ typedef struct _ENetPacket ENetPacket;
 #define ENET_EVENT_TYPE_DISCONNECT 2
 #define ENET_EVENT_TYPE_RECEIVE 3
 
+#include "common/system.h"
 #include "common/str.h"
 #include "common/debug.h"
 


Commit: 1a50e10a8327a86bfdb058f9b2769b1535e3572b
    https://github.com/scummvm/scummvm/commit/1a50e10a8327a86bfdb058f9b2769b1535e3572b
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Now compiles and works on MSVC.

Changed paths:
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h
    backends/networking/enet/socket.h
    devtools/create_project/create_project.cpp
    devtools/create_project/msvc.cpp


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index ea62aa2fe4a..0eaee3b5d68 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -29,14 +29,16 @@ namespace Networking {
 Host::Host(ENetHost *host) {
 	_host = host;
 	_serverPeer = nullptr;
-	_recentEvent = nullptr;
+	_recentHost = "";
+	_recentPort = 0;
 	_recentPacket = nullptr;
 }
 
 Host::Host(ENetHost *host, ENetPeer *serverPeer) {
 	_host = host;
 	_serverPeer = serverPeer;
-	_recentEvent = nullptr;
+	_recentHost = "";
+	_recentPort = 0;
 	_recentPacket = nullptr;
 }
 
@@ -49,7 +51,14 @@ Host::~Host() {
 uint8 Host::service(int timeout) {
 	ENetEvent event;
 	enet_host_service(_host, &event, timeout);
-	_recentEvent = &event;
+
+	if (event.type > ENET_EVENT_TYPE_NONE) {
+		char hostName[50];
+		if (enet_address_get_host_ip(&event.peer->address, hostName, 50) == 0)
+			_recentHost = Common::String(hostName);
+		_recentPort = event.peer->address.port;
+	}
+
 	if (event.type == ENET_EVENT_TYPE_RECEIVE) {
 		if (_recentPacket)
 			destroyPacket();
@@ -82,19 +91,11 @@ void Host::disconnectPeer(int peerIndex) {
 }
 
 Common::String Host::getHost() {
-	if (!_recentEvent)
-		return "";
-
-	char _hostName[50];
-	if (enet_address_get_host_ip(&_recentEvent->peer->address, _hostName, 50) == 0)
-		return Common::String(_hostName);
-	return "";
+	return _recentHost;
 }
 
 int Host::getPort() {
-	if (!_recentEvent || !_recentEvent->peer)
-		return 0;
-	return _recentEvent->peer->address.port;
+	return _recentPort;
 }
 
 int Host::getPeerIndexFromHost(Common::String host, int port) {
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index 566a43f50e5..3845d7e89a9 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -64,7 +64,8 @@ public:
 private:
 	ENetHost *_host;
 	ENetPeer *_serverPeer; // Only used for clients.
-	ENetEvent *_recentEvent;
+	Common::String _recentHost;
+	int _recentPort;
 	ENetPacket *_recentPacket;
 
 
diff --git a/backends/networking/enet/socket.h b/backends/networking/enet/socket.h
index beaef096529..ed26fc260c6 100644
--- a/backends/networking/enet/socket.h
+++ b/backends/networking/enet/socket.h
@@ -23,8 +23,11 @@
 #define BACKENDS_NETWORKING_ENET_SOCKET_H
 
 #ifdef WIN32
-// TODO: Test me.
-#include <winsock2.h>
+// Including winsock2.h will result in errors, we have to define
+// SOCKET ourselves.
+#include <basetsd.h>
+typedef UINT_PTR SOCKET;
+
 typedef SOCKET ENetSocket;
 #else
 typedef int ENetSocket;
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index b5715a87de2..f93bd51d63e 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -1078,6 +1078,7 @@ const Feature s_features[] = {
 	{ "fluidlite",   "USE_FLUIDLITE", true, false, "FluidLite support" },
 	{   "libcurl",     "USE_LIBCURL", true, true,  "libcurl support" },
 	{    "sdlnet",     "USE_SDL_NET", true, true,  "SDL_net support" },
+	{      "enet",        "USE_ENET", true, true,  "ENet support" },
 	{   "discord",     "USE_DISCORD", true, false, "Discord support" },
 	{ "retrowave",   "USE_RETROWAVE", true, false, "RetroWave OPL3 support" },
 
diff --git a/devtools/create_project/msvc.cpp b/devtools/create_project/msvc.cpp
index 29c504181a0..bc8031d135a 100644
--- a/devtools/create_project/msvc.cpp
+++ b/devtools/create_project/msvc.cpp
@@ -74,6 +74,7 @@ std::string MSVCProvider::getLibraryFromFeature(const char *feature, const Build
 		{   "libcurl", "libcurl.lib",               "libcurl-d.lib", "ws2_32.lib wldap32.lib crypt32.lib normaliz.lib", nullptr },
 		{    "sdlnet", "SDL_net.lib",               nullptr,         "iphlpapi.lib",                                    nullptr },
 		{   "sdl2net", "SDL2_net.lib",              nullptr,         "iphlpapi.lib",                                    "SDL_net.lib" },
+		{      "enet", "enet.lib",                  nullptr,         "winmm.lib ws2_32.lib",                            nullptr },
 		{   "discord", "discord-rpc.lib",           nullptr,         nullptr,                                           nullptr },
 		{ "retrowave", "retrowave.lib",             nullptr,         nullptr,                                           nullptr },
 		// Feature flags with library dependencies


Commit: 06b69d3480ca092cefd57b4e37c78df99f91aeed
    https://github.com/scummvm/scummvm/commit/06b69d3480ca092cefd57b4e37c78df99f91aeed
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Remove trailing whitespace.

Changed paths:
    backends/networking/enet/enet.h
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h
    backends/networking/enet/socket.cpp


diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index d9b08a82964..a84ee14c555 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -41,7 +41,7 @@ public:
 private:
 	bool _initialized;
 };
-	
+
 } // End of namespace Networking
 
 
diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 0eaee3b5d68..e7cc6305bb7 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -159,9 +159,9 @@ bool Host::sendRawData(Common::String address, int port, const char *data) {
 	int sentLength = enet_socket_send(_host->socket, &enetAddress, &_buffer, 1);
 	if (sentLength < 0)
 		return false;
-	
+
 	return true;
 
 }
-	
+
 } // End of namespace Networking
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index 3845d7e89a9..aa66db8d943 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -70,7 +70,7 @@ private:
 
 
 };
-	
+
 } // End of namespace Networking
 
 #endif
diff --git a/backends/networking/enet/socket.cpp b/backends/networking/enet/socket.cpp
index dcfc91e6529..6a462dabd34 100644
--- a/backends/networking/enet/socket.cpp
+++ b/backends/networking/enet/socket.cpp
@@ -54,7 +54,7 @@ bool Socket::send(Common::String address, int port, const char *data) {
 	int sentLength = enet_socket_send(_socket, &enetAddress, &buffer, 1);
 	if (sentLength < 0)
 		return false;
-	
+
 	return true;
 }
 
@@ -75,7 +75,7 @@ bool Socket::receive() {
 
 	if (receivedLength == 0)
 		return false;
-	
+
 	_recentData = Common::String((const char*)data, receivedLength);
 
 	char hostName[15];


Commit: f9ec21c27ae31b53ad311b444fe1960e84878951
    https://github.com/scummvm/scummvm/commit/f9ec21c27ae31b53ad311b444fe1960e84878951
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Fix naming convention

Changed paths:
    backends/networking/enet/host.cpp


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index e7cc6305bb7..0a67654b299 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -100,9 +100,9 @@ int Host::getPort() {
 
 int Host::getPeerIndexFromHost(Common::String host, int port) {
 	for (int i = 0; i < (int)_host->peerCount; i++) {
-		char _hostName[50];
-		if (enet_address_get_host_ip(&_host->peers[i].address, _hostName, 50) == 0) {
-			if (host == _hostName && port == _host->peers[i].address.port) {
+		char hostName[50];
+		if (enet_address_get_host_ip(&_host->peers[i].address, hostName, 50) == 0) {
+			if (host == hostName && port == _host->peers[i].address.port) {
 				return i;
 			}
 		}


Commit: 9329f8c3d675a6a7789500623fd1c8d6ee22b58c
    https://github.com/scummvm/scummvm/commit/9329f8c3d675a6a7789500623fd1c8d6ee22b58c
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Hosts can connect to peers.

Useful for connecting to a "rendezvous" server or some sort.

Changed paths:
    backends/networking/enet/enet.cpp
    backends/networking/enet/enet.h
    backends/networking/enet/host.cpp
    backends/networking/enet/host.h


diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index a701d63739e..bdf3d06595e 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -36,21 +36,21 @@ ENet::ENet() {
 ENet::~ENet() {
 	if (_initialized) {
 		// Deinitialize the library.
-		debug(1, "ENet: Deinitalizing.");
+		debug(1, "ENet: Deinitializing.");
 		enet_deinitialize();
 	}
 }
 
-bool ENet::initalize() {
+bool ENet::initialize() {
 	if (ENet::_initialized) {
 		return true;
 	}
 
 	if (enet_initialize() != 0) {
-		warning("ENet: ENet library failed to initalize");
+		warning("ENet: ENet library failed to initialize");
 		return false;
 	}
-	debug(1, "ENet: Initalized.");
+	debug(1, "ENet: Initialized.");
 	_initialized = true;
 	return true;
 }
@@ -70,9 +70,14 @@ Host *ENet::createHost(Common::String address, int port, int numClients, int num
 	return new Host(_host);
 }
 
-Host *ENet::connectToHost(Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
+Host *ENet::connectToHost(Common::String hostAddress, int hostPort, Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
+	ENetAddress enetHostAddress;
+	// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
+	enet_address_set_host(&enetHostAddress, hostAddress.c_str());
+	enetHostAddress.port = hostPort;
+
 	// NOTE: Number of channels must match with the server's.
-	ENetHost *enetHost = enet_host_create(nullptr, 1, numChannels, incBand, outBand);
+	ENetHost *enetHost = enet_host_create(&enetHostAddress, 1, numChannels, incBand, outBand);
 	if (enetHost == nullptr) {
 		warning("ENet: An error occured when trying to create client host");
 		return nullptr;
@@ -96,9 +101,15 @@ Host *ENet::connectToHost(Common::String address, int port, int timeout, int num
 		return new Host(enetHost, enetPeer);
 	}
 	warning("ENet: Connection to %s:%d failed", address.c_str(), port);
+	enet_peer_reset(enetPeer);
+	enet_host_destroy(enetHost);
 	return nullptr;
 }
 
+Host *ENet::connectToHost(Common::String address, int port, int timeout, int numChannels, int incBand, int outBand) {
+	return connectToHost("0.0.0.0", 0, address, port, timeout, numChannels, incBand, outBand);
+}
+
 Socket *ENet::createSocket(Common::String address, int port) {
 	ENetAddress enetAddress;
 	if (address == "255.255.255.255") {
diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index a84ee14c555..70c78bd2eaf 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -34,8 +34,9 @@ public:
 	ENet();
 	~ENet();
 
-	bool initalize();
+	bool initialize();
 	Host *createHost(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
+	Host *connectToHost(Common::String hostAddress, int hostPort, Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
 	Host *connectToHost(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
 	Socket *createSocket(Common::String address, int port);
 private:
diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 0a67654b299..2b85b359540 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -67,6 +67,29 @@ uint8 Host::service(int timeout) {
 	return event.type;
 }
 
+bool Host::connectPeer(Common::String address, int port, int timeout, int numChannels) {
+	ENetAddress enetAddress;
+	if (address == "255.255.255.255") {
+		enetAddress.host = ENET_HOST_BROADCAST;
+	} else {
+		// NOTE: 0.0.0.0 returns ENET_HOST_ANY normally.
+		enet_address_set_host(&enetAddress, address.c_str());
+	}
+	enetAddress.port = port;
+
+	// Connect to server address
+	ENetPeer *enetPeer = enet_host_connect(_host, &enetAddress, numChannels, 0);
+
+	ENetEvent event;
+	if (enet_host_service(_host, &event, timeout) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
+		debug(1, "ENet: Connection to %s:%d succeeded.", address.c_str(), port);
+		return true;
+	}
+	warning("ENet: Connection to %s:%d failed", address.c_str(), port);
+	enet_peer_reset(enetPeer);
+	return false;
+}
+
 void Host::disconnectPeer(int peerIndex) {
 	// calling _later will ensure that all the queued packets are sent before disconnecting.
 	enet_peer_disconnect_later(&_host->peers[peerIndex], 0);
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index aa66db8d943..3e1e23f1359 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -49,6 +49,7 @@ public:
 
 	uint8 service(int timeout = 0);
 
+	bool connectPeer(Common::String address, int port, int timeout = 5000, int numChannels = 1);
 	void disconnectPeer(int peerIndex);
 
 	bool send(const char *data, int peerIndex, int channel = 0, bool reliable = true);


Commit: ce45a4f4e60e97dcede07a4a158837b0780c0bc1
    https://github.com/scummvm/scummvm/commit/ce45a4f4e60e97dcede07a4a158837b0780c0bc1
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Use new online networking code.

Changed paths:
  A engines/scumm/he/net/net_defines.h
  A engines/scumm/he/net/net_main.cpp
  A engines/scumm/he/net/net_main.h
  R engines/scumm/he/moonbase/net_defines.h
  R engines/scumm/he/moonbase/net_main.cpp
  R engines/scumm/he/moonbase/net_main.h
    engines/scumm/he/intern_he.h
    engines/scumm/he/logic/moonbase_logic.cpp
    engines/scumm/he/moonbase/moonbase.cpp
    engines/scumm/module.mk
    engines/scumm/scumm.cpp
    engines/scumm/vars.cpp


diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index 0c7a2e27ee8..eab49d3b9b8 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -249,6 +249,10 @@ protected:
 };
 
 #ifdef ENABLE_HE
+
+#ifdef USE_ENET
+class Net;
+#endif
 class Moonbase;
 
 class ScummEngine_v71he : public ScummEngine_v70he {
@@ -536,6 +540,9 @@ protected:
 
 class ScummEngine_v90he : public ScummEngine_v80he {
 	friend class LogicHE;
+#ifdef USE_ENET
+	friend class Net;
+#endif
 	friend class Moonbase;
 	friend class MoviePlayer;
 	friend class Sprite;
@@ -613,6 +620,11 @@ protected:
 	MoviePlayer *_moviePlay;
 	Sprite *_sprite;
 
+#ifdef USE_ENET
+public:
+	Net *_net;
+#endif
+
 public:
 	ScummEngine_v90he(OSystem *syst, const DetectorResult &dr);
 	~ScummEngine_v90he() override;
@@ -711,6 +723,12 @@ protected:
 
 	byte VAR_U32_VERSION;
 	byte VAR_U32_ARRAY_UNK;
+
+#ifdef USE_ENET
+	byte VAR_REMOTE_START_SCRIPT;
+	byte VAR_NETWORK_AVAILABLE;
+	byte VAR_NETWORK_RECEIVE_ARRAY_SCRIPT;
+#endif
 };
 
 class ScummEngine_v99he : public ScummEngine_v90he {
diff --git a/engines/scumm/he/logic/moonbase_logic.cpp b/engines/scumm/he/logic/moonbase_logic.cpp
index 3ce3f306437..ff4caa49109 100644
--- a/engines/scumm/he/logic/moonbase_logic.cpp
+++ b/engines/scumm/he/logic/moonbase_logic.cpp
@@ -23,9 +23,9 @@
 #include "scumm/he/logic_he.h"
 #include "scumm/he/moonbase/moonbase.h"
 #include "scumm/he/moonbase/ai_main.h"
-#ifdef USE_LIBCURL
-#include "scumm/he/moonbase/net_main.h"
-#include "scumm/he/moonbase/net_defines.h"
+#ifdef USE_ENET
+#include "scumm/he/net/net_main.h"
+#include "scumm/he/net/net_defines.h"
 #endif
 
 namespace Scumm {
@@ -59,7 +59,7 @@ private:
 	void op_ai_set_type(int op, int numArgs, int32 *args);
 	void op_ai_clean_up(int op, int numArgs, int32 *args);
 
-#ifdef USE_LIBCURL
+#ifdef USE_ENET
 	void op_net_remote_start_script(int op, int numArgs, int32 *args);
 	void op_net_remote_send_array(int op, int numArgs, int32 *args);
 	int op_net_remote_start_function(int op, int numArgs, int32 *args);
@@ -164,8 +164,8 @@ int LogicHEmoonbase::versionID() {
 #define OP_NET_SET_AI_PLAYER_COUNT			1565
 
 int LogicHEmoonbase::startOfFrame() {
-#ifdef USE_LIBCURL
-	_vm1->_moonbase->_net->doNetworkOnceAFrame(15); // Value should be passed in...
+#ifdef USE_ENET
+	_vm1->_net->doNetworkOnceAFrame(15); // Value should be passed in...
 #endif
 
 	return 0;
@@ -209,7 +209,7 @@ int32 LogicHEmoonbase::dispatch(int op, int numArgs, int32 *args) {
 		op_ai_clean_up(op, numArgs, args);
 		break;
 
-#ifdef USE_LIBCURL
+#ifdef USE_ENET
 	case OP_NET_REMOTE_START_SCRIPT:
 		op_net_remote_start_script(op, numArgs, args);
 		break;
@@ -380,82 +380,82 @@ void LogicHEmoonbase::op_ai_clean_up(int op, int numArgs, int32 *args) {
 	_vm1->_moonbase->_ai->cleanUpAI();
 }
 
-#ifdef USE_LIBCURL
+#ifdef USE_ENET
 void LogicHEmoonbase::op_net_remote_start_script(int op, int numArgs, int32 *args) {
-	_vm1->_moonbase->_net->remoteStartScript(args[0], args[1], args[2], numArgs - 3, &args[3]);
+	_vm1->_net->remoteStartScript(args[0], args[1], args[2], numArgs - 3, &args[3]);
 }
 
 void LogicHEmoonbase::op_net_remote_send_array(int op, int numArgs, int32 *args) {
-	_vm1->_moonbase->_net->remoteSendArray(args[0], args[1], args[2], args[3]);
+	_vm1->_net->remoteSendArray(args[0], args[1], args[2], args[3]);
 }
 
 int LogicHEmoonbase::op_net_remote_start_function(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->remoteStartScriptFunction(args[0], args[1], args[2], args[3], numArgs - 4, &args[4]);
+	return _vm1->_net->remoteStartScriptFunction(args[0], args[1], args[2], args[3], numArgs - 4, &args[4]);
 }
 
 int LogicHEmoonbase::op_net_do_init_all(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->initAll();
+	return _vm1->_net->initAll();
 }
 
 int LogicHEmoonbase::op_net_do_init_provider(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->initProvider();
+	return _vm1->_net->initProvider();
 }
 
 int LogicHEmoonbase::op_net_do_init_session(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->initSession();
+	return _vm1->_net->initSession();
 }
 
 int LogicHEmoonbase::op_net_do_init_user(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->initUser();
+	return _vm1->_net->initUser();
 }
 
 int LogicHEmoonbase::op_net_query_providers(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->queryProviders();
+	return _vm1->_net->queryProviders();
 }
 
 int LogicHEmoonbase::op_net_get_provider_name(int op, int numArgs, int32 *args) {
 	char name[MAX_PROVIDER_NAME];
-	_vm1->_moonbase->_net->getProviderName(args[0] - 1, name, sizeof(name));
+	_vm1->_net->getProviderName(args[0] - 1, name, sizeof(name));
 	return _vm1->setupStringArrayFromString(name);
 }
 
 int LogicHEmoonbase::op_net_set_provider(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->setProvider(args[0] - 1);
+	return _vm1->_net->setProvider(args[0] - 1);
 }
 
 int LogicHEmoonbase::op_net_close_provider(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->closeProvider();
+	return _vm1->_net->closeProvider();
 }
 
 int LogicHEmoonbase::op_net_start_query_sessions(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->startQuerySessions();
+	return _vm1->_net->startQuerySessions();
 }
 
 int LogicHEmoonbase::op_net_update_query_sessions(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->updateQuerySessions();
+	return _vm1->_net->updateQuerySessions();
 }
 
 int LogicHEmoonbase::op_net_stop_query_sessions(int op, int numArgs, int32 *args) {
-	_vm1->_moonbase->_net->stopQuerySessions();
+	_vm1->_net->stopQuerySessions();
 	return 1;
 }
 
 int LogicHEmoonbase::op_net_query_sessions(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->querySessions();
+	return _vm1->_net->querySessions();
 }
 
 int LogicHEmoonbase::op_net_get_session_name(int op, int numArgs, int32 *args) {
 	char name[MAX_PROVIDER_NAME];
-	_vm1->_moonbase->_net->getSessionName(args[0] - 1, name, sizeof(name));
+	_vm1->_net->getSessionName(args[0] - 1, name, sizeof(name));
 	return _vm1->setupStringArrayFromString(name);
 }
 
 int LogicHEmoonbase::op_net_get_session_player_count(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->getSessionPlayerCount(args[0] - 1);
+	return _vm1->_net->getSessionPlayerCount(args[0] - 1);
 }
 
 int LogicHEmoonbase::op_net_destroy_player(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->destroyPlayer(args[0]);
+	return _vm1->_net->destroyPlayer(args[0]);
 }
 
 int LogicHEmoonbase::op_net_get_player_long_name(int op, int numArgs, int32 *args) {
@@ -469,64 +469,64 @@ int LogicHEmoonbase::op_net_get_player_short_name(int op, int numArgs, int32 *ar
 int LogicHEmoonbase::op_net_create_session(int op, int numArgs, int32 *args) {
 	char name[MAX_SESSION_NAME];
 	_vm1->getStringFromArray(args[0], name, sizeof(name));
-	return _vm1->_moonbase->_net->createSession(name);
+	return _vm1->_net->createSession(name);
 }
 
 int LogicHEmoonbase::op_net_join_session(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->joinSession(args[0] - 1);
+	return _vm1->_net->joinSession(args[0] - 1);
 }
 
 int LogicHEmoonbase::op_net_end_session(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->endSession();
+	return _vm1->_net->endSession();
 }
 
 int LogicHEmoonbase::op_net_disable_session_player_join(int op, int numArgs, int32 *args) {
-	_vm1->_moonbase->_net->disableSessionJoining();
+	_vm1->_net->disableSessionJoining();
 	return 1;
 }
 
 int LogicHEmoonbase::op_net_enable_session_player_join(int op, int numArgs, int32 *args) {
-	_vm1->_moonbase->_net->enableSessionJoining();
+	_vm1->_net->enableSessionJoining();
 	return 1;
 }
 
 int LogicHEmoonbase::op_net_set_ai_player_count(int op, int numArgs, int32 *args) {
-	_vm1->_moonbase->_net->setBotsCount(args[0]);
+	_vm1->_net->setBotsCount(args[0]);
 	return 1;
 }
 
 int LogicHEmoonbase::op_net_add_user(int op, int numArgs, int32 *args) {
 	char userName[MAX_PLAYER_NAME];
 	_vm1->getStringFromArray(args[0], userName, sizeof(userName));
-	return _vm1->_moonbase->_net->addUser(userName, userName);
+	return _vm1->_net->addUser(userName, userName);
 }
 
 int LogicHEmoonbase::op_net_remove_user(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->removeUser();
+	return _vm1->_net->removeUser();
 }
 
 int LogicHEmoonbase::op_net_who_sent_this(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->whoSentThis();
+	return _vm1->_net->whoSentThis();
 }
 
 int LogicHEmoonbase::op_net_who_am_i(int op, int numArgs, int32 *args) {
-	return _vm1->_moonbase->_net->whoAmI();
+	return _vm1->_net->whoAmI();
 }
 
 int LogicHEmoonbase::op_net_set_provider_by_name(int op, int numArgs, int32 *args) {
 	// Parameter 1 is the provider name and
 	// Parameter 2 is the (optional) tcp/ip address
-	return _vm1->_moonbase->_net->setProviderByName(args[0], args[1]);
+	return _vm1->_net->setProviderByName(args[0], args[1]);
 }
 
 void LogicHEmoonbase::op_net_set_fake_latency(int op, int numArgs, int32 *args) {
-	_vm1->_moonbase->_net->setFakeLatency(args[0]);
+	_vm1->_net->setFakeLatency(args[0]);
 }
 
 int LogicHEmoonbase::op_net_get_host_name(int op, int numArgs, int32 *args) {
 	char name[MAX_HOSTNAME_SIZE];
 
-	if (_vm1->_moonbase->_net->getHostName(name, MAX_HOSTNAME_SIZE)) {
+	if (_vm1->_net->getHostName(name, MAX_HOSTNAME_SIZE)) {
 		return _vm1->setupStringArrayFromString(name);
 	}
 
@@ -539,7 +539,7 @@ int LogicHEmoonbase::op_net_get_ip_from_name(int op, int numArgs, int32 *args) {
 
 	char ip[MAX_IP_SIZE];
 
-	if (_vm1->_moonbase->_net->getIPfromName(ip, MAX_IP_SIZE, name)) {
+	if (_vm1->_net->getIPfromName(ip, MAX_IP_SIZE, name)) {
 		return _vm1->setupStringArrayFromString(ip);
 	}
 
@@ -553,7 +553,7 @@ int LogicHEmoonbase::op_net_host_tcpip_game(int op, int numArgs, int32 *args) {
 	_vm1->getStringFromArray(args[0], sessionName, sizeof(sessionName));
 	_vm1->getStringFromArray(args[1], userName, sizeof(userName));
 
-	return _vm1->_moonbase->_net->hostGame(sessionName, userName);
+	return _vm1->_net->hostGame(sessionName, userName);
 }
 
 int LogicHEmoonbase::op_net_join_tcpip_game(int op, int numArgs, int32 *args) {
@@ -563,7 +563,7 @@ int LogicHEmoonbase::op_net_join_tcpip_game(int op, int numArgs, int32 *args) {
 	_vm1->getStringFromArray(args[0], ip, sizeof(ip));
 	_vm1->getStringFromArray(args[1], userName, sizeof(userName));
 
-	return _vm1->_moonbase->_net->joinGame(ip, userName);
+	return _vm1->_net->joinGame(ip, userName);
 }
 #endif
 
diff --git a/engines/scumm/he/moonbase/moonbase.cpp b/engines/scumm/he/moonbase/moonbase.cpp
index 16f44298ab7..a512f9cdf06 100644
--- a/engines/scumm/he/moonbase/moonbase.cpp
+++ b/engines/scumm/he/moonbase/moonbase.cpp
@@ -24,9 +24,9 @@
 #include "scumm/he/intern_he.h"
 #include "scumm/he/moonbase/moonbase.h"
 #include "scumm/he/moonbase/ai_main.h"
-#ifdef USE_LIBCURL
-#include "scumm/he/moonbase/net_main.h"
-#endif
+// #ifdef USE_LIBCURL
+// #include "scumm/he/moonbase/net_main.h"
+// #endif
 
 namespace Scumm {
 
@@ -36,17 +36,11 @@ Moonbase::Moonbase(ScummEngine_v100he *vm) : _vm(vm) {
 	initFOW();
 
 	_ai = new AI(_vm);
-#ifdef USE_LIBCURL
-	_net = new Net(_vm);
-#endif
 }
 
 Moonbase::~Moonbase() {
 	delete _exe;
 	delete _ai;
-#ifdef USE_LIBCURL
-	delete _net;
-#endif
 }
 
 int Moonbase::readFromArray(int array, int y, int x) {
diff --git a/engines/scumm/he/moonbase/net_main.cpp b/engines/scumm/he/moonbase/net_main.cpp
deleted file mode 100644
index 47716c141cd..00000000000
--- a/engines/scumm/he/moonbase/net_main.cpp
+++ /dev/null
@@ -1,741 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "backends/networking/curl/connectionmanager.h"
-
-#include "scumm/he/intern_he.h"
-#include "scumm/he/moonbase/moonbase.h"
-#include "scumm/he/moonbase/net_main.h"
-#include "scumm/he/moonbase/net_defines.h"
-
-namespace Scumm {
-
-Net::Net(ScummEngine_v100he *vm) : _latencyTime(1), _fakeLatency(false), _vm(vm) {
-	//some defaults for fields
-
-	_packbuffer = (byte *)malloc(MAX_PACKET_SIZE + DATA_HEADER_SIZE);
-	_tmpbuffer = (byte *)malloc(MAX_PACKET_SIZE);
-
-	_myUserId = -1;
-	_myPlayerKey = -1;
-	_lastResult = 0;
-
-	_sessionsBeingQueried = false;
-
-	_sessionid = -1;
-	_sessions = nullptr;
-	_packetdata = nullptr;
-
-	_serverprefix = "http://localhost/moonbase";
-}
-
-Net::~Net() {
-	free(_tmpbuffer);
-	free(_packbuffer);
-
-	delete _sessions;
-	delete _packetdata;
-}
-
-int Net::hostGame(char *sessionName, char *userName) {
-	if (createSession(sessionName)) {
-		if (addUser(userName, userName)) {
-			return 1;
-		} else {
-			_vm->displayMessage(0, "Error Adding User \"%s\" to Session \"%s\"", userName, sessionName);
-			endSession();
-			closeProvider();
-		}
-	} else {
-		_vm->displayMessage(0, "Error creating session \"%s\"", userName );
-
-		closeProvider();
-	}
-
-	return 0;
-}
-
-int Net::joinGame(char *IP, char *userName) {
-	warning("STUB: Net::joinGame(\"%s\", \"%s\")", IP, userName); // PN_JoinTCPIPGame
-	return 0;
-}
-
-int Net::addUser(char *shortName, char *longName) {
-	debug(1, "Net::addUser(\"%s\", \"%s\")", shortName, longName); // PN_AddUser
-
-	Networking::PostRequest rq(_serverprefix + "/adduser",
-		new Common::Callback<Net, Common::JSONValue *>(this, &Net::addUserCallback),
-		new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::addUserErrorCallback));
-
-	char *buf = new char[MAX_PACKET_SIZE];
-	snprintf(buf, MAX_PACKET_SIZE, "{\"shortname\":\"%s\",\"longname\":\"%s\",\"sessionid\":%d}", shortName, longName, _sessionid);
-	rq.setPostData((byte *)buf, strlen(buf));
-	rq.setContentType("application/json");
-
-	rq.start();
-
-	_myUserId = -1;
-
-	while(rq.state() == Networking::PROCESSING) {
-		g_system->delayMillis(5);
-	}
-
-	if (_myUserId == -1)
-		return 0;
-
-	return 1;
-}
-
-void Net::addUserCallback(Common::JSONValue *response) {
-	Common::JSONObject info = response->asObject();
-
-	if (info.contains("userid")) {
-		_myUserId = info["userid"]->asIntegerNumber();
-		_myPlayerKey = info["playerkey"]->asIntegerNumber();
-	}
-	debug(1, "addUserCallback: got: '%s' as %d", response->stringify().c_str(), _myUserId);
-}
-
-void Net::addUserErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in addUser(): %ld %s", error.httpResponseCode, error.response.c_str());
-}
-
-int Net::removeUser() {
-	debug(1, "Net::removeUser()"); // PN_RemoveUser
-
-	if (_myUserId != -1)
-		destroyPlayer(_myUserId);
-
-	return 1;
-}
-
-int Net::whoSentThis() {
-	debug(1, "Net::whoSentThis()"); // PN_WhoSentThis
-	return _packetdata->child("from")->asIntegerNumber();
-}
-
-int Net::whoAmI() {
-	debug(1, "Net::whoAmI()"); // PN_WhoAmI
-
-	return _myUserId;
-}
-
-int Net::createSession(char *name) {
-	debug(1, "Net::createSession(\"%s\")", name); // PN_CreateSession
-
-	Networking::PostRequest rq(_serverprefix + "/createsession",
-		new Common::Callback<Net, Common::JSONValue *>(this, &Net::createSessionCallback),
-		new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::createSessionErrorCallback));
-
-	char *buf = new char[MAX_PACKET_SIZE];
-	snprintf(buf, MAX_PACKET_SIZE, "{\"name\":\"%s\"}", name);
-	rq.setPostData((byte *)buf, strlen(buf));
-	rq.setContentType("application/json");
-
-	rq.start();
-
-	_sessionid = -1;
-
-	while(rq.state() == Networking::PROCESSING) {
-		g_system->delayMillis(5);
-	}
-
-	if (_sessionid == -1)
-		return 0;
-
-	return 1;
-}
-
-void Net::createSessionCallback(Common::JSONValue *response) {
-	Common::JSONObject info = response->asObject();
-
-	if (info.contains("sessionid")) {
-		_sessionid = info["sessionid"]->asIntegerNumber();
-	}
-	debug(1, "createSessionCallback: got: '%s' as %d", response->stringify().c_str(), _sessionid);
-}
-
-void Net::createSessionErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in createSession(): %ld %s", error.httpResponseCode, error.response.c_str());
-}
-
-int Net::joinSession(int sessionIndex) {
-	debug(1, "Net::joinSession(%d)", sessionIndex); // PN_JoinSession
-
-	if (!_sessions) {
-		warning("Net::joinSession(): no sessions");
-		return 0;
-	}
-
-	if (sessionIndex >= (int)_sessions->countChildren()) {
-		warning("Net::joinSession(): session number too big: %d >= %d", sessionIndex, (int)_sessions->countChildren());
-		return 0;
-	}
-
-	if (!_sessions->child(sessionIndex)->hasChild("sessionid")) {
-		warning("Net::joinSession(): no sessionid in session");
-		return 0;
-	}
-
-	_sessionid = _sessions->child(sessionIndex)->child("sessionid")->asIntegerNumber();
-
-	return 1;
-}
-
-int Net::endSession() {
-	debug(1, "Net::endSession()"); // PN_EndSession
-
-	Networking::PostRequest rq(_serverprefix + "/endsession",
-		new Common::Callback<Net, Common::JSONValue *>(this, &Net::endSessionCallback),
-		new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::endSessionErrorCallback));
-
-	char *buf = new char[MAX_PACKET_SIZE];
-	snprintf(buf, MAX_PACKET_SIZE, "{\"sessionid\":%d, \"userid\":%d}", _sessionid, _myUserId);
-	rq.setPostData((byte *)buf, strlen(buf));
-	rq.setContentType("application/json");
-
-	rq.start();
-
-	while(rq.state() == Networking::PROCESSING) {
-		g_system->delayMillis(5);
-	}
-
-	return _lastResult;
-}
-
-void Net::endSessionCallback(Common::JSONValue *response) {
-	_lastResult = 1;
-}
-
-void Net::endSessionErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in endSession(): %ld %s", error.httpResponseCode, error.response.c_str());
-
-	_lastResult = 0;
-}
-
-
-void Net::disableSessionJoining() {
-	debug(1, "Net::disableSessionJoining()"); // PN_DisableSessionPlayerJoin
-
-	Networking::PostRequest *rq = new Networking::PostRequest(_serverprefix + "/disablesession",
-		nullptr,
-		new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::disableSessionJoiningErrorCallback));
-
-	char *buf = new char[MAX_PACKET_SIZE];
-	snprintf(buf, MAX_PACKET_SIZE, "{\"sessionid\":%d}", _sessionid);
-	rq->setPostData((byte *)buf, strlen(buf));
-	rq->setContentType("application/json");
-
-	rq->start();
-
-	ConnMan.addRequest(rq);
-}
-
-void Net::disableSessionJoiningErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in disableSessionJoining(): %ld %s", error.httpResponseCode, error.response.c_str());
-}
-
-void Net::enableSessionJoining() {
-	warning("STUB: Net::enableSessionJoining()"); // PN_EnableSessionPlayerJoin
-}
-
-void Net::setBotsCount(int botsCount) {
-	warning("STUB: Net::setBotsCount(%d)", botsCount); // PN_SetAIPlayerCountKludge
-}
-
-int32 Net::setProviderByName(int32 parameter1, int32 parameter2) {
-	char name[MAX_PROVIDER_NAME];
-	char ipaddress[MAX_IP_SIZE];
-
-	ipaddress[0] = '\0';
-
-	_vm->getStringFromArray(parameter1, name, sizeof(name));
-	if (parameter2)
-		_vm->getStringFromArray(parameter2, ipaddress, sizeof(ipaddress));
-
-	debug(1, "Net::setProviderByName(\"%s\", \"%s\")", name, ipaddress); // PN_SetProviderByName
-
-	// Emulate that we found a TCP/IP provider
-
-	return 1;
-}
-
-void Net::setFakeLatency(int time) {
-	_latencyTime = time;
-	debug("NETWORK: Setting Fake Latency to %d ms", _latencyTime);
-	_fakeLatency = true;
-}
-
-bool Net::destroyPlayer(int32 playerDPID) {
-	// bool PNETWIN_destroyplayer(DPID idPlayer)
-	debug(1, "Net::destroyPlayer(%d)", playerDPID);
-
-	Networking::PostRequest *rq = new Networking::PostRequest(_serverprefix + "/removeuser",
-		nullptr,
-		new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::destroyPlayerErrorCallback));
-
-	char *buf = new char[MAX_PACKET_SIZE];
-	snprintf(buf, MAX_PACKET_SIZE, "{\"sessionid\":%d, \"userid\":%d}", _sessionid, playerDPID);
-	rq->setPostData((byte *)buf, strlen(buf));
-	rq->setContentType("application/json");
-
-	rq->start();
-
-	ConnMan.addRequest(rq);
-
-	return true;
-}
-
-void Net::destroyPlayerErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in destroyPlayer(): %ld %s", error.httpResponseCode, error.response.c_str());
-}
-
-int32 Net::startQuerySessions() {
-	if (!_sessionsBeingQueried) { // Do not run parallel queries
-		debug(1, "Net::startQuerySessions()"); // StartQuerySessions
-
-		Networking::PostRequest *rq = new Networking::PostRequest(_serverprefix + "/lobbies",
-			new Common::Callback<Net, Common::JSONValue *>(this, &Net::startQuerySessionsCallback),
-			new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::startQuerySessionsErrorCallback));
-
-		_sessionsBeingQueried = true;
-
-		rq->start();
-
-		ConnMan.addRequest(rq);
-	}
-
-	if (!_sessions)
-		return 0;
-
-	debug(1, "Net::startQuerySessions(): got %d", (int)_sessions->countChildren());
-
-	return _sessions->countChildren();
-}
-
-void Net::startQuerySessionsCallback(Common::JSONValue *response) {
-	debug(1, "startQuerySessions: Got: '%s' which is %d", response->stringify().c_str(), (int)response->countChildren());
-
-	_sessionsBeingQueried = false;
-
-	delete _sessions;
-
-	_sessions = new Common::JSONValue(*response);
-}
-
-void Net::startQuerySessionsErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in startQuerySessions(): %ld %s", error.httpResponseCode, error.response.c_str());
-
-	_sessionsBeingQueried = false;
-}
-
-int32 Net::updateQuerySessions() {
-	debug(1, "Net::updateQuerySessions()"); // UpdateQuerySessions
-	return startQuerySessions();
-}
-
-void Net::stopQuerySessions() {
-	debug(1, "Net::stopQuerySessions()"); // StopQuerySessions
-
-	_sessionsBeingQueried = false;
-	// No op
-}
-
-int Net::querySessions() {
-	warning("STUB: Net::querySessions()"); // PN_QuerySessions
-	return 0;
-}
-
-int Net::queryProviders() {
-	debug(1, "Net::queryProviders()"); // PN_QueryProviders
-
-	// Emulate that we have 1 provider, TCP/IP
-	return 1;
-}
-
-int Net::setProvider(int providerIndex) {
-	warning("STUB: Net::setProvider(%d)", providerIndex); // PN_SetProvider
-	return 0;
-}
-
-int Net::closeProvider() {
-	debug(1, "Net::closeProvider()"); // PN_CloseProvider
-
-	return 1;
-}
-
-bool Net::initAll() {
-	warning("STUB: Net::initAll()"); // PN_DoInitAll
-	return false;
-}
-
-bool Net::initProvider() {
-	warning("STUB: Net::initProvider()"); // PN_DoInitProvider
-	return false;
-}
-
-bool Net::initSession() {
-	warning("STUB: Net::initSession()"); // PN_DoInitSession
-	return false;
-}
-
-bool Net::initUser() {
-	warning("STUB: Net::initUser()"); // PN_DoInitUser
-	return false;
-}
-
-void Net::remoteStartScript(int typeOfSend, int sendTypeParam, int priority, int argsCount, int32 *args) {
-	Common::String res = "\"params\": [";
-
-	if (argsCount > 2)
-		for (int i = 0; i < argsCount - 1; i++)
-			res += Common::String::format("%d, ", args[i]);
-
-	if (argsCount > 1)
-		res += Common::String::format("%d]", args[argsCount - 1]);
-	else
-		res += "]";
-
-	debug(1, "Net::remoteStartScript(%d, %d, %d, %d, ...)", typeOfSend, sendTypeParam, priority, argsCount); // PN_RemoteStartScriptCommand
-
-	remoteSendData(typeOfSend, sendTypeParam, PACKETTYPE_REMOTESTARTSCRIPT, res);
-}
-
-int Net::remoteSendData(int typeOfSend, int sendTypeParam, int type, Common::String data, int defaultRes, bool wait, int callid) {
-	// Since I am lazy, instead of constructing the JSON object manually
-	// I'd rather parse it
-	Common::String res = Common::String::format(
-		"{\"sessionid\":%d, \"from\":%d, \"to\":%d, \"toparam\": %d, "
-		"\"type\":%d, \"timestamp\": %d, \"size\": 1, \"data\": { %s } }", _sessionid, _myUserId,
-		typeOfSend, sendTypeParam, type, g_system->getMillis(), data.c_str());
-
-	byte *buf = (byte *)malloc(res.size() + 1);
-	strncpy((char *)buf, res.c_str(), res.size());
-
-	debug(2, "Package to send: %s", res.c_str());
-
-	Networking::PostRequest *rq = new Networking::PostRequest(_serverprefix + "/packet",
-		nullptr,
-		new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::remoteSendDataErrorCallback));
-
-	rq->setPostData(buf, res.size());
-	rq->setContentType("application/json");
-
-	rq->start();
-
-	ConnMan.addRequest(rq);
-
-	if (!wait)
-		return 0;
-
-	uint32 timeout = g_system->getMillis() + 1000;
-
-	while (g_system->getMillis() < timeout) {
-		if (remoteReceiveData()) {
-			if (_packetdata->child("data")->hasChild("callid")) {
-				if (_packetdata->child("data")->child("callid")->asIntegerNumber() == callid) {
-					return _packetdata->child("data")->child("result")->asIntegerNumber();
-				}
-			}
-
-			warning("Net::remoteSendData(): Received wrong package: %s", _packetdata->stringify().c_str());
-		}
-
-		_vm->parseEvents();
-	}
-
-	if (!_sessions)
-		return 0;
-
-	return defaultRes;
-}
-
-void Net::remoteSendDataErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in remoteSendData(): %ld %s", error.httpResponseCode, error.response.c_str());
-}
-
-void Net::remoteSendArray(int typeOfSend, int sendTypeParam, int priority, int arrayIndex) {
-	debug(1, "Net::remoteSendArray(%d, %d, %d, %d)", typeOfSend, sendTypeParam, priority, arrayIndex & ~0x33539000); // PN_RemoteSendArrayCommand
-
-	ScummEngine_v100he::ArrayHeader *ah = (ScummEngine_v100he::ArrayHeader *)_vm->getResourceAddress(rtString, arrayIndex & ~0x33539000);
-
-	Common::String jsonData = Common::String::format(
-		"\"type\":%d, \"dim1start\":%d, \"dim1end\":%d, \"dim2start\":%d, \"dim2end\":%d, \"data\": [",
-		ah->type, ah->dim1start, ah->dim1end, ah->dim2start, ah->dim2end);
-
-	int32 size = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
-		(FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
-
-	for (int i = 0; i < size; i++) {
-		int32 data;
-
-		switch (FROM_LE_32(ah->type)) {
-		case ScummEngine_v100he::kByteArray:
-		case ScummEngine_v100he::kStringArray:
-			data = ah->data[i];
-			break;
-
-		case ScummEngine_v100he::kIntArray:
-			data = (int16)READ_LE_UINT16(ah->data + i * 2);
-			break;
-
-		case ScummEngine_v100he::kDwordArray:
-			data = (int32)READ_LE_UINT32(ah->data + i * 4);
-			break;
-
-		default:
-			error("Net::remoteSendArray(): Unknown array type %d for array %d", FROM_LE_32(ah->type), arrayIndex);
-		}
-
-		jsonData += Common::String::format("%d", data);
-
-		if (i < size - 1)
-			jsonData += ", ";
-		else
-			jsonData += "]";
-	}
-
-	remoteSendData(typeOfSend, sendTypeParam, PACKETTYPE_REMOTESENDSCUMMARRAY, jsonData);
-}
-
-int Net::remoteStartScriptFunction(int typeOfSend, int sendTypeParam, int priority, int defaultReturnValue, int argsCount, int32 *args) {
-	int callid = _vm->_rnd.getRandomNumber(1000000);
-
-	Common::String res = Common::String::format("\"callid\":%d, \"params\": [", callid);
-
-	if (argsCount > 2)
-		for (int i = 0; i < argsCount - 1; i++)
-			res += Common::String::format("%d, ", args[i]);
-
-	if (argsCount > 1)
-		res += Common::String::format("%d]", args[argsCount - 1]);
-	else
-		res += "]";
-
-	debug(1, "Net::remoteStartScriptFunction(%d, %d, %d, %d, %d, ...)", typeOfSend, sendTypeParam, priority, defaultReturnValue, argsCount); // PN_RemoteStartScriptFunction
-
-	return remoteSendData(typeOfSend, sendTypeParam, PACKETTYPE_REMOTESTARTSCRIPTRETURN, res, defaultReturnValue, true, callid);
-}
-
-bool Net::getHostName(char *hostname, int length) {
-	warning("STUB: Net::getHostName(\"%s\", %d)", hostname, length); // PN_GetHostName
-	return false;
-}
-
-bool Net::getIPfromName(char *ip, int ipLength, char *nameBuffer) {
-	warning("STUB: Net::getIPfromName(\"%s\", %d, \"%s\")", ip, ipLength, nameBuffer); // PN_GetIPfromName
-	return false;
-}
-
-void Net::getSessionName(int sessionNumber, char *buffer, int length) {
-	debug(1, "Net::getSessionName(%d, ..., %d)", sessionNumber, length); // PN_GetSessionName
-
-	if (!_sessions) {
-		*buffer = '\0';
-		warning("Net::getSessionName(): no sessions");
-		return;
-	}
-
-	if (sessionNumber >= (int)_sessions->countChildren()) {
-		*buffer = '\0';
-		warning("Net::getSessionName(): session number too big: %d >= %d", sessionNumber, (int)_sessions->countChildren());
-		return;
-	}
-
-	Common::strlcpy(buffer, _sessions->child(sessionNumber)->child("name")->asString().c_str(), length);
-}
-
-int Net::getSessionPlayerCount(int sessionNumber) {
-	debug(1, "Net::getSessionPlayerCount(%d)", sessionNumber); // case GET_SESSION_PLAYER_COUNT_KLUDGE:
-
-	if (!_sessions) {
-		warning("Net::getSessionPlayerCount(): no sessions");
-		return 0;
-	}
-
-	if (sessionNumber >= (int)_sessions->countChildren()) {
-		warning("Net::getSessionPlayerCount(): session number too big: %d >= %d", sessionNumber, (int)_sessions->countChildren());
-		return 0;
-	}
-
-	if (!_sessions->child(sessionNumber)->hasChild("players")) {
-		warning("Net::getSessionPlayerCount(): no players in session");
-		return 0;
-	}
-
-	return _sessions->child(sessionNumber)->child("players")->countChildren();
-}
-
-void Net::getProviderName(int providerIndex, char *buffer, int length) {
-	warning("STUB: Net::getProviderName(%d, \"%s\", %d)", providerIndex, buffer, length); // PN_GetProviderName
-}
-
-bool Net::remoteReceiveData() {
-	Networking::PostRequest rq(_serverprefix + "/getpacket",
-		new Common::Callback<Net, Common::JSONValue *>(this, &Net::remoteReceiveDataCallback),
-		new Common::Callback<Net, Networking::ErrorResponse>(this, &Net::remoteReceiveDataErrorCallback));
-
-	char *buf = new char[MAX_PACKET_SIZE];
-	snprintf(buf, MAX_PACKET_SIZE, "{\"sessionid\":%d, \"playerid\":%d}", _sessionid, _myUserId);
-	rq.setPostData((byte *)buf, strlen(buf));
-	rq.setContentType("application/json");
-
-	delete _packetdata;
-	_packetdata = nullptr;
-
-	rq.start();
-
-	while(rq.state() == Networking::PROCESSING) {
-		g_system->delayMillis(5);
-	}
-
-	if (!_packetdata || _packetdata->child("size")->asIntegerNumber() == 0)
-		return false;
-
-	uint from = _packetdata->child("from")->asIntegerNumber();
-	uint type = _packetdata->child("type")->asIntegerNumber();
-
-	uint32 *params;
-
-	switch (type) {
-	case PACKETTYPE_REMOTESTARTSCRIPT:
-		{
-			int datalen = _packetdata->child("data")->child("params")->asArray().size();
-			params = (uint32 *)_tmpbuffer;
-
-			for (int i = 0; i < datalen; i++) {
-				*params = _packetdata->child("data")->child("params")->asArray()[i]->asIntegerNumber();
-				params++;
-			}
-
-			_vm->runScript(_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT), 1, 0, (int *)_tmpbuffer);
-		}
-		break;
-
-	case PACKETTYPE_REMOTESTARTSCRIPTRETURN:
-		{
-			int datalen = _packetdata->child("data")->child("params")->asArray().size();
-			params = (uint32 *)_tmpbuffer;
-
-			for (int i = 0; i < datalen; i++) {
-				*params = _packetdata->child("data")->child("params")->asArray()[i]->asIntegerNumber();
-				params++;
-			}
-
-			_vm->runScript(_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT), 1, 0, (int *)_tmpbuffer);
-			int result = _vm->pop();
-
-			Common::String res = Common::String::format("\"result\": %d, \"callid\": %d", result,
-					(int)_packetdata->child("data")->child("callid")->asIntegerNumber());
-
-			remoteSendData(PN_SENDTYPE_INDIVIDUAL, from, PACKETTYPE_REMOTESTARTSCRIPTRESULT, res);
-		}
-		break;
-
-	case PACKETTYPE_REMOTESTARTSCRIPTRESULT:
-		//
-		// Ignore it.
-		//
-
-		break;
-
-	case PACKETTYPE_REMOTESENDSCUMMARRAY:
-		{
-			int newArray = 0;
-
-			// Assume that the packet data contains a "SCUMM PACKAGE"
-			// and unpack it into an scumm array :-)
-
-			int dim1start = _packetdata->child("data")->child("dim1start")->asIntegerNumber();
-			int dim1end   = _packetdata->child("data")->child("dim1end")->asIntegerNumber();
-			int dim2start = _packetdata->child("data")->child("dim2start")->asIntegerNumber();
-			int dim2end   = _packetdata->child("data")->child("dim2end")->asIntegerNumber();
-			int atype     = _packetdata->child("data")->child("type")->asIntegerNumber();
-
-			byte *data = _vm->defineArray(0, atype, dim2start, dim2end, dim1start, dim1end, true, &newArray);
-
-			int32 size = (dim1end - dim1start + 1) * (dim2end - dim2start + 1);
-
-			int32 value;
-
-			for (int i = 0; i < size; i++) {
-				value = _packetdata->child("data")->child("data")->asArray()[i]->asIntegerNumber();
-
-				switch (atype) {
-				case ScummEngine_v100he::kByteArray:
-				case ScummEngine_v100he::kStringArray:
-					data[i] = value;
-					break;
-
-				case ScummEngine_v100he::kIntArray:
-					WRITE_LE_UINT16(data + i * 2, value);
-					break;
-
-				case ScummEngine_v100he::kDwordArray:
-					WRITE_LE_UINT32(data + i * 4, value);
-					break;
-
-				default:
-					error("Net::remoteReceiveData(): Unknown array type %d", atype);
-				}
-			}
-
-			memset(_tmpbuffer, 0, 25 * 4);
-			WRITE_UINT32(_tmpbuffer, newArray);
-
-			// Quick start the script (1st param is the new array)
-			_vm->runScript(_vm->VAR(_vm->VAR_NETWORK_RECEIVE_ARRAY_SCRIPT), 1, 0, (int *)_tmpbuffer);
-		}
-		break;
-
-	default:
-		warning("Moonbase: Received unknown network command %d", type);
-	}
-
-	return true;
-}
-
-void Net::remoteReceiveDataCallback(Common::JSONValue *response) {
-	_packetdata = new Common::JSONValue(*response);
-
-	if (_packetdata->child("size")->asIntegerNumber() != 0)
-		debug(1, "remoteReceiveData: Got: '%s'", response->stringify().c_str());
-}
-
-void Net::remoteReceiveDataErrorCallback(Networking::ErrorResponse error) {
-	warning("Error in remoteReceiveData(): %ld %s", error.httpResponseCode, error.response.c_str());
-}
-
-
-void Net::doNetworkOnceAFrame(int msecs) {
-	if (_sessionid == -1 || _myUserId == -1)
-		return;
-
-	uint32 tickCount = g_system->getMillis() + msecs;
-
-	while (remoteReceiveData()) {
-		if (tickCount >= g_system->getMillis()) {
-			break;
-		}
-	}
-}
-
-} // End of namespace Scumm
diff --git a/engines/scumm/he/moonbase/net_defines.h b/engines/scumm/he/net/net_defines.h
similarity index 80%
rename from engines/scumm/he/moonbase/net_defines.h
rename to engines/scumm/he/net/net_defines.h
index d1802262319..d91a8fa9fae 100644
--- a/engines/scumm/he/moonbase/net_defines.h
+++ b/engines/scumm/he/net/net_defines.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef SCUMM_HE_MOONBASE_NET_DEFINES_H
-#define SCUMM_HE_MOONBASE_NET_DEFINES_H
+#ifndef SCUMM_HE_NET_DEFINES_H
+#define SCUMM_HE_NET_DEFINES_H
 
 namespace Scumm {
 
@@ -46,22 +46,10 @@ namespace Scumm {
 #define PACKETTYPE_REMOTESTARTSCRIPTRESULT			3
 #define PACKETTYPE_REMOTESENDSCUMMARRAY				4
 
-const int SESSION_ERROR = 0;
-const int USER_CREATED_SESSION = 1;
-const int USER_JOINED_SESSION = 2;
-
-const int TCPIP_PROVIDER = -1;
-const int NO_PROVIDER = -2;
-
 const int MAX_PACKET_SIZE = 4096;	// bytes
 const int MAX_HOSTNAME_SIZE = 256;
 const int MAX_IP_SIZE = 32;
-const char LOCAL_HOST[] = "127.0.0.1";	//localhost
-
-const int DATA_HEADER_SIZE = 28;
-
-#define NULL_IP "";						//no IP address (causes enumsessions to search local subnet)
 
 } // End of namespace Scumm
 
-#endif
+#endif
\ No newline at end of file
diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
new file mode 100644
index 00000000000..b4c834423bd
--- /dev/null
+++ b/engines/scumm/he/net/net_main.cpp
@@ -0,0 +1,1359 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/net/net_main.h"
+#include "scumm/he/net/net_defines.h"
+
+namespace Scumm {
+
+Net::Net(ScummEngine_v90he *vm) : _latencyTime(1), _fakeLatency(false), _vm(vm) {
+	//some defaults for fields
+
+	_gameName = _vm->_game.gameid;
+	_gameVersion = "";
+	if (_vm->_game.variant)
+		_gameVersion = _vm->_game.variant; // 1.0/1.1/Demo
+
+	_tmpbuffer = (byte *)malloc(MAX_PACKET_SIZE);
+
+	_enet = nullptr;
+
+	_sessionHost = nullptr;
+	_broadcastSocket = nullptr;
+
+	_sessionServerPeer = -1;
+	_sessionServerHost = nullptr;
+	_isRelayingGame = false;
+
+	_numUsers = 0;
+	_numBots = 0;
+
+	_maxPlayers = 2;
+	if (_gameName == "moonbase")
+		_maxPlayers = 4;
+
+	_userIdCounter = 0;
+
+	_myUserId = -1;
+	_fromUserId = -1;
+
+	_sessionId = -1;
+	_isHost = false;
+	_isShuttingDown = false;
+	_sessionName = Common::String();
+	_sessions = Common::Array<Session>();
+
+	_hostPort = 0;
+
+	_hostDataQueue = Common::Queue<Common::JSONValue *>();
+	_peerIndexQueue = Common::Queue<int>();
+}
+
+Net::~Net() {
+	free(_tmpbuffer);
+	closeProvider();
+}
+
+Net::Address Net::getAddressFromString(Common::String addressStr) {
+	Address address;
+	int portPos = addressStr.findFirstOf(":");
+	if (portPos > -1) {
+		address.port = atoi(addressStr.substr(portPos + 1).c_str());
+		address.host = addressStr.substr(0, portPos);
+	} else {
+		// Assume that the string has no port defined.
+		address.host = addressStr;
+		address.port = 0;
+	}
+	return address;
+}
+
+Common::String Net::getStringFromAddress(Address address) {
+	return Common::String::format("%s:%d", address.host.c_str(), address.port);
+}
+
+int Net::hostGame(char *sessionName, char *userName) {
+	if (createSession(sessionName)) {
+		if (addUser(userName, userName)) {
+			_myUserId = _userIdCounter;
+			_userIdToPeerIndex[_myUserId] = -1;
+			return 1;
+		} else {
+			_vm->displayMessage(0, "Error Adding User \"%s\" to Session \"%s\"", userName, sessionName);
+			endSession();
+			closeProvider();
+		}
+	} else {
+		_vm->displayMessage(0, "Error creating session \"%s\"", userName );
+
+		closeProvider();
+	}
+
+	return 0;
+}
+
+int Net::joinGame(Common::String IP, char *userName) {
+	// This gets called when attempting to join with the --join-game command line param.
+	debug(1, "Net::joinGame(\"%s\", \"%s\")", IP.c_str(), userName); // PN_JoinTCPIPGame
+	Address address = getAddressFromString(IP);
+
+	bool isLocal = false;
+	// TODO: 20-bit block address (172.16.0.0 – 172.31.255.255)
+	if (address.host == "127.0.0.1" || address.host == "localhost" || address.host == "255.255.255.255" ||
+		address.host.matchString("10.*.*.*") || address.host.matchString("192.168.*.*")) {
+		isLocal = true;
+	}
+
+	if (isLocal) {
+		if (!address.port) {
+			// Local connection with no port specified.  Send a session request to get port:
+			startQuerySessions(false);
+			if (!_broadcastSocket) {
+				return 0;
+			}
+
+			_sessions.clear();
+			_broadcastSocket->send(address.host.c_str(), 9130, "{\"cmd\": \"get_session\"}");
+
+			uint tickCount = 0;
+			while(!_sessions.size()) {
+				serviceBroadcast();
+				// Wait for one minute for response before giving up
+				tickCount += 5;
+				g_system->delayMillis(5);
+				if (tickCount >= 1000)
+					break;
+			}
+
+			if (!_sessions.size())
+				return 0;
+
+			if (address.host == "255.255.255.255")
+				address.host = _sessions[0].host;
+			address.port = _sessions[0].port;
+			stopQuerySessions();
+		}
+		// We got our address and port, attempt connection:
+		if (connectToSession(address.host, address.port)) {
+			// Connected, add our user.
+			return addUser(userName, userName);
+		} else {
+			warning("NETWORK: Failed to connect to %s:%d", address.host.c_str(), address.port);
+		}
+	} else {
+		warning("STUB: joinGame: Public IP connection %s", address.host.c_str());
+	}
+
+	return 0;
+}
+
+bool Net::connectToSession(Common::String address, int port) {
+	if (_hostPort)
+		_sessionHost = _enet->connectToHost("0.0.0.0", _hostPort, address, port);
+	else
+		_sessionHost = _enet->connectToHost(address, port);
+	if (!_sessionHost)
+		return false;
+
+	_isHost = false;
+	return true;
+}
+
+int Net::addUser(char *shortName, char *longName) {
+	debug(1, "Net::addUser(\"%s\", \"%s\")", shortName, longName); // PN_AddUser
+	// TODO: What's the difference between shortName and longName?
+
+	if (_isHost) {
+		if (getTotalPlayers() > 4) {
+			// We are full.
+			return 0;
+		}
+		_userIdToName[++_userIdCounter] = longName;
+		_numUsers++;
+		if (_sessionId && _sessionServerPeer > -1) {
+			// Update player count to session server {
+			Common::String updatePlayers = Common::String::format(
+				"{\"cmd\":\"update_players\",\"game\":\"%s\",\"version\":\"%s\",\"players\":%d}",
+				_gameName.c_str(), _gameVersion.c_str(), getTotalPlayers());
+			_sessionHost->send(updatePlayers.c_str(), _sessionServerPeer);
+		}
+		return 1;
+	}
+
+	// Client:
+	if (_myUserId != -1)
+		return 1;
+
+	Common::String addUser = Common::String::format(
+		"{\"cmd\":\"add_user\",\"name\":\"%s\"}", longName);
+
+	_sessionHost->send(addUser.c_str(), 0, 0, true);
+
+	uint tickCount = 0;
+	while(_myUserId == -1) {
+		remoteReceiveData(12);
+		// Wait for five seconds for our user id before giving up
+		tickCount += 5;
+		g_system->delayMillis(5);
+		if (tickCount >= 5000)
+			break;
+	}
+	return (_myUserId > -1) ? 1 : 0;
+}
+
+int Net::removeUser() {
+	debug(1, "Net::removeUser()"); // PN_RemoveUser
+
+	if (_myUserId != -1)
+		destroyPlayer(_myUserId);
+
+	return 1;
+}
+
+int Net::whoSentThis() {
+	debug(1, "Net::whoSentThis(): return %d", _fromUserId); // PN_WhoSentThis
+	return _fromUserId;
+}
+
+int Net::whoAmI() {
+	debug(1, "Net::whoAmI(): return %d", _myUserId); // PN_WhoAmI
+	return _myUserId;
+}
+
+int Net::createSession(char *name) {
+	debug(1, "Net::createSession(\"%s\")", name); // PN_CreateSession
+
+	if (!_enet) {
+		return 0;
+	};
+
+	_sessionId = -1;
+	_sessionName = name;
+	// Normally we would do only one peer (0) or three peers (2) but we are reserving one
+	// for our connection to the session server.
+	_sessionHost = _enet->createHost("0.0.0.0", 0, _maxPlayers);
+
+	if (!_sessionHost) {
+		return 0;
+	}
+
+	_isHost = true;
+	// TODO: Config to enable/disable Internet sessions.
+	if (_sessionHost->connectPeer("127.0.0.1", 9120)) {
+		_sessionServerPeer = _sessionHost->getPeerIndexFromHost("127.0.0.1", 9120);
+		// Create session to the session server.
+		Common::String req = Common::String::format(
+			"{\"cmd\":\"host_session\",\"game\":\"%s\",\"version\":\"%s\",\"name\":\"%s\"}",
+			_gameName.c_str(), _gameVersion.c_str(), name);
+		_sessionHost->send(req.c_str(), _sessionServerPeer);
+	} else {
+		warning("Failed to connect to session server!  This game will not be listed on the Internet");
+	}
+
+	// TODO: Config to enable/disable LAN discovery.
+	_broadcastSocket = _enet->createSocket("0.0.0.0", 9130);
+	if (!_broadcastSocket) {
+		warning("NETWORK: Unable to create broadcast socket, your game will not be broadcast over LAN");
+	}
+
+	return 1;
+}
+
+int Net::getTotalPlayers() {
+	return _numUsers + _numBots;
+}
+
+int Net::joinSession(int sessionIndex) {
+	debug(1, "Net::joinSession(%d)", sessionIndex); // PN_JoinSession
+	if (_sessions.empty()) {
+		warning("Net::joinSession(): no sessions");
+		return 0;
+	}
+
+	if (sessionIndex >= (int)_sessions.size()) {
+		warning("Net::joinSession(): session number too big: %d >= %d", sessionIndex, _sessions.size());
+		return 0;
+	}
+
+	Session session = _sessions[sessionIndex];
+	if (!session.local && _sessionServerHost) {
+		Common::String joinSession = Common::String::format(
+			"{\"cmd\":\"join_session\",\"game\":\"%s\",\"version\":\"%s\",\"session\":%d}",
+			_gameName.c_str(), _gameVersion.c_str(), sessionIndex);
+		_sessionServerHost->send(joinSession.c_str(), 0);
+
+		// Give the host time to hole punch us.
+		g_system->delayMillis(500);
+	}
+
+	// Disconnect the session server to free up and use the same port we've connected previously.
+	if (_sessionServerHost) {
+		_sessionServerHost->disconnectPeer(0);
+		delete _sessionServerHost;
+		_sessionServerHost = nullptr;
+	}
+
+	bool success = connectToSession(session.host, session.port);
+	if (!success) {
+		if (!session.local) {
+			// Start up a relay session with the host.
+
+			// This will re-connect us to the session server.
+			startQuerySessions();
+			if (_sessionServerHost) {
+				Common::String startRelay = Common::String::format(
+					"{\"cmd\":\"start_relay\",\"game\":\"%s\",\"version\":\"%s\",\"session\":%d}",
+					_gameName.c_str(), _gameVersion.c_str(), session.id);
+				_sessionServerHost->send(startRelay.c_str(), 0);
+
+				uint tickCount = 0;
+				while(_myUserId == -1) {
+					serviceSessionServer();
+					// Wait for five seconds for our user id before giving up
+					tickCount += 5;
+					g_system->delayMillis(5);
+					if (tickCount >= 5000)
+						break;
+				}
+
+				if (_myUserId > -1)
+					// If we have gotten our user id, that means that we are now relaying.
+					return true;
+			}
+		}
+		_vm->displayMessage(0, "Unable to join game session with address \"%s:%d\"", session.host.c_str(), session.port);
+		return false;
+	}
+
+	return true;
+}
+
+int Net::endSession() {
+	debug(1, "Net::endSession()"); // PN_EndSession
+
+	if (_isHost && _hostDataQueue.size()) {
+		_isShuttingDown = true;
+		// Send out any remaining data from the queue before shutting down.
+		while (_hostDataQueue.size()) {
+			if (_hostDataQueue.size() != _hostDataQueue.size())
+				warning("NETWORK: Sizes of data and peer index queues does not match!  Expect some wonky stuff");
+			Common::JSONValue *json = _hostDataQueue.pop();
+			int peerIndex = _peerIndexQueue.pop();
+			handleGameDataHost(json, peerIndex);
+		_isShuttingDown = false;
+		}
+	}
+
+	if (_sessionHost && _sessionServerPeer > -1) {
+		_sessionHost->disconnectPeer(_sessionServerPeer);
+		_sessionServerPeer = -1;
+	}
+
+	if (_sessionHost) {
+		delete _sessionHost;
+		_sessionHost = nullptr;
+	}
+	if (_sessionServerHost) {
+		_sessionServerHost->disconnectPeer(0);
+		delete _sessionServerHost;
+		_sessionServerHost = nullptr;
+	}
+	if (_broadcastSocket) {
+		delete _broadcastSocket;
+		_broadcastSocket = nullptr;
+	}
+
+	_hostPort = 0;
+
+	_numUsers = 0;
+	_numBots = 0;
+
+	_userIdCounter = 0;
+	_userIdToName.clear();
+	_userIdToPeerIndex.clear();
+
+	_sessionId = -1;
+	_sessionName.clear();
+
+	_myUserId = -1;
+	_fromUserId = -1;
+
+	_hostDataQueue.clear();
+	_peerIndexQueue.clear();
+
+	_isRelayingGame = false;
+
+	return 0;
+}
+
+void Net::disableSessionJoining() {
+	debug(1, "Net::disableSessionJoining()"); // PN_DisableSessionPlayerJoin
+	if (_sessionHost && _sessionServerPeer > -1 && !_isRelayingGame) {
+		_sessionHost->disconnectPeer(_sessionServerPeer);
+		_sessionServerPeer = -1;
+	}
+	if (_broadcastSocket) {
+		delete _broadcastSocket;
+		_broadcastSocket = nullptr;
+	}
+}
+
+void Net::enableSessionJoining() {
+	warning("STUB: Net::enableSessionJoining()"); // PN_EnableSessionPlayerJoin
+}
+
+void Net::setBotsCount(int botsCount) {
+	debug(1, "Net::setBotsCount(%d)", botsCount); // PN_SetAIPlayerCountKludge
+	_numBots = botsCount;
+}
+
+int32 Net::setProviderByName(int32 parameter1, int32 parameter2) {
+	// char name[MAX_PROVIDER_NAME];
+	// char ipaddress[MAX_IP_SIZE];
+
+	// ipaddress[0] = '\0';
+
+	// _vm->getStringFromArray(parameter1, name, sizeof(name));
+	// if (parameter2)
+	// 	_vm->getStringFromArray(parameter2, ipaddress, sizeof(ipaddress));
+
+	// debug(1, "Net::setProviderByName(\"%s\", \"%s\")", name, ipaddress); // PN_SetProviderByName
+
+	// Emulate that we found a TCP/IP provider
+
+	// Create a new ENet instance and initialize the library.
+	if (_enet) {
+		warning("Net::setProviderByName: ENet instance already exists.");
+		return 1;
+	}
+	_enet = new Networking::ENet();
+	if (!_enet->initialize()) {
+		_vm->displayMessage(0, "Unable to initialize ENet library.");
+		Net::closeProvider();
+		return 0;
+	}
+	return 1;
+}
+
+void Net::setFakeLatency(int time) {
+	_latencyTime = time;
+	debug("NETWORK: Setting Fake Latency to %d ms", _latencyTime);
+	_fakeLatency = true;
+}
+
+bool Net::destroyPlayer(int32 userId) {
+	// bool PNETWIN_destroyplayer(DPID idPlayer)
+	debug(1, "Net::destroyPlayer(%d)", userId);
+	if (_isHost) {
+		if (userId == 1)
+			return true;
+		if (_userIdToName.contains(userId)) {
+			_userIdToName.erase(userId);
+			_numUsers--;
+
+			if (_userIdToAddress.contains(userId)) {
+				Common::String address = _userIdToAddress[userId];
+				_addressToUserId.erase(address);
+				_userIdToAddress.erase(userId);
+			}
+
+			if (_userIdToPeerIndex.contains(userId) && _userIdToPeerIndex[userId] != _sessionServerPeer) {
+				_sessionHost->disconnectPeer(_userIdToPeerIndex[userId]);
+				_userIdToPeerIndex.erase(userId);
+			}
+			return true;
+		}
+		warning("NETWORK: destoryPlayer(%d): User does not exist!", userId);
+		return false;
+	}
+
+	Common::String removerUser = "{\"cmd\":\"remove_user\"}";
+	_sessionHost->send(removerUser.c_str(), 0, 0, true);
+	_sessionHost->disconnectPeer(0);
+
+	return true;
+}
+
+int32 Net::startQuerySessions(bool connectToSessionServer) {
+	debug(1, "Net::startQuerySessions()");
+
+	// TODO: Config to enable/disable Internet sessions.
+	if (!_sessionServerHost && connectToSessionServer) {
+		// TODO: Configurable session server address and port
+		_sessionServerHost = _enet->connectToHost("127.0.0.1", 9120);
+		if (!_sessionServerHost)
+			warning("Failed to connect to session server!  You'll won't be able to join internet sessions");
+	}
+
+	// TODO: Config to enable/disable LAN discovery.
+	if (!_broadcastSocket) {
+		_broadcastSocket = _enet->createSocket("0.0.0.0", 0);
+	}
+	return 0;
+}
+
+int32 Net::updateQuerySessions() {
+	debug(1, "Net::updateQuerySessions(): begin"); // UpdateQuerySessions
+
+	if (_sessionServerHost) {
+		// Get internet-based sessions from the sessin server.
+		Common::String getSessions = Common::String::format(
+			"{\"cmd\":\"get_sessions\",\"game\":\"%s\",\"version\":\"%s\"}",
+			_gameName.c_str(), _gameVersion.c_str());
+		_sessionServerHost->send(getSessions.c_str(), 0);
+
+		uint32 tickCount = g_system->getMillis() + 100;
+		while(g_system->getMillis() < tickCount) {
+			serviceSessionServer();
+		}
+	}
+	if (_broadcastSocket) {
+		// Send a session query to the broadcast address.
+		_broadcastSocket->send("255.255.255.255", 9130, "{\"cmd\": \"get_session\"}");
+
+		uint32 tickCount = g_system->getMillis() + 500;
+		while(g_system->getMillis() < tickCount) {
+			serviceBroadcast();
+		}
+	}
+
+	for (Common::Array<Session>::iterator i = _sessions.begin(); i != _sessions.end();) {
+		if (g_system->getMillis() - i->timestamp > 5000) {
+			// It has been 5 seconds since we have last seen this session, remove it.
+			i = _sessions.erase(i);
+		} else {
+			i++;
+		}
+	}
+
+	debug(1, "Net::updateQuerySessions(): got %d", _sessions.size());
+	return _sessions.size();
+}
+
+void Net::stopQuerySessions() {
+	debug(1, "Net::stopQuerySessions()"); // StopQuerySessions
+
+	if (_sessionServerHost && !_isRelayingGame) {
+		_sessionServerHost->disconnectPeer(0);
+		delete _sessionServerHost;
+		_sessionServerHost = nullptr;
+	}
+
+	if (_broadcastSocket) {
+		delete _broadcastSocket;
+		_broadcastSocket = nullptr;
+	}
+
+	_sessions.clear();
+	// No op
+}
+
+int Net::querySessions() {
+	debug(1, "Net::querySessions()"); // PN_QuerySessions
+	// Deprecated OP used in Backyard Football 2002 to query sessions,
+	// emulate this by using the functions used in Moonbase Commander:
+	startQuerySessions();
+
+	return updateQuerySessions();
+}
+
+int Net::queryProviders() {
+	debug(1, "Net::queryProviders()"); // PN_QueryProviders
+
+	// Emulate that we have 1 provider, TCP/IP
+	return 1;
+}
+
+int Net::setProvider(int providerIndex) {
+	warning("STUB: Net::setProvider(%d)", providerIndex); // PN_SetProvider
+	return 0;
+}
+
+int Net::closeProvider() {
+	debug(1, "Net::closeProvider()"); // PN_CloseProvider
+	if (_enet) {
+		// Destroy all ENet instances and deinitialize.
+		if (_sessionHost) {
+			endSession();
+		}
+		delete _enet;
+		_enet = nullptr;
+	}
+
+	return 1;
+}
+
+bool Net::initAll() {
+	warning("STUB: Net::initAll()"); // PN_DoInitAll
+	return false;
+}
+
+bool Net::initProvider() {
+	warning("STUB: Net::initProvider()"); // PN_DoInitProvider
+	return false;
+}
+
+bool Net::initSession() {
+	warning("STUB: Net::initSession()"); // PN_DoInitSession
+	return false;
+}
+
+bool Net::initUser() {
+	warning("STUB: Net::initUser()"); // PN_DoInitUser
+	return false;
+}
+
+void Net::remoteStartScript(int typeOfSend, int sendTypeParam, int priority, int argsCount, int32 *args) {
+	Common::String res = "\"params\": [";
+
+	if (argsCount > 2)
+		for (int i = 0; i < argsCount - 1; i++)
+			res += Common::String::format("%d,", args[i]);
+
+	if (argsCount > 1)
+		res += Common::String::format("%d]", args[argsCount - 1]);
+	else
+		res += "]";
+
+	debug(1, "Net::remoteStartScript(%d, %d, %d, %d, ...)", typeOfSend, sendTypeParam, priority, argsCount); // PN_RemoteStartScriptCommand
+
+	remoteSendData(typeOfSend, sendTypeParam, PACKETTYPE_REMOTESTARTSCRIPT, res, priority);
+}
+
+int Net::remoteSendData(int typeOfSend, int sendTypeParam, int type, Common::String data, int priority, int defaultRes, bool wait, int callid) {
+	if (!_enet || !_sessionHost || _myUserId == -1)
+		return defaultRes;
+
+	if (typeOfSend == PN_SENDTYPE_INDIVIDUAL && sendTypeParam == 0)
+		// In DirectPlay, sending a message to 0 means all players
+		// sooo, send all.
+		typeOfSend = PN_SENDTYPE_ALL;
+
+	// Since I am lazy, instead of constructing the JSON object manually
+	// I'd rather parse it
+	Common::String res = Common::String::format(
+		"{\"cmd\":\"game\",\"from\":%d,\"to\":%d,\"toparam\":%d,"
+		"\"type\":%d, \"reliable\":%s, \"data\":{%s}}",
+		_myUserId, typeOfSend, sendTypeParam, type,
+		priority == PN_PRIORITY_HIGH ? "true" : "false", data.c_str());
+
+	debug(1, "NETWORK: Sending data: %s", res.c_str());
+	Common::JSONValue *str = Common::JSON::parse(res.c_str());
+	if (_isHost) {
+		// handleGameDataHost(str, sendTypeParam - 1);
+		_hostDataQueue.push(str);
+		_peerIndexQueue.push(sendTypeParam - 1);
+	} else
+		_sessionHost->send(res.c_str(), 0, 0, priority == PN_PRIORITY_HIGH);
+	return defaultRes;
+}
+
+void Net::remoteSendArray(int typeOfSend, int sendTypeParam, int priority, int arrayIndex) {
+	debug(1, "Net::remoteSendArray(%d, %d, %d, %d)", typeOfSend, sendTypeParam, priority, arrayIndex & ~0x33539000); // PN_RemoteSendArrayCommand
+
+	ScummEngine_v90he::ArrayHeader *ah = (ScummEngine_v90he::ArrayHeader *)_vm->getResourceAddress(rtString, arrayIndex & ~0x33539000);
+
+	Common::String jsonData = Common::String::format(
+		"\"type\":%d,\"dim1start\":%d,\"dim1end\":%d,\"dim2start\":%d,\"dim2end\":%d,\"data\":[",
+		ah->type, ah->dim1start, ah->dim1end, ah->dim2start, ah->dim2end);
+
+	int32 size = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+		(FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
+
+	for (int i = 0; i < size; i++) {
+		int32 data;
+
+		switch (FROM_LE_32(ah->type)) {
+		case ScummEngine_v90he::kByteArray:
+		case ScummEngine_v90he::kStringArray:
+			data = ah->data[i];
+			break;
+
+		case ScummEngine_v90he::kIntArray:
+			data = (int16)READ_LE_UINT16(ah->data + i * 2);
+			break;
+
+		case ScummEngine_v90he::kDwordArray:
+			data = (int32)READ_LE_UINT32(ah->data + i * 4);
+			break;
+
+		default:
+			error("Net::remoteSendArray(): Unknown array type %d for array %d", FROM_LE_32(ah->type), arrayIndex);
+		}
+
+		jsonData += Common::String::format("%d", data);
+
+		if (i < size - 1)
+			jsonData += ",";
+		else
+			jsonData += "]";
+	}
+
+	remoteSendData(typeOfSend, sendTypeParam, PACKETTYPE_REMOTESENDSCUMMARRAY, jsonData, priority);
+}
+
+int Net::remoteStartScriptFunction(int typeOfSend, int sendTypeParam, int priority, int defaultReturnValue, int argsCount, int32 *args) {
+	warning("STUB: Net::remoteStartScriptFunction(%d, %d, %d, %d, %d, ...)", typeOfSend, sendTypeParam, priority, defaultReturnValue, argsCount);
+	return 0;
+	int callid = _vm->_rnd.getRandomNumber(1000000);
+
+	Common::String res = Common::String::format("\"callid\":%d, \"params\": [", callid);
+
+	if (argsCount > 2)
+		for (int i = 0; i < argsCount - 1; i++)
+			res += Common::String::format("%d, ", args[i]);
+
+	if (argsCount > 1)
+		res += Common::String::format("%d]", args[argsCount - 1]);
+	else
+		res += "]";
+
+	debug(1, "Net::remoteStartScriptFunction(%d, %d, %d, %d, %d, ...)", typeOfSend, sendTypeParam, priority, defaultReturnValue, argsCount); // PN_RemoteStartScriptFunction
+
+	return remoteSendData(typeOfSend, sendTypeParam, PACKETTYPE_REMOTESTARTSCRIPTRETURN, res, defaultReturnValue, true, callid);
+}
+
+bool Net::getHostName(char *hostname, int length) {
+	warning("STUB: Net::getHostName(\"%s\", %d)", hostname, length); // PN_GetHostName
+	return false;
+}
+
+bool Net::getIPfromName(char *ip, int ipLength, char *nameBuffer) {
+	warning("STUB: Net::getIPfromName(\"%s\", %d, \"%s\")", ip, ipLength, nameBuffer); // PN_GetIPfromName
+	return false;
+}
+
+void Net::getSessionName(int sessionNumber, char *buffer, int length) {
+	debug(1, "Net::getSessionName(%d, ..., %d)", sessionNumber, length); // PN_GetSessionName
+
+	if (_sessions.empty()) {
+		*buffer = '\0';
+		warning("Net::getSessionName(): no sessions");
+		return;
+	}
+
+	if (sessionNumber >= (int)_sessions.size()) {
+		*buffer = '\0';
+		warning("Net::getSessionName(): session number too big: %d >= %d", sessionNumber, (int)_sessions.size());
+		return;
+	}
+
+	Common::strlcpy(buffer, _sessions[sessionNumber].name.c_str(), length);
+}
+
+int Net::getSessionPlayerCount(int sessionNumber) {
+	debug(1, "Net::getSessionPlayerCount(%d)", sessionNumber); // case GET_SESSION_PLAYER_COUNT_KLUDGE:
+
+	if (_sessions.empty()) {
+		warning("Net::getSessionPlayerCount(): no sessions");
+		return 0;
+	}
+
+	if (sessionNumber >= (int)_sessions.size()) {
+		warning("Net::getSessionPlayerCount(): session number too big: %d >= %d", sessionNumber, (int)_sessions.size());
+		return 0;
+	}
+
+	if (_sessions[sessionNumber].players < 1) {
+		warning("Net::getSessionPlayerCount(): no players in session");
+		return 0;
+	}
+
+	return _sessions[sessionNumber].players;
+}
+
+void Net::getProviderName(int providerIndex, char *buffer, int length) {
+	warning("STUB: Net::getProviderName(%d, \"%s\", %d)", providerIndex, buffer, length); // PN_GetProviderName
+}
+
+void Net::serviceSessionServer() {
+	if (!_sessionServerHost)
+		return;
+
+	uint8 type = _sessionServerHost->service();
+	switch(type) {
+	case ENET_EVENT_TYPE_NONE:
+		break;
+	case ENET_EVENT_TYPE_DISCONNECT:
+		warning("NETWORK: Lost connection to session server");
+		delete _sessionServerHost;
+		_sessionServerHost = nullptr;
+		break;
+	case ENET_EVENT_TYPE_RECEIVE:
+		handleSessionServerData(_sessionServerHost->getPacketData());
+		break;
+	}
+}
+
+void Net::handleSessionServerData(Common::String data) {
+	debug(1, "NETWORK: Received data from session server.  Data: %s", data.c_str());
+
+	Common::JSONValue *json = Common::JSON::parse(data.c_str());
+	if (!json) {
+		warning("NETWORK: Received non-JSON string from session server ignoring.");
+		return;
+	}
+	if (!json->isObject()){
+		warning("NETWORK: Received non-JSON object from session server: \"%s\"", data.c_str());
+		return;
+	}
+
+	Common::JSONObject root = json->asObject();
+	if (root.contains("cmd") && root["cmd"]->isString()) {
+		Common::String command = root["cmd"]->asString();
+		if (_isHost && command == "host_session_resp") {
+			if (root.contains("id")) {
+				_sessionId = root["id"]->asIntegerNumber();
+				debug(1, "NETWORK: Our session id from session server: %d", _sessionId);
+			}
+		} else if (!_isHost && command == "get_sessions_resp") {
+			if (root.contains("address") && root.contains("sessions")) {
+				_hostPort = getAddressFromString(root["address"]->asString()).port;
+				Common::JSONArray sessions = root["sessions"]->asArray();
+				for (uint i = 0; i != sessions.size(); i++) {
+					Common::JSONObject sessionData = sessions[i]->asObject();
+					Address sessionAddress = getAddressFromString(sessionData["address"]->asString());
+
+					// Check if we already know about this session:
+					bool makeNewSession = true;
+					for (Common::Array<Session>::iterator j = _sessions.begin(); j != _sessions.end(); j++) {
+						if (j->id == sessionData["id"]->asIntegerNumber()) {
+							// Yes we do, Update the timestamp and player count.
+							makeNewSession = false;
+							if (!j->local) {
+								// Only update if it's not a local session
+								j->timestamp = g_system->getMillis();
+								j->players = sessionData["players"]->asIntegerNumber();
+							}
+							break;
+						}
+					}
+
+					if (!makeNewSession)
+						continue;
+
+					Session session;
+					session.id = sessionData["id"]->asIntegerNumber();
+					session.name = sessionData["name"]->asString();
+					session.players = sessionData["players"]->asIntegerNumber();
+					session.host = sessionAddress.host;
+					session.port = sessionAddress.port;
+					session.timestamp = g_system->getMillis();
+					_sessions.push_back(session);
+				}
+			}
+		} else if (_isHost && command == "joining_session") {
+			// Someone is gonna attempt to join our session.  Get their address and hole-punch:
+			if (_sessionHost && root.contains("address")) {
+				Address address = getAddressFromString(root["address"]->asString());
+				// By sending an UDP packet, the router will open a hole for the
+				// destinated address, allowing someone with the same address to
+				// communicate with us.  This does not work with every router though...
+				//
+				// More infomation: https://en.wikipedia.org/wiki/UDP_hole_punching
+				debug(1, "NETWORK: Hole punching %s:%d", address.host.c_str(), address.port);
+				_sessionHost->sendRawData(address.host, address.port, "");
+			}
+		} else if (_isHost && command == "add_user_for_relay") {
+			// For cases that peer-to-peer communication is not possible, the session server
+			// will act as a relay to the host.
+			if (root.contains("address")) {
+				// To be sent back for context.
+				Common::String address = root["address"]->asString();
+
+				if (addUser(const_cast<char *>(address.c_str()), const_cast<char *>(address.c_str()))) {
+					_userIdToAddress[_userIdCounter] = "127.0.0.1:9120";
+					_addressToUserId["127.0.0.1:9120"] = _userIdCounter;
+					_userIdToPeerIndex[_userIdCounter] = _sessionServerPeer;
+
+					_isRelayingGame = true;
+
+					Common::String resp = Common::String::format(
+						"{\"cmd\":\"add_user_resp\",\"game\":\"%s\",\"version\":\"%s\",\"address\":\"%s\",\"id\":%d}",
+						_gameName.c_str(), _gameVersion.c_str(), address.c_str(), _userIdCounter);
+					_sessionHost->send(resp.c_str(), _sessionServerPeer);
+				}
+			}
+		} else if (!_isHost && command == "add_user_resp") {
+			if (root.contains("id") && _myUserId == -1) {
+				_myUserId = root["id"]->asIntegerNumber();
+
+				// We are now relaying data to the session server,
+				// set the sessionServerHost as the sessionHost.
+				_isRelayingGame = true;
+				_sessionHost = _sessionServerHost;
+				_sessionServerHost = nullptr;
+			}
+		} else if (_isHost && command == "remove_user") {
+			// Relay user wants their removal (if they haven't been removed already).
+			if (root.contains("id")) {
+				int userId = root["id"]->asIntegerNumber();
+				if (_userIdToName.contains(userId)) {
+					if (_userIdToPeerIndex[userId] == _sessionServerPeer) {
+						debugC(1, "Removing relay user %d", userId);
+						destroyPlayer(userId);
+					} else {
+						warning("NETWORK: Attempt to remove non-relay user: %d", userId);
+					}
+				}
+			}
+		} else if (command == "game") {
+			// Received relayed data.
+			if (_isHost)
+				handleGameDataHost(json, _sessionServerPeer);
+			else
+				handleGameData(json, _sessionServerPeer);
+		}
+	}
+}
+
+bool Net::serviceBroadcast() {
+	if (!_broadcastSocket)
+		return false;
+
+	if (!_broadcastSocket->receive())
+		return false;
+
+	handleBroadcastData(_broadcastSocket->getData(), _broadcastSocket->getHost(), _broadcastSocket->getPort());
+	return true;
+}
+
+void Net::handleBroadcastData(Common::String data, Common::String host, int port) {
+	debug(1, "NETWORK: Received data from broadcast socket.  Source: %s:%d  Data: %s", host.c_str(), port, data.c_str());
+
+	Common::JSONValue *json = Common::JSON::parse(data.c_str());
+	if (!json) {
+		// Just about anything could come from the broadcast address, so do not warn.
+		debug(1, "NETWORK: Not a JSON string, ignoring.");
+		return;
+	}
+	if (!json->isObject()){
+		warning("NETWORK: Received non JSON object from broadcast socket: \"%s\"", data.c_str());
+		return;
+	}
+
+	Common::JSONObject root = json->asObject();
+	if (root.contains("cmd") && root["cmd"]->isString()) {
+		Common::String command = root["cmd"]->asString();
+
+		if (command == "get_session") {
+			// Session query.
+			if (_sessionHost) {
+				Common::String resp = Common::String::format(
+					"{\"cmd\":\"session_resp\",\"version\":\"%s\",\"id\":%d,\"name\":\"%s\",\"players\":%d}",
+					_gameVersion.c_str(), _sessionId, _sessionName.c_str(), getTotalPlayers());
+
+				// Send this through the session host instead of the broadcast socket
+				// because that will send the correct port to connect to.
+				// They'll still receive it though, that's the power of connection-less sockets.
+				_sessionHost->sendRawData(host, port, resp.c_str());
+			}
+		} else if (command == "session_resp") {
+			if (!_sessionHost && root.contains("version") && root.contains("id") && root.contains("name") && root.contains("players")) {
+				Common::String version = root["version"]->asString();
+				int sessionId = root["id"]->asIntegerNumber();
+				Common::String name = root["name"]->asString();
+				int players = root["players"]->asIntegerNumber();
+
+				// TODO: Check and match game name
+
+				if (version != _gameVersion)
+					// Version mismatch.
+					return;
+
+				if (players < 1 || players > _maxPlayers)
+					// This session is either full or not finished initalizing (adding the host player itself)
+					return;
+
+				// Check if the session of the game ID (from the internet session server) exists.
+				// if so, update it as a local session and swap the internet-based address to local.
+				for (Common::Array<Session>::iterator i = _sessions.begin(); i != _sessions.end(); i++) {
+					if (i->id == sessionId && !i->local) {
+						i->local = true;
+						i->host = host;
+						i->port = port;
+						i->timestamp = g_system->getMillis();
+						i->players = players;
+						return;
+					}
+				}
+				// Check if we already know about this session:
+				for (Common::Array<Session>::iterator i = _sessions.begin(); i != _sessions.end(); i++) {
+					if (i->host == host && i->port == port) {
+						// Yes we do, Update the timestamp and player count.
+						i->timestamp = g_system->getMillis();
+						i->players = players;
+						return;
+					}
+				}
+				// If we're here, assume that we had no clue about this session, store it.
+				Session session;
+				session.local = true;
+				session.host = host;
+				session.port = port;
+				session.name = name;
+				session.players = players;
+				session.timestamp = g_system->getMillis();
+				_sessions.push_back(session);
+			}
+		}
+	}
+}
+
+bool Net::remoteReceiveData(uint32 tickCount) {
+	uint8 messageType = _sessionHost->service();
+	switch (messageType) {
+	case ENET_EVENT_TYPE_NONE:
+		return true;
+	case ENET_EVENT_TYPE_CONNECT:
+		{
+			debug(1, "NETWORK: New connection from %s:%d", _sessionHost->getHost().c_str(), _sessionHost->getPort());
+			return true;
+		}
+		return true;
+	case ENET_EVENT_TYPE_DISCONNECT:
+		{
+			Common::String address = Common::String::format("%s:%d", _sessionHost->getHost().c_str(), _sessionHost->getPort());
+
+			int userId = -1;
+			if (_addressToUserId.contains(address))
+				userId = _addressToUserId[address];
+			if (userId > -1)
+				debug(1, "NETWORK: User %s (%d) has disconnected.", _userIdToName[userId].c_str(), userId);
+			else
+				debug(1, "NETWORK: Connection from %s has disconnected.", address.c_str());
+
+			if (_gameName == "moonbase") {
+				// TODO: Host migration
+				if (!_isHost && _vm->_currentRoom == 2) {
+					_vm->displayMessage(0, "You have been disconnected from the host.\nNormally, host migration would take place, but ScummVM doesn't do that yet, so this game session will now end." );
+					_vm->VAR(253) = 26; // gGameMode = GAME-OVER
+					_vm->runScript(2104, 1, 0, 0); // leave-game
+				}
+			}
+			return true;
+		}
+		return true;
+	case ENET_EVENT_TYPE_RECEIVE:
+		{
+			Common::String host = _sessionHost->getHost();
+			int port = _sessionHost->getPort();
+			debug(1, "NETWORK: Got data from %s:%d", host.c_str(), port);
+
+			int peerIndex = _sessionHost->getPeerIndexFromHost(host, port);
+			if (peerIndex == -1) {
+				warning("NETWORK: Unable to get peer index for host %s:%d", host.c_str(), port);
+				_sessionHost->destroyPacket();
+				return false;
+			}
+
+			Common::String data = _sessionHost->getPacketData();
+			debug(1, "%s", data.c_str());
+
+			if (peerIndex == _sessionServerPeer) {
+				handleSessionServerData(data);
+				return true;
+			}
+
+			Common::JSONValue *json = Common::JSON::parse(data.c_str());
+			if (!json) {
+				// Just about anything could come from the broadcast address, so do not warn.
+				warning("NETWORK: Received non-JSON string.  Got: \"%s\"", data.c_str());
+				_sessionHost->destroyPacket();
+				return false;
+			}
+			if (!json->isObject()){
+				warning("NETWORK: Received non JSON object from broadcast socket: \"%s\"", data.c_str());
+				_sessionHost->destroyPacket();
+				return false;
+			}
+
+			Common::JSONObject root = json->asObject();
+			if (root.contains("cmd") && root["cmd"]->isString()) {
+				Common::String command = root["cmd"]->asString();
+
+				if (_isHost && command == "add_user") {
+					if (root.contains("name")) {
+						Common::String name = root["name"]->asString();
+						if (getTotalPlayers() > 4) {
+							// We are full.
+							return 0;
+						}
+						_userIdToName[++_userIdCounter] = name;
+						_numUsers++;
+						if (_sessionId && _sessionServerPeer > -1) {
+							// Update player count to session server
+							Common::String updatePlayers = Common::String::format(
+								"{\"cmd\":\"update_players\",\"game\":\"%s\",\"version\":\"%s\",\"players\":%d}",
+								_gameName.c_str(), _gameVersion.c_str(), getTotalPlayers());
+							_sessionHost->send(updatePlayers.c_str(), _sessionServerPeer);
+						}
+
+						Common::String address = Common::String::format("%s:%d", host.c_str(), port);
+						_userIdToAddress[_userIdCounter] = address;
+						_addressToUserId[address] = _userIdCounter;
+						_userIdToPeerIndex[_userIdCounter] = peerIndex;
+
+						Common::String resp = Common::String::format(
+							"{\"cmd\":\"add_user_resp\",\"id\":%d}", _userIdCounter);
+						_sessionHost->send(resp.c_str(), peerIndex);
+					}
+				} else if (!_isHost && command == "add_user_resp") {
+					if (root.contains("id") && _myUserId == -1) {
+						_myUserId = root["id"]->asIntegerNumber();
+					}
+				} else if (_isHost && command == "remove_user") {
+					Common::String address = Common::String::format("%s:%d", host.c_str(), port);
+					int userId = -1;
+					userId = _addressToUserId[address];
+					if (userId == -1) {
+						warning("Got remove_user but we don't know the user for address: %s", address.c_str());
+						return false;
+					}
+					destroyPlayer(userId);
+				} else if (command == "game") {
+					if (_isHost)
+						handleGameDataHost(json, peerIndex);
+					else
+						handleGameData(json, peerIndex);
+				}
+			}
+			if (_sessionHost)
+				_sessionHost->destroyPacket();
+		}
+		return true;
+		break;
+	}
+	return true;
+}
+
+void Net::doNetworkOnceAFrame(int msecs) {
+	if (!_enet || !_sessionHost)
+		return;
+
+	remoteReceiveData(msecs);
+
+	if (_sessionServerHost)
+		serviceSessionServer();
+
+	if (_broadcastSocket)
+		serviceBroadcast();
+
+	if (_isHost && _hostDataQueue.size()) {
+		if (_hostDataQueue.size() != _hostDataQueue.size())
+			warning("NETWORK: Sizes of data and peer index queues does not match!  Expect some wonky stuff");
+		Common::JSONValue *json = _hostDataQueue.pop();
+		int peerIndex = _peerIndexQueue.pop();
+		handleGameDataHost(json, peerIndex);
+	}
+}
+
+void Net::handleGameData(Common::JSONValue *json, int peerIndex) {
+	if (!_enet || !_sessionHost)
+		return;
+	_fromUserId = json->child("from")->asIntegerNumber();
+	uint type = json->child("type")->asIntegerNumber();
+
+	uint32 *params;
+
+	switch (type) {
+	case PACKETTYPE_REMOTESTARTSCRIPT:
+		{
+			Common::JSONArray paramsArray = json->child("data")->child("params")->asArray();
+
+			if (_gameName == "moonbase") {
+				// Detect if the host has disconnected.
+				if (paramsArray[0]->asIntegerNumber() == 145 && _fromUserId == 1) {
+					if (!_isHost && _vm->_currentRoom == 2) {
+						// TODO: Host migration
+						_vm->displayMessage(0, "You have been disconnected from the host.\nNormally, host migration would take place, but ScummVM doesn't do that yet, so this game session will now end.");
+						_vm->VAR(253) = 26; // GAME-OVER
+						_vm->runScript(2104, 1, 0, 0); // leave-game
+						return;
+					}
+				}
+			}
+
+			int datalen = paramsArray.size();
+			params = (uint32 *)_tmpbuffer;
+
+			for (int i = 0; i < datalen; i++) {
+				*params = paramsArray[i]->asIntegerNumber();
+				params++;
+			}
+
+			if (!_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT)) {
+				warning("NETWORK: VAR_REMOTE_START_SCRIPT not defined!");
+				return;
+			}
+
+			debug("%d", _vm->VAR(_vm->VAR_REMOTE_START_SCRIPT));
+
+			_vm->runScript(_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT), 1, 0, (int *)_tmpbuffer);
+			_vm->pop();
+		}
+		break;
+
+	case PACKETTYPE_REMOTESTARTSCRIPTRETURN:
+		{
+			int datalen = json->child("data")->child("params")->asArray().size();
+			params = (uint32 *)_tmpbuffer;
+
+			for (int i = 0; i < datalen; i++) {
+				*params = json->child("data")->child("params")->asArray()[i]->asIntegerNumber();
+				params++;
+			}
+
+			_vm->runScript(_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT), 1, 0, (int *)_tmpbuffer);
+			int result = _vm->pop();
+
+			Common::String res = Common::String::format("\"result\": %d, \"callid\": %d", result,
+					(int)json->child("data")->child("callid")->asIntegerNumber());
+
+			remoteSendData(PN_SENDTYPE_INDIVIDUAL, _fromUserId, PACKETTYPE_REMOTESTARTSCRIPTRESULT, res, PN_PRIORITY_HIGH);
+		}
+		break;
+
+	case PACKETTYPE_REMOTESTARTSCRIPTRESULT:
+		//
+		// Ignore it.
+		//
+
+		break;
+
+	case PACKETTYPE_REMOTESENDSCUMMARRAY:
+		{
+			int newArray = 0;
+
+			// Assume that the packet data contains a "SCUMM PACKAGE"
+			// and unpack it into an scumm array :-)
+
+			int dim1start = json->child("data")->child("dim1start")->asIntegerNumber();
+			int dim1end   = json->child("data")->child("dim1end")->asIntegerNumber();
+			int dim2start = json->child("data")->child("dim2start")->asIntegerNumber();
+			int dim2end   = json->child("data")->child("dim2end")->asIntegerNumber();
+			int atype     = json->child("data")->child("type")->asIntegerNumber();
+
+			byte *data = _vm->defineArray(0, atype, dim2start, dim2end, dim1start, dim1end, true, &newArray);
+
+			int32 size = (dim1end - dim1start + 1) * (dim2end - dim2start + 1);
+
+			int32 value;
+
+			for (int i = 0; i < size; i++) {
+				value = json->child("data")->child("data")->asArray()[i]->asIntegerNumber();
+
+				switch (atype) {
+				case ScummEngine_v90he::kByteArray:
+				case ScummEngine_v90he::kStringArray:
+					data[i] = value;
+					break;
+
+				case ScummEngine_v90he::kIntArray:
+					WRITE_LE_UINT16(data + i * 2, value);
+					break;
+
+				case ScummEngine_v90he::kDwordArray:
+					WRITE_LE_UINT32(data + i * 4, value);
+					break;
+
+				default:
+					error("Net::remoteReceiveData(): Unknown array type %d", atype);
+				}
+			}
+
+			memset(_tmpbuffer, 0, 25 * 4);
+			WRITE_UINT32(_tmpbuffer, newArray);
+
+			// Quick start the script (1st param is the new array)
+			_vm->runScript(_vm->VAR(_vm->VAR_NETWORK_RECEIVE_ARRAY_SCRIPT), 1, 0, (int *)_tmpbuffer);
+		}
+		break;
+
+	default:
+		warning("NETWORK: Received unknown network command %d", type);
+	}
+
+}
+
+void Net::handleGameDataHost(Common::JSONValue *json, int peerIndex) {
+	int from = json->child("from")->asIntegerNumber();
+	int to = json->child("to")->asIntegerNumber();
+	int toparam = json->child("toparam")->asIntegerNumber();
+	bool reliable = json->child("reliable")->asBool();
+
+	switch(to) {
+	case PN_SENDTYPE_INDIVIDUAL:
+		{
+			if (toparam == _myUserId) {
+				// It's for us, handle it.
+				handleGameData(json, peerIndex);
+				return;
+			}
+			// It's for someone else, transfer it.
+			if (!_userIdToName.contains(toparam)) {
+				warning("NETWORK: Got individual message for %d, but we don't know this person!  Ignoring...", toparam);
+				return;
+			}
+			debug(1, "NETWORK: Transfering message to %s (%d), peerIndex: %d", _userIdToName[toparam].c_str(), toparam, _userIdToPeerIndex[toparam]);
+			Common::String str = Common::JSON::stringify(json);
+			_sessionHost->send(str.c_str(), _userIdToPeerIndex[toparam], 0, reliable);
+		}
+		break;
+	case PN_SENDTYPE_GROUP:
+		warning("STUB: PN_SENDTYPE_GROUP");
+		break;
+	case PN_SENDTYPE_HOST:
+		{
+			// It's for us, handle it.
+			handleGameData(json, peerIndex);
+		}
+		break;
+	case PN_SENDTYPE_ALL:
+		{
+			// It's for all of us, including the host.
+			// Don't handle data if we're shutting down, or the game will crash.
+			if (!_isShuttingDown && from != _myUserId)
+				handleGameData(json, peerIndex);
+			Common::String str = Common::JSON::stringify(json);
+			bool sentToSessionServer = false;
+			for (int i = 0; i < _numUsers; i++) {
+				if (i != _userIdToPeerIndex[from]) {
+					if (i == _sessionServerPeer) {
+						// If we are relaying game data, make sure that the data only get sent
+						// to the session server only once.
+						if (_isRelayingGame && !sentToSessionServer) {
+							_sessionHost->send(str.c_str(), _sessionServerPeer, 0, reliable);
+							sentToSessionServer = true;
+						}
+					} else
+						_sessionHost->send(str.c_str(), i, 0, reliable);
+				}
+			}
+		}
+		break;
+	default:
+		warning("NETWORK: Unknown data type: %d", to);
+	}
+}
+
+} // End of namespace Scumm
\ No newline at end of file
diff --git a/engines/scumm/he/moonbase/net_main.h b/engines/scumm/he/net/net_main.h
similarity index 50%
rename from engines/scumm/he/moonbase/net_main.h
rename to engines/scumm/he/net/net_main.h
index cf78a52374c..25da3850e87 100644
--- a/engines/scumm/he/moonbase/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -19,22 +19,36 @@
  *
  */
 
-#ifndef SCUMM_HE_MOONBASE_NET_MAIN_H
-#define SCUMM_HE_MOONBASE_NET_MAIN_H
-
-#include "backends/networking/curl/postrequest.h"
+#ifndef SCUMM_HE_NET_MAIN_H
+#define SCUMM_HE_NET_MAIN_H
 
+#include "common/formats/json.h"
+#include "backends/networking/enet/enet.h"
+#include "backends/networking/enet/host.h"
+#include "backends/networking/enet/socket.h"
 namespace Scumm {
 
-class ScummEngine_v100he;
+class ScummEngine_v90he;
 
 class Net {
 public:
-	Net(ScummEngine_v100he *vm);
+	Net(ScummEngine_v90he *vm);
 	~Net();
 
+	struct Address {
+		Common::String host;
+		int port;
+		bool operator==(const Address &other) {
+			return host == other.host && port == other.port;
+		};
+	};
+
+private:
+	Address getAddressFromString(Common::String address);
+	Common::String getStringFromAddress(Address address);
+public:
 	int hostGame(char *sessionName, char *userName);
-	int joinGame(char *IP, char *userName);
+	int joinGame(Common::String IP, char *userName);
 	int addUser(char *shortName, char *longName);
 	int removeUser();
 	int whoSentThis();
@@ -47,8 +61,8 @@ public:
 	void setBotsCount(int botsCount);
 	int32 setProviderByName(int32 parameter1, int32 parameter2);
 	void setFakeLatency(int time);
-	bool destroyPlayer(int32 playerDPID);
-	int32 startQuerySessions();
+	bool destroyPlayer(int32 userId);
+	int32 startQuerySessions(bool connectToSessionServer = true);
 	int32 updateQuerySessions();
 	void stopQuerySessions();
 	int querySessions();
@@ -60,34 +74,20 @@ public:
 	bool initSession();
 	bool initUser();
 	void remoteStartScript(int typeOfSend, int sendTypeParam, int priority, int argsCount, int32 *args);
-	int remoteSendData(int typeOfSend, int sendTypeParam, int type, Common::String data, int defaultRes = 0, bool wait = false, int callid = 0);
+	int remoteSendData(int typeOfSend, int sendTypeParam, int type, Common::String data, int priority, int defaultRes = 0, bool wait = false, int callid = 0);
 	void remoteSendArray(int typeOfSend, int sendTypeParam, int priority, int arrayIndex);
 	int remoteStartScriptFunction(int typeOfSend, int sendTypeParam, int priority, int defaultReturnValue, int argsCount, int32 *args);
 	void doNetworkOnceAFrame(int msecs);
+	void handleGameData(Common::JSONValue *json, int peerIndex);
+	void handleGameDataHost(Common::JSONValue *json, int peerIndex);
 
 private:
-	bool remoteReceiveData();
-
-	void createSessionCallback(Common::JSONValue *response);
-	void createSessionErrorCallback(Networking::ErrorResponse error);
-
-	void startQuerySessionsCallback(Common::JSONValue *response);
-	void startQuerySessionsErrorCallback(Networking::ErrorResponse error);
-
-	void addUserCallback(Common::JSONValue *response);
-	void addUserErrorCallback(Networking::ErrorResponse error);
-
-	void disableSessionJoiningErrorCallback(Networking::ErrorResponse error);
-
-	void endSessionCallback(Common::JSONValue *response);
-	void endSessionErrorCallback(Networking::ErrorResponse error);
-
-	void destroyPlayerErrorCallback(Networking::ErrorResponse error);
-
-	void remoteSendDataErrorCallback(Networking::ErrorResponse error);
-
-	void remoteReceiveDataCallback(Common::JSONValue *response);
-	void remoteReceiveDataErrorCallback(Networking::ErrorResponse error);
+	bool connectToSession(Common::String address, int port);
+	bool serviceBroadcast();
+	void handleBroadcastData(Common::String data, Common::String host, int port);
+	void serviceSessionServer();
+	void handleSessionServerData(Common::String data);
+	bool remoteReceiveData(uint32 tickCount);
 
 public:
 	//getters
@@ -99,33 +99,66 @@ public:
 
 private:
 	//mostly getters
+	int getTotalPlayers();
 
 public:
 	//fields
 	int _latencyTime; // ms
 	bool _fakeLatency;
 
-	ScummEngine_v100he *_vm;
+	ScummEngine_v90he *_vm;
 
-	byte *_packbuffer;
-	int _packetsize;
-	byte *_tmpbuffer;
+	Common::String _gameName;
+	Common::String _gameVersion;
 
-	int _myUserId;
-	int _myPlayerKey;
+	Networking::ENet *_enet;
 
-	int _lastResult;
-
-	int _sessionid;
-
-	bool _sessionsBeingQueried;
+	byte *_tmpbuffer;
 
-	Common::JSONValue *_sessions;
-	Common::JSONValue *_packetdata;
+	int _numUsers;
+	int _numBots;
+	int _maxPlayers;
+	int _userIdCounter;
+	Common::HashMap<int, Common::String> _userIdToName;
+	Common::HashMap<int, int> _userIdToPeerIndex;
+	Common::HashMap<int, Common::String> _userIdToAddress;
+	Common::HashMap<Common::String, int> _addressToUserId;
 
-	Common::String _serverprefix;
+	int _myUserId;
+	int _fromUserId;
+
+	int _sessionId; // Session ID received from the session server.
+	Common::String _sessionName;
+	Networking::Host *_sessionHost;
+
+	bool _isHost;  // true = hosting game, false = joined game.
+	bool _isShuttingDown;
+
+	Common::Queue<Common::JSONValue *> _hostDataQueue;
+	Common::Queue<int> _peerIndexQueue;
+
+	struct Session {
+		bool local = false;
+		int id = -1;
+		Common::String host;
+		int port;
+		Common::String name;
+		int players;
+		uint32 timestamp;
+	};
+
+	Common::Array<Session> _sessions;
+	int _hostPort;
+
+	// For broadcasting our game session over LAN.
+	Networking::Socket *_broadcastSocket;
+
+	// For creating/joining sessions over the Internet.
+	Networking::Host *_sessionServerHost;
+	int _sessionServerPeer;
+	bool _isRelayingGame; // If we're relaying in-game data over the session server or not.
 };
 
 } // End of namespace Scumm
 
-#endif
+#endif
\ No newline at end of file
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 7ed34a42082..24b82623e21 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -166,9 +166,9 @@ MODULE_OBJS += \
 	he/moonbase/moonbase.o \
 	he/moonbase/moonbase_fow.o
 
-ifdef USE_LIBCURL
+ifdef USE_ENET
 MODULE_OBJS += \
-	he/moonbase/net_main.o
+	he/net/net_main.o
 endif
 endif
 
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 40fa616d415..831b264b01c 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -87,6 +87,10 @@
 #include "scumm/imuse/drivers/midi.h"
 #include "scumm/detection_steam.h"
 
+#ifdef USE_ENET
+#include "scumm/he/net/net_main.h"
+#endif
+
 #include "backends/audiocd/audiocd.h"
 
 #include "audio/mixer.h"
@@ -713,6 +717,15 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr)
 	_videoParams.number = 0;
 	_videoParams.wizResNum = 0;
 
+#ifdef USE_ENET
+	/* Online stuff for Backyard Football and Backyard Baseball 2001 */
+	_net = 0;
+	if (_game.id == GID_FOOTBALL || _game.id == GID_BASEBALL2001 || _game.id == GID_FOOTBALL2002 ||
+		_game.id == GID_MOONBASE) {
+		_net = new Net(this);
+	}
+#endif
+
 	VAR_NUM_SPRITE_GROUPS = 0xFF;
 	VAR_NUM_SPRITES = 0xFF;
 	VAR_NUM_PALETTES = 0xFF;
@@ -725,6 +738,11 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr)
 ScummEngine_v90he::~ScummEngine_v90he() {
 	delete _moviePlay;
 	delete _sprite;
+
+#ifdef USE_ENET
+	delete _net;
+#endif
+
 	if (_game.heversion >= 98) {
 		delete _logicHE;
 	}
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 66fca7a50ae..d80d02188bb 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -335,6 +335,14 @@ void ScummEngine_v90he::setupScummVars() {
 		VAR_SKIP_RESET_TALK_ACTOR = 125;
 	}
 	if (_game.heversion >= 99) {
+#ifdef USE_ENET
+		if (_game.id == GID_FOOTBALL || _game.id == GID_BASEBALL2001 || _game.id == GID_FOOTBALL2002 ||
+			_game.id == GID_MOONBASE) {
+			VAR_REMOTE_START_SCRIPT = 98;
+			VAR_NETWORK_AVAILABLE = 100;
+			VAR_NETWORK_RECEIVE_ARRAY_SCRIPT = 101;
+		}
+#endif
 		VAR_MAIN_SCRIPT = 127;
 		VAR_NUM_PALETTES = 130;
 		VAR_NUM_UNK = 131;


Commit: 07fc39e26bfe49701af39108228d1b0078443fb6
    https://github.com/scummvm/scummvm/commit/07fc39e26bfe49701af39108228d1b0078443fb6
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Use new network code in Football.

Changed paths:
    engines/scumm/he/logic/football.cpp


diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index 143ffe3cde0..20d7d698962 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -22,8 +22,28 @@
 #include "common/savefile.h"
 
 #include "scumm/he/intern_he.h"
+
+#ifdef USE_ENET
+#include "scumm/he/net/net_main.h"
+#include "scumm/he/net/net_defines.h"
+#endif
+
 #include "scumm/he/logic_he.h"
 
+// DirectPlay opcodes:
+#define OP_NET_REMOTE_START_SCRIPT	1492
+#define OP_NET_QUERY_PROVIDERS		1497
+#define OP_NET_CLOSE_PROVIDER		1500
+#define OP_NET_QUERY_SESSIONS		1501
+#define OP_NET_GET_SESSION_NAME		1502
+#define OP_NET_END_SESSION			1505
+#define OP_NET_WHO_SENT_THIS		1508
+#define OP_NET_REMOTE_SEND_ARRAY	1509
+#define OP_NET_INIT					1513
+#define OP_NET_WHO_AM_I				1510
+#define OP_NET_INIT_LAN_GAME		1515
+#define OP_NET_SET_PROVIDER_BY_NAME	1516
+
 namespace Scumm {
 
 /**
@@ -36,9 +56,15 @@ public:
 	LogicHEfootball(ScummEngine_v90he *vm) : LogicHE(vm) {}
 
 	int versionID() override;
+	int startOfFrame() override;
 	int32 dispatch(int op, int numArgs, int32 *args) override;
 
 protected:
+#ifdef USE_ENET
+	void netRemoteStartScript(int numArgs, int32 *args);
+	void netRemoteSendArray(int32 *args);
+#endif
+
 	int lineEquation3D(int32 *args);
 	virtual int translateWorldToScreen(int32 *args);
 	int fieldGoalScreenTranslation(int32 *args);
@@ -52,6 +78,14 @@ int LogicHEfootball::versionID() {
 	return 1;
 }
 
+int LogicHEfootball::startOfFrame() {
+#ifdef USE_ENET
+	_vm->_net->doNetworkOnceAFrame(0);
+#endif
+	return 0;
+}
+
+
 int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 	int res = 0;
 
@@ -89,11 +123,43 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 		res = getFromArray(args[0], args[1], args[2]);
 		break;
 
-	case 1492: case 1493: case 1494: case 1495: case 1496:
-	case 1497: case 1498: case 1499: case 1500: case 1501:
-	case 1502: case 1503: case 1504: case 1505: case 1506:
-	case 1507: case 1508: case 1509: case 1510: case 1511:
-	case 1512: case 1513: case 1514: case 1555:
+
+	case OP_NET_INIT:
+		// Initialize network system, this gets called at boot up and
+		// sets VAR_NETWORK_AVAILABLE (100).  We just return a 1 if
+		// ENet is compiled.
+#ifdef USE_ENET
+		res = 1;
+#endif
+		break;
+
+#ifdef USE_ENET
+	case OP_NET_REMOTE_START_SCRIPT:
+		netRemoteStartScript(numArgs, args);
+		break;
+
+	case OP_NET_END_SESSION:
+		res = _vm->_net->endSession();
+		break;
+
+	case OP_NET_WHO_SENT_THIS:
+		res = _vm->_net->whoSentThis();
+		break;
+
+	case OP_NET_REMOTE_SEND_ARRAY:
+		netRemoteSendArray(args);
+		break;
+
+	case OP_NET_WHO_AM_I:
+		res = _vm->_net->whoAmI();
+		break;
+#endif
+
+	case 1493: case 1494: case 1495: case 1496:
+	case 1498: case 1499: case 1501:
+	case 1502: case 1503: case 1504: case 1506:
+	case 1507: case 1511:
+	case 1512: case 1514: case 1555:
 		// DirectPlay-related
 		// 1513: initialize
 		// 1555: set fake lag
@@ -283,6 +349,36 @@ int LogicHEfootball::computeTwoCircleIntercepts(int32 *args) {
 	return 1;
 }
 
+#ifdef USE_ENET
+void LogicHEfootball::netRemoteStartScript(int numArgs, int32 *args) {
+	int priority = 0;
+	if (args[0] >= 15)
+		priority = PN_PRIORITY_HIGH;
+
+	int targetUserId;
+	if (_vm->_net->_isHost)
+		targetUserId = 2;
+	else
+		targetUserId = 1;
+
+	_vm->_net->remoteStartScript(PN_SENDTYPE_INDIVIDUAL, targetUserId, priority, numArgs - 3, &args[3]);
+}
+
+void LogicHEfootball::netRemoteSendArray(int32 *args) {
+	int priority = 0;
+	if (args[0] >= 10)
+		priority = PN_PRIORITY_HIGH;
+
+	int targetUserId;
+	if (_vm->_net->_isHost)
+		targetUserId = 2;
+	else
+		targetUserId = 1;
+
+	_vm->_net->remoteSendArray(PN_SENDTYPE_INDIVIDUAL, targetUserId, priority, args[3]);
+}
+#endif
+
 class LogicHEfootball2002 : public LogicHEfootball {
 public:
 	LogicHEfootball2002(ScummEngine_v90he *vm) : LogicHEfootball(vm) {
@@ -302,6 +398,11 @@ private:
 	int getPlaybookFiles(int32 *args);
 	int largestFreeBlock();
 
+#ifdef USE_ENET
+	int netGetSessionName(int index);
+	int netInitLanGame(int32 *args);
+#endif
+
 	float _var0;
 	float _var1;
 	float _var2;
@@ -342,14 +443,40 @@ int32 LogicHEfootball2002::dispatch(int op, int numArgs, int32 *args) {
 		// Get Computer Name (online play only)
 		break;
 
-	case 1515:
-		// Initialize Session (online play only)
+	// These cases are outside #ifdef USE_ENET intentionally
+	// to silence warnings:
+	case OP_NET_QUERY_PROVIDERS:
+#ifdef USE_ENET
+		res = _vm->_net->queryProviders();
+#endif
+		break;
+
+	case OP_NET_SET_PROVIDER_BY_NAME:
+#ifdef USE_ENET
+		res = _vm->_net->setProviderByName(args[0], args[1]);
+#endif
+		break;
+
+#ifdef USE_ENET
+	case OP_NET_CLOSE_PROVIDER:
+		res = _vm->_net->closeProvider();
 		break;
 
-	case 1516:
-		// Start auto LAN game (online play only)
+	case OP_NET_QUERY_SESSIONS:
+		// TODO: Replace the in-game session querying with
+		// our own GUI.
+		res = _vm->_net->querySessions();
 		break;
 
+	case OP_NET_GET_SESSION_NAME:
+		res = netGetSessionName(args[0]);
+		break;
+
+	case OP_NET_INIT_LAN_GAME:
+		res = netInitLanGame(args);
+		break;
+#endif
+
 	default:
 		res = LogicHEfootball::dispatch(op, numArgs, args);
 		break;
@@ -487,6 +614,44 @@ int LogicHEfootball2002::largestFreeBlock() {
 	return 1;
 }
 
+#ifdef USE_ENET
+int LogicHEfootball2002::netGetSessionName(int index) {
+	char name[MAX_PROVIDER_NAME];
+	_vm->_net->getSessionName(index - 1, name, sizeof(name));
+	return _vm->setupStringArrayFromString(name);
+}
+
+int LogicHEfootball2002::netInitLanGame(int32 *args) {
+	// Initialize Session
+	// Arg 0 is the provider name ("TCP/IP")
+	// Arg 1 is the session name
+	// Arg 2 is the host name.
+	// Arg 3 is a boolean determining we're hosting or joining a session.
+	char sessionName[MAX_SESSION_NAME];
+	_vm->getStringFromArray(args[1], sessionName, sizeof(sessionName));
+	char userName[MAX_PLAYER_NAME];
+	_vm->getStringFromArray(args[2], userName, sizeof(userName));
+
+	int res;
+
+	if (args[3] == 1) {
+		// Stop querying sessions if we haven't already
+		_vm->_net->stopQuerySessions();
+		// And host our new game.
+
+		res = _vm->_net->hostGame(sessionName, userName);
+	} else {
+		// TODO: Join via session name
+		res = _vm->_net->joinSession(0);
+		if (res)
+			_vm->_net->addUser(userName, userName);
+		_vm->_net->stopQuerySessions();
+	}
+
+	return res;
+}
+#endif
+
 LogicHE *makeLogicHEfootball(ScummEngine_v90he *vm) {
 	return new LogicHEfootball(vm);
 }


Commit: 70c94aced3d46b1a22cf099e135b9da3bec691b2
    https://github.com/scummvm/scummvm/commit/70c94aced3d46b1a22cf099e135b9da3bec691b2
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Command line host/join Moonbase games.

Normally, GameSpy Arcade (or a third-party app) would set these settings
into moonbase.ini for the game to read.  But I feel like a couple of
command line options would be a lot simpler.

Also, doubles as a quick way to host and join a game for testing.

Unfortunately this does not support the demo version.

Changed paths:
    base/commandLine.cpp
    engines/scumm/he/script_v80he.cpp


diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index 3e068579804..73b442ccb1c 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -213,6 +213,14 @@ static const char HELP_STRING4[] =
 #ifdef ENABLE_SCUMM
 	"  --tempo=NUM              Set music tempo (in percent, 50-200) for SCUMM games\n"
 	"                           (default: 100)\n"
+#endif
+#if defined(ENABLE_HE) && defined(USE_ENET)
+	"  --host-game              Host an online game for Moonbase Commander.\n"
+	"                           This method only works on the full version of the game,\n"
+	"                           Demo users can still host a game via the main menu.\n"
+	"  --join-game=IP[:PORT]    Join an online game for Moonbase Commander.\n"
+	"                           This method only works on the full version of the game,\n"
+	"                           Demo users can still join a game via the main menu.\n"
 #endif
 	"  --engine-speed=NUM       Set frame per second limit (0 - 100), 0 = no limit\n"
 	"                           (default: 60)\n"
@@ -336,6 +344,10 @@ void registerDefaults() {
 #ifdef ENABLE_SCUMM
 	ConfMan.registerDefault("tempo", 0);
 #endif
+#if defined(ENABLE_HE) && defined(USE_ENET)
+	ConfMan.registerDefault("host_game", false);
+	ConfMan.registerDefault("join_game", "null");
+#endif
 #if defined(ENABLE_SKY) || defined(ENABLE_QUEEN)
 	ConfMan.registerDefault("alt_intro", false);
 #endif
@@ -908,6 +920,13 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
 			END_OPTION
 #endif
 
+#if defined(ENABLE_HE) && defined(USE_ENET)
+			DO_LONG_OPTION_BOOL("host-game")
+			END_OPTION
+			DO_LONG_OPTION("join-game")
+			END_OPTION
+#endif
+
 #if defined(ENABLE_SCUMM) || defined(ENABLE_GROOVIE)
 			DO_LONG_OPTION_BOOL("demo-mode")
 			END_OPTION
diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp
index 714b18022d8..3dfd037c4b2 100644
--- a/engines/scumm/he/script_v80he.cpp
+++ b/engines/scumm/he/script_v80he.cpp
@@ -188,6 +188,8 @@ void ScummEngine_v80he::o80_readConfigFile() {
 	case SO_DWORD: // number
 		if (!strcmp((char *)option, "Benchmark"))
 			push(2);
+		else if (!strcmp((char *)option, "hostip"))
+			push(ConfMan.getBool("host_game"));
 		else
 			push(atoi(entry.c_str()));
 		break;


Commit: c16244a23aad29961b1f1e62b77239913a5cf9f6
    https://github.com/scummvm/scummvm/commit/c16244a23aad29961b1f1e62b77239913a5cf9f6
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Compile stable 1.3.17 source.

Changed paths:
  A backends/networking/enet/source/LICENSE
  A backends/networking/enet/source/README
  A backends/networking/enet/source/callbacks.cpp
  A backends/networking/enet/source/callbacks.h
  A backends/networking/enet/source/compress.cpp
  A backends/networking/enet/source/enet.h
  A backends/networking/enet/source/host.cpp
  A backends/networking/enet/source/list.cpp
  A backends/networking/enet/source/list.h
  A backends/networking/enet/source/packet.cpp
  A backends/networking/enet/source/peer.cpp
  A backends/networking/enet/source/protocol.cpp
  A backends/networking/enet/source/protocol.h
  A backends/networking/enet/source/time.h
  A backends/networking/enet/source/types.h
  A backends/networking/enet/source/unix.cpp
  A backends/networking/enet/source/unix.h
  A backends/networking/enet/source/utility.h
  A backends/networking/enet/source/win32.cpp
  A backends/networking/enet/source/win32.h
    backends/module.mk
    backends/networking/enet/enet.cpp
    backends/networking/enet/host.cpp
    backends/networking/enet/socket.cpp
    configure
    devtools/create_project/create_project.cpp
    devtools/create_project/msvc.cpp


diff --git a/backends/module.mk b/backends/module.mk
index 76f92728568..4a3aebff3e3 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -99,7 +99,23 @@ MODULE_OBJS += \
 	networking/sdl_net/uploadfileclienthandler.o
 endif
 
+# ENet networking source files.
 ifdef USE_ENET
+MODULE_OBJS += \
+	networking/enet/source/callbacks.o \
+	networking/enet/source/compress.o \
+	networking/enet/source/host.o \
+	networking/enet/source/list.o \
+	networking/enet/source/packet.o \
+	networking/enet/source/peer.o \
+	networking/enet/source/protocol.o
+ifdef WIN32
+MODULE_OBJS += \
+	networking/enet/source/win32.o
+else
+MODULE_OBJS += \
+	networking/enet/source/unix.o
+endif
 MODULE_OBJS += \
 	networking/enet/enet.o \
 	networking/enet/host.o \
diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index bdf3d06595e..381bcf75dec 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -21,7 +21,7 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <enet/enet.h>
+#include "backends/networking/enet/source/enet.h"
 #include "backends/networking/enet/enet.h"
 #include "backends/networking/enet/host.h"
 #include "backends/networking/enet/socket.h"
diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 2b85b359540..df77ed9426b 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -21,7 +21,7 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <enet/enet.h>
+#include "backends/networking/enet/source/enet.h"
 #include "backends/networking/enet/host.h"
 
 namespace Networking {
diff --git a/backends/networking/enet/socket.cpp b/backends/networking/enet/socket.cpp
index 6a462dabd34..cb8395b162d 100644
--- a/backends/networking/enet/socket.cpp
+++ b/backends/networking/enet/socket.cpp
@@ -21,7 +21,7 @@
 
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 
-#include <enet/enet.h>
+#include "backends/networking/enet/source/enet.h"
 #include "backends/networking/enet/socket.h"
 #include "common/debug.h"
 
diff --git a/backends/networking/enet/source/LICENSE b/backends/networking/enet/source/LICENSE
new file mode 100644
index 00000000000..6906f8eb0b0
--- /dev/null
+++ b/backends/networking/enet/source/LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2002-2020 Lee Salzman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/backends/networking/enet/source/README b/backends/networking/enet/source/README
new file mode 100644
index 00000000000..54b2d213040
--- /dev/null
+++ b/backends/networking/enet/source/README
@@ -0,0 +1,15 @@
+Please visit the ENet homepage at http://enet.bespin.org for installation
+and usage instructions.
+
+If you obtained this package from github, the quick description on how to build
+is:
+
+# Generate the build system.
+
+autoreconf -vfi
+
+# Compile and install the library.
+
+./configure && make && make install
+
+
diff --git a/backends/networking/enet/source/callbacks.cpp b/backends/networking/enet/source/callbacks.cpp
new file mode 100644
index 00000000000..dbc7108e4d0
--- /dev/null
+++ b/backends/networking/enet/source/callbacks.cpp
@@ -0,0 +1,53 @@
+/** 
+ @file callbacks.c
+ @brief ENet callback functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "enet.h"
+
+static ENetCallbacks callbacks = { malloc, free, abort };
+
+int
+enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits)
+{
+   if (version < ENET_VERSION_CREATE (1, 3, 0))
+     return -1;
+
+   if (inits -> malloc != NULL || inits -> free != NULL)
+   {
+      if (inits -> malloc == NULL || inits -> free == NULL)
+        return -1;
+
+      callbacks.malloc = inits -> malloc;
+      callbacks.free = inits -> free;
+   }
+      
+   if (inits -> no_memory != NULL)
+     callbacks.no_memory = inits -> no_memory;
+
+   return enet_initialize ();
+}
+
+ENetVersion
+enet_linked_version (void)
+{
+    return ENET_VERSION;
+}
+           
+void *
+enet_malloc (size_t size)
+{
+   void * memory = callbacks.malloc (size);
+
+   if (memory == NULL)
+     callbacks.no_memory ();
+
+   return memory;
+}
+
+void
+enet_free (void * memory)
+{
+   callbacks.free (memory);
+}
+
diff --git a/backends/networking/enet/source/callbacks.h b/backends/networking/enet/source/callbacks.h
new file mode 100644
index 00000000000..340a4a9896b
--- /dev/null
+++ b/backends/networking/enet/source/callbacks.h
@@ -0,0 +1,27 @@
+/** 
+ @file  callbacks.h
+ @brief ENet callbacks
+*/
+#ifndef __ENET_CALLBACKS_H__
+#define __ENET_CALLBACKS_H__
+
+#include <stdlib.h>
+
+typedef struct _ENetCallbacks
+{
+    void * (ENET_CALLBACK * malloc) (size_t size);
+    void (ENET_CALLBACK * free) (void * memory);
+    void (ENET_CALLBACK * no_memory) (void);
+} ENetCallbacks;
+
+/** @defgroup callbacks ENet internal callbacks
+    @{
+    @ingroup private
+*/
+extern void * enet_malloc (size_t);
+extern void   enet_free (void *);
+
+/** @} */
+
+#endif /* __ENET_CALLBACKS_H__ */
+
diff --git a/backends/networking/enet/source/compress.cpp b/backends/networking/enet/source/compress.cpp
new file mode 100644
index 00000000000..27cc2f5968c
--- /dev/null
+++ b/backends/networking/enet/source/compress.cpp
@@ -0,0 +1,654 @@
+/** 
+ @file compress.c
+ @brief An adaptive order-2 PPM range coder
+*/
+#define ENET_BUILDING_LIB 1
+#include <string.h>
+#include "enet.h"
+
+typedef struct _ENetSymbol
+{
+    /* binary indexed tree of symbols */
+    enet_uint8 value;
+    enet_uint8 count;
+    enet_uint16 under;
+    enet_uint16 left, right;
+
+    /* context defined by this symbol */
+    enet_uint16 symbols;
+    enet_uint16 escapes;
+    enet_uint16 total;
+    enet_uint16 parent; 
+} ENetSymbol;
+
+/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */
+enum
+{
+    ENET_RANGE_CODER_TOP    = 1<<24,
+    ENET_RANGE_CODER_BOTTOM = 1<<16,
+
+    ENET_CONTEXT_SYMBOL_DELTA = 3,
+    ENET_CONTEXT_SYMBOL_MINIMUM = 1,
+    ENET_CONTEXT_ESCAPE_MINIMUM = 1,
+
+    ENET_SUBCONTEXT_ORDER = 2,
+    ENET_SUBCONTEXT_SYMBOL_DELTA = 2,
+    ENET_SUBCONTEXT_ESCAPE_DELTA = 5
+};
+
+/* context exclusion roughly halves compression speed, so disable for now */
+#undef ENET_CONTEXT_EXCLUSION
+
+typedef struct _ENetRangeCoder
+{
+    /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */
+    ENetSymbol symbols[4096];
+} ENetRangeCoder;
+
+void *
+enet_range_coder_create (void)
+{
+    ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder));
+    if (rangeCoder == NULL)
+      return NULL;
+
+    return rangeCoder;
+}
+
+void
+enet_range_coder_destroy (void * context)
+{
+    ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
+    if (rangeCoder == NULL)
+      return;
+
+    enet_free (rangeCoder);
+}
+
+#define ENET_SYMBOL_CREATE(symbol, value_, count_) \
+{ \
+    symbol = & rangeCoder -> symbols [nextSymbol ++]; \
+    symbol -> value = value_; \
+    symbol -> count = count_; \
+    symbol -> under = count_; \
+    symbol -> left = 0; \
+    symbol -> right = 0; \
+    symbol -> symbols = 0; \
+    symbol -> escapes = 0; \
+    symbol -> total = 0; \
+    symbol -> parent = 0; \
+}
+
+#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \
+{ \
+    ENET_SYMBOL_CREATE (context, 0, 0); \
+    (context) -> escapes = escapes_; \
+    (context) -> total = escapes_ + 256*minimum; \
+    (context) -> symbols = 0; \
+}
+
+static enet_uint16
+enet_symbol_rescale (ENetSymbol * symbol)
+{
+    enet_uint16 total = 0;
+    for (;;)
+    {
+        symbol -> count -= symbol->count >> 1;
+        symbol -> under = symbol -> count;
+        if (symbol -> left)
+          symbol -> under += enet_symbol_rescale (symbol + symbol -> left);
+        total += symbol -> under;
+        if (! symbol -> right) break;
+        symbol += symbol -> right;
+    } 
+    return total;
+}
+
+#define ENET_CONTEXT_RESCALE(context, minimum) \
+{ \
+    (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \
+    (context) -> escapes -= (context) -> escapes >> 1; \
+    (context) -> total += (context) -> escapes + 256*minimum; \
+}
+
+#define ENET_RANGE_CODER_OUTPUT(value) \
+{ \
+    if (outData >= outEnd) \
+      return 0; \
+    * outData ++ = value; \
+}
+
+#define ENET_RANGE_CODER_ENCODE(under, count, total) \
+{ \
+    encodeRange /= (total); \
+    encodeLow += (under) * encodeRange; \
+    encodeRange *= (count); \
+    for (;;) \
+    { \
+        if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \
+        { \
+            if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
+            encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
+        } \
+        ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
+        encodeRange <<= 8; \
+        encodeLow <<= 8; \
+    } \
+}
+
+#define ENET_RANGE_CODER_FLUSH \
+{ \
+    while (encodeLow) \
+    { \
+        ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
+        encodeLow <<= 8; \
+    } \
+}
+
+#define ENET_RANGE_CODER_FREE_SYMBOLS \
+{ \
+    if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \
+    { \
+        nextSymbol = 0; \
+        ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \
+        predicted = 0; \
+        order = 0; \
+    } \
+}
+
+#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \
+{ \
+    under_ = value*minimum; \
+    count_ = minimum; \
+    if (! (context) -> symbols) \
+    { \
+        ENET_SYMBOL_CREATE (symbol_, value_, update); \
+        (context) -> symbols = symbol_ - (context); \
+    } \
+    else \
+    { \
+        ENetSymbol * node = (context) + (context) -> symbols; \
+        for (;;) \
+        { \
+            if (value_ < node -> value) \
+            { \
+                node -> under += update; \
+                if (node -> left) { node += node -> left; continue; } \
+                ENET_SYMBOL_CREATE (symbol_, value_, update); \
+                node -> left = symbol_ - node; \
+            } \
+            else \
+            if (value_ > node -> value) \
+            { \
+                under_ += node -> under; \
+                if (node -> right) { node += node -> right; continue; } \
+                ENET_SYMBOL_CREATE (symbol_, value_, update); \
+                node -> right = symbol_ - node; \
+            } \
+            else \
+            { \
+                count_ += node -> count; \
+                under_ += node -> under - node -> count; \
+                node -> under += update; \
+                node -> count += update; \
+                symbol_ = node; \
+            } \
+            break; \
+        } \
+    } \
+}
+
+#ifdef ENET_CONTEXT_EXCLUSION
+static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+#define ENET_CONTEXT_WALK(context, body) \
+{ \
+    const ENetSymbol * node = (context) + (context) -> symbols; \
+    const ENetSymbol * stack [256]; \
+    size_t stackSize = 0; \
+    while (node -> left) \
+    { \
+        stack [stackSize ++] = node; \
+        node += node -> left; \
+    } \
+    for (;;) \
+    { \
+        body; \
+        if (node -> right) \
+        { \
+            node += node -> right; \
+            while (node -> left) \
+            { \
+                stack [stackSize ++] = node; \
+                node += node -> left; \
+            } \
+        } \
+        else \
+        if (stackSize <= 0) \
+            break; \
+        else \
+            node = stack [-- stackSize]; \
+    } \
+}
+
+#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \
+ENET_CONTEXT_WALK(context, { \
+    if (node -> value != value_) \
+    { \
+        enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \
+        if (node -> value < value_) \
+          under -= parentCount; \
+        total -= parentCount; \
+    } \
+})
+#endif
+
+size_t
+enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit)
+{
+    ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
+    enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
+    const enet_uint8 * inData, * inEnd;
+    enet_uint32 encodeLow = 0, encodeRange = ~0;
+    ENetSymbol * root;
+    enet_uint16 predicted = 0;
+    size_t order = 0, nextSymbol = 0;
+
+    if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0)
+      return 0;
+
+    inData = (const enet_uint8 *) inBuffers -> data;
+    inEnd = & inData [inBuffers -> dataLength];
+    inBuffers ++;
+    inBufferCount --;
+
+    ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+    for (;;)
+    {
+        ENetSymbol * subcontext, * symbol;
+#ifdef ENET_CONTEXT_EXCLUSION
+        const ENetSymbol * childContext = & emptyContext;
+#endif
+        enet_uint8 value;
+        enet_uint16 count, under, * parent = & predicted, total;
+        if (inData >= inEnd)
+        {
+            if (inBufferCount <= 0)
+              break;
+            inData = (const enet_uint8 *) inBuffers -> data;
+            inEnd = & inData [inBuffers -> dataLength];
+            inBuffers ++;
+            inBufferCount --;
+        }
+        value = * inData ++;
+    
+        for (subcontext = & rangeCoder -> symbols [predicted]; 
+             subcontext != root; 
+#ifdef ENET_CONTEXT_EXCLUSION
+             childContext = subcontext, 
+#endif
+                subcontext = & rangeCoder -> symbols [subcontext -> parent])
+        {
+            ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
+            * parent = symbol - rangeCoder -> symbols;
+            parent = & symbol -> parent;
+            total = subcontext -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+            if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
+              ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0);
+#endif
+            if (count > 0)
+            {
+                ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total);
+            }
+            else
+            {
+                if (subcontext -> escapes > 0 && subcontext -> escapes < total) 
+                    ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total); 
+                subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
+                subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
+            }
+            subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
+            if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+              ENET_CONTEXT_RESCALE (subcontext, 0);
+            if (count > 0) goto nextInput;
+        }
+
+        ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM);
+        * parent = symbol - rangeCoder -> symbols;
+        parent = & symbol -> parent;
+        total = root -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+        if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
+          ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM); 
+#endif
+        ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total);
+        root -> total += ENET_CONTEXT_SYMBOL_DELTA; 
+        if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+          ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+    nextInput:
+        if (order >= ENET_SUBCONTEXT_ORDER) 
+          predicted = rangeCoder -> symbols [predicted].parent;
+        else 
+          order ++;
+        ENET_RANGE_CODER_FREE_SYMBOLS;
+    }
+
+    ENET_RANGE_CODER_FLUSH;
+
+    return (size_t) (outData - outStart);
+}
+
+#define ENET_RANGE_CODER_SEED \
+{ \
+    if (inData < inEnd) decodeCode |= * inData ++ << 24; \
+    if (inData < inEnd) decodeCode |= * inData ++ << 16; \
+    if (inData < inEnd) decodeCode |= * inData ++ << 8; \
+    if (inData < inEnd) decodeCode |= * inData ++; \
+}
+
+#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total)))
+
+#define ENET_RANGE_CODER_DECODE(under, count, total) \
+{ \
+    decodeLow += (under) * decodeRange; \
+    decodeRange *= (count); \
+    for (;;) \
+    { \
+        if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \
+        { \
+            if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
+            decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
+        } \
+        decodeCode <<= 8; \
+        if (inData < inEnd) \
+          decodeCode |= * inData ++; \
+        decodeRange <<= 8; \
+        decodeLow <<= 8; \
+    } \
+}
+
+#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \
+{ \
+    under_ = 0; \
+    count_ = minimum; \
+    if (! (context) -> symbols) \
+    { \
+        createRoot; \
+    } \
+    else \
+    { \
+        ENetSymbol * node = (context) + (context) -> symbols; \
+        for (;;) \
+        { \
+            enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \
+            visitNode; \
+            if (code >= after) \
+            { \
+                under_ += node -> under; \
+                if (node -> right) { node += node -> right; continue; } \
+                createRight; \
+            } \
+            else \
+            if (code < after - before) \
+            { \
+                node -> under += update; \
+                if (node -> left) { node += node -> left; continue; } \
+                createLeft; \
+            } \
+            else \
+            { \
+                value_ = node -> value; \
+                count_ += node -> count; \
+                under_ = after - before; \
+                node -> under += update; \
+                node -> count += update; \
+                symbol_ = node; \
+            } \
+            break; \
+        } \
+    } \
+}
+
+#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
+ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0)
+
+#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
+ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \
+    { \
+        value_ = code / minimum; \
+        under_ = code - code%minimum; \
+        ENET_SYMBOL_CREATE (symbol_, value_, update); \
+        (context) -> symbols = symbol_ - (context); \
+    }, \
+    exclude (node -> value, after, before), \
+    { \
+        value_ = node->value + 1 + (code - after)/minimum; \
+        under_ = code - (code - after)%minimum; \
+        ENET_SYMBOL_CREATE (symbol_, value_, update); \
+        node -> right = symbol_ - node; \
+    }, \
+    { \
+        value_ = node->value - 1 - (after - before - code - 1)/minimum; \
+        under_ = code - (after - before - code - 1)%minimum; \
+        ENET_SYMBOL_CREATE (symbol_, value_, update); \
+        node -> left = symbol_ - node; \
+    }) \
+
+#ifdef ENET_CONTEXT_EXCLUSION
+typedef struct _ENetExclude
+{
+    enet_uint8 value;
+    enet_uint16 under;
+} ENetExclude;
+
+#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \
+{ \
+    enet_uint16 under = 0; \
+    nextExclude = excludes; \
+    ENET_CONTEXT_WALK (context, { \
+        under += rangeCoder -> symbols [node -> parent].count + minimum; \
+        nextExclude -> value = node -> value; \
+        nextExclude -> under = under; \
+        nextExclude ++; \
+    }); \
+    total -= under; \
+}
+
+#define ENET_CONTEXT_EXCLUDED(value_, after, before) \
+{ \
+    size_t low = 0, high = nextExclude - excludes; \
+    for(;;) \
+    { \
+        size_t mid = (low + high) >> 1; \
+        const ENetExclude * exclude = & excludes [mid]; \
+        if (value_ < exclude -> value) \
+        { \
+            if (low + 1 < high) \
+            { \
+                high = mid; \
+                continue; \
+            } \
+            if (exclude > excludes) \
+              after -= exclude [-1].under; \
+        } \
+        else \
+        { \
+            if (value_ > exclude -> value) \
+            { \
+                if (low + 1 < high) \
+                { \
+                    low = mid; \
+                    continue; \
+                } \
+            } \
+            else \
+              before = 0; \
+            after -= exclude -> under; \
+        } \
+        break; \
+    } \
+}
+#endif
+
+#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before)
+
+size_t
+enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit)
+{
+    ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
+    enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
+    const enet_uint8 * inEnd = & inData [inLimit];
+    enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0;
+    ENetSymbol * root;
+    enet_uint16 predicted = 0;
+    size_t order = 0, nextSymbol = 0;
+#ifdef ENET_CONTEXT_EXCLUSION
+    ENetExclude excludes [256];
+    ENetExclude * nextExclude = excludes;
+#endif
+  
+    if (rangeCoder == NULL || inLimit <= 0)
+      return 0;
+
+    ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+    ENET_RANGE_CODER_SEED;
+
+    for (;;)
+    {
+        ENetSymbol * subcontext, * symbol, * patch;
+#ifdef ENET_CONTEXT_EXCLUSION
+        const ENetSymbol * childContext = & emptyContext;
+#endif
+        enet_uint8 value = 0;
+        enet_uint16 code, under, count, bottom, * parent = & predicted, total;
+
+        for (subcontext = & rangeCoder -> symbols [predicted];
+             subcontext != root;
+#ifdef ENET_CONTEXT_EXCLUSION
+             childContext = subcontext, 
+#endif
+                subcontext = & rangeCoder -> symbols [subcontext -> parent])
+        {
+            if (subcontext -> escapes <= 0)
+              continue;
+            total = subcontext -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+            if (childContext -> total > 0) 
+              ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0); 
+#endif
+            if (subcontext -> escapes >= total)
+              continue;
+            code = ENET_RANGE_CODER_READ (total);
+            if (code < subcontext -> escapes) 
+            {
+                ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total); 
+                continue;
+            }
+            code -= subcontext -> escapes;
+#ifdef ENET_CONTEXT_EXCLUSION
+            if (childContext -> total > 0)
+            {
+                ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED); 
+            }
+            else
+#endif
+            {
+                ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED); 
+            }
+            bottom = symbol - rangeCoder -> symbols;
+            ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total);
+            subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
+            if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+              ENET_CONTEXT_RESCALE (subcontext, 0);
+            goto patchContexts;
+        }
+
+        total = root -> total;
+#ifdef ENET_CONTEXT_EXCLUSION
+        if (childContext -> total > 0)
+          ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM);  
+#endif
+        code = ENET_RANGE_CODER_READ (total);
+        if (code < root -> escapes)
+        {
+            ENET_RANGE_CODER_DECODE (0, root -> escapes, total);
+            break;
+        }
+        code -= root -> escapes;
+#ifdef ENET_CONTEXT_EXCLUSION
+        if (childContext -> total > 0)
+        {
+            ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED); 
+        }
+        else
+#endif
+        {
+            ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED); 
+        }
+        bottom = symbol - rangeCoder -> symbols;
+        ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total);
+        root -> total += ENET_CONTEXT_SYMBOL_DELTA;
+        if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+          ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
+
+    patchContexts:
+        for (patch = & rangeCoder -> symbols [predicted];
+             patch != subcontext;
+             patch = & rangeCoder -> symbols [patch -> parent])
+        {
+            ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
+            * parent = symbol - rangeCoder -> symbols;
+            parent = & symbol -> parent;
+            if (count <= 0)
+            {
+                patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
+                patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
+            }
+            patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; 
+            if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
+              ENET_CONTEXT_RESCALE (patch, 0);
+        }
+        * parent = bottom;
+
+        ENET_RANGE_CODER_OUTPUT (value);
+
+        if (order >= ENET_SUBCONTEXT_ORDER)
+          predicted = rangeCoder -> symbols [predicted].parent;
+        else
+          order ++;
+        ENET_RANGE_CODER_FREE_SYMBOLS;
+    }
+                        
+    return (size_t) (outData - outStart);
+}
+
+/** @defgroup host ENet host functions
+    @{
+*/
+
+/** Sets the packet compressor the host should use to the default range coder.
+    @param host host to enable the range coder for
+    @returns 0 on success, < 0 on failure
+*/
+int
+enet_host_compress_with_range_coder (ENetHost * host)
+{
+    ENetCompressor compressor;
+    memset (& compressor, 0, sizeof (compressor));
+    compressor.context = enet_range_coder_create();
+    if (compressor.context == NULL)
+      return -1;
+    compressor.compress = enet_range_coder_compress;
+    compressor.decompress = enet_range_coder_decompress;
+    compressor.destroy = enet_range_coder_destroy;
+    enet_host_compress (host, & compressor);
+    return 0;
+}
+    
+/** @} */
+    
+     
diff --git a/backends/networking/enet/source/enet.h b/backends/networking/enet/source/enet.h
new file mode 100644
index 00000000000..642f77ba67a
--- /dev/null
+++ b/backends/networking/enet/source/enet.h
@@ -0,0 +1,612 @@
+/** 
+ @file  enet.h
+ @brief ENet public header file
+*/
+#ifndef __ENET_ENET_H__
+#define __ENET_ENET_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include "unix.h"
+#endif
+
+#include "types.h"
+#include "protocol.h"
+#include "list.h"
+#include "callbacks.h"
+
+#define ENET_VERSION_MAJOR 1
+#define ENET_VERSION_MINOR 3
+#define ENET_VERSION_PATCH 17
+#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch))
+#define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF)
+#define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF)
+#define ENET_VERSION_GET_PATCH(version) ((version)&0xFF)
+#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH)
+
+typedef enet_uint32 ENetVersion;
+
+struct _ENetHost;
+struct _ENetEvent;
+struct _ENetPacket;
+
+typedef enum _ENetSocketType
+{
+   ENET_SOCKET_TYPE_STREAM   = 1,
+   ENET_SOCKET_TYPE_DATAGRAM = 2
+} ENetSocketType;
+
+typedef enum _ENetSocketWait
+{
+   ENET_SOCKET_WAIT_NONE      = 0,
+   ENET_SOCKET_WAIT_SEND      = (1 << 0),
+   ENET_SOCKET_WAIT_RECEIVE   = (1 << 1),
+   ENET_SOCKET_WAIT_INTERRUPT = (1 << 2)
+} ENetSocketWait;
+
+typedef enum _ENetSocketOption
+{
+   ENET_SOCKOPT_NONBLOCK  = 1,
+   ENET_SOCKOPT_BROADCAST = 2,
+   ENET_SOCKOPT_RCVBUF    = 3,
+   ENET_SOCKOPT_SNDBUF    = 4,
+   ENET_SOCKOPT_REUSEADDR = 5,
+   ENET_SOCKOPT_RCVTIMEO  = 6,
+   ENET_SOCKOPT_SNDTIMEO  = 7,
+   ENET_SOCKOPT_ERROR     = 8,
+   ENET_SOCKOPT_NODELAY   = 9
+} ENetSocketOption;
+
+typedef enum _ENetSocketShutdown
+{
+    ENET_SOCKET_SHUTDOWN_READ       = 0,
+    ENET_SOCKET_SHUTDOWN_WRITE      = 1,
+    ENET_SOCKET_SHUTDOWN_READ_WRITE = 2
+} ENetSocketShutdown;
+
+#define ENET_HOST_ANY       0
+#define ENET_HOST_BROADCAST 0xFFFFFFFFU
+#define ENET_PORT_ANY       0
+
+/**
+ * Portable internet address structure. 
+ *
+ * The host must be specified in network byte-order, and the port must be in host 
+ * byte-order. The constant ENET_HOST_ANY may be used to specify the default 
+ * server host. The constant ENET_HOST_BROADCAST may be used to specify the
+ * broadcast address (255.255.255.255).  This makes sense for enet_host_connect,
+ * but not for enet_host_create.  Once a server responds to a broadcast, the
+ * address is updated from ENET_HOST_BROADCAST to the server's actual IP address.
+ */
+typedef struct _ENetAddress
+{
+   enet_uint32 host;
+   enet_uint16 port;
+} ENetAddress;
+
+/**
+ * Packet flag bit constants.
+ *
+ * The host must be specified in network byte-order, and the port must be in
+ * host byte-order. The constant ENET_HOST_ANY may be used to specify the
+ * default server host.
+ 
+   @sa ENetPacket
+*/
+typedef enum _ENetPacketFlag
+{
+   /** packet must be received by the target peer and resend attempts should be
+     * made until the packet is delivered */
+   ENET_PACKET_FLAG_RELIABLE    = (1 << 0),
+   /** packet will not be sequenced with other packets
+     * not supported for reliable packets
+     */
+   ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1),
+   /** packet will not allocate data, and user must supply it instead */
+   ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2),
+   /** packet will be fragmented using unreliable (instead of reliable) sends
+     * if it exceeds the MTU */
+   ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3),
+
+   /** whether the packet has been sent from all queues it has been entered into */
+   ENET_PACKET_FLAG_SENT = (1<<8)
+} ENetPacketFlag;
+
+typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *);
+
+/**
+ * ENet packet structure.
+ *
+ * An ENet data packet that may be sent to or received from a peer. The shown 
+ * fields should only be read and never modified. The data field contains the 
+ * allocated data for the packet. The dataLength fields specifies the length 
+ * of the allocated data.  The flags field is either 0 (specifying no flags), 
+ * or a bitwise-or of any combination of the following flags:
+ *
+ *    ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer
+ *    and resend attempts should be made until the packet is delivered
+ *
+ *    ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets 
+ *    (not supported for reliable packets)
+ *
+ *    ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead
+ *
+ *    ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable
+ *    (instead of reliable) sends if it exceeds the MTU
+ *
+ *    ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into
+   @sa ENetPacketFlag
+ */
+typedef struct _ENetPacket
+{
+   size_t                   referenceCount;  /**< internal use only */
+   enet_uint32              flags;           /**< bitwise-or of ENetPacketFlag constants */
+   enet_uint8 *             data;            /**< allocated data for packet */
+   size_t                   dataLength;      /**< length of data */
+   ENetPacketFreeCallback   freeCallback;    /**< function to be called when the packet is no longer in use */
+   void *                   userData;        /**< application private data, may be freely modified */
+} ENetPacket;
+
+typedef struct _ENetAcknowledgement
+{
+   ENetListNode acknowledgementList;
+   enet_uint32  sentTime;
+   ENetProtocol command;
+} ENetAcknowledgement;
+
+typedef struct _ENetOutgoingCommand
+{
+   ENetListNode outgoingCommandList;
+   enet_uint16  reliableSequenceNumber;
+   enet_uint16  unreliableSequenceNumber;
+   enet_uint32  sentTime;
+   enet_uint32  roundTripTimeout;
+   enet_uint32  roundTripTimeoutLimit;
+   enet_uint32  fragmentOffset;
+   enet_uint16  fragmentLength;
+   enet_uint16  sendAttempts;
+   ENetProtocol command;
+   ENetPacket * packet;
+} ENetOutgoingCommand;
+
+typedef struct _ENetIncomingCommand
+{  
+   ENetListNode     incomingCommandList;
+   enet_uint16      reliableSequenceNumber;
+   enet_uint16      unreliableSequenceNumber;
+   ENetProtocol     command;
+   enet_uint32      fragmentCount;
+   enet_uint32      fragmentsRemaining;
+   enet_uint32 *    fragments;
+   ENetPacket *     packet;
+} ENetIncomingCommand;
+
+typedef enum _ENetPeerState
+{
+   ENET_PEER_STATE_DISCONNECTED                = 0,
+   ENET_PEER_STATE_CONNECTING                  = 1,
+   ENET_PEER_STATE_ACKNOWLEDGING_CONNECT       = 2,
+   ENET_PEER_STATE_CONNECTION_PENDING          = 3,
+   ENET_PEER_STATE_CONNECTION_SUCCEEDED        = 4,
+   ENET_PEER_STATE_CONNECTED                   = 5,
+   ENET_PEER_STATE_DISCONNECT_LATER            = 6,
+   ENET_PEER_STATE_DISCONNECTING               = 7,
+   ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT    = 8,
+   ENET_PEER_STATE_ZOMBIE                      = 9 
+} ENetPeerState;
+
+#ifndef ENET_BUFFER_MAXIMUM
+#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
+#endif
+
+enum
+{
+   ENET_HOST_RECEIVE_BUFFER_SIZE          = 256 * 1024,
+   ENET_HOST_SEND_BUFFER_SIZE             = 256 * 1024,
+   ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL  = 1000,
+   ENET_HOST_DEFAULT_MTU                  = 1400,
+   ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE  = 32 * 1024 * 1024,
+   ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024,
+
+   ENET_PEER_DEFAULT_ROUND_TRIP_TIME      = 500,
+   ENET_PEER_DEFAULT_PACKET_THROTTLE      = 32,
+   ENET_PEER_PACKET_THROTTLE_SCALE        = 32,
+   ENET_PEER_PACKET_THROTTLE_COUNTER      = 7, 
+   ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2,
+   ENET_PEER_PACKET_THROTTLE_DECELERATION = 2,
+   ENET_PEER_PACKET_THROTTLE_INTERVAL     = 5000,
+   ENET_PEER_PACKET_LOSS_SCALE            = (1 << 16),
+   ENET_PEER_PACKET_LOSS_INTERVAL         = 10000,
+   ENET_PEER_WINDOW_SIZE_SCALE            = 64 * 1024,
+   ENET_PEER_TIMEOUT_LIMIT                = 32,
+   ENET_PEER_TIMEOUT_MINIMUM              = 5000,
+   ENET_PEER_TIMEOUT_MAXIMUM              = 30000,
+   ENET_PEER_PING_INTERVAL                = 500,
+   ENET_PEER_UNSEQUENCED_WINDOWS          = 64,
+   ENET_PEER_UNSEQUENCED_WINDOW_SIZE      = 1024,
+   ENET_PEER_FREE_UNSEQUENCED_WINDOWS     = 32,
+   ENET_PEER_RELIABLE_WINDOWS             = 16,
+   ENET_PEER_RELIABLE_WINDOW_SIZE         = 0x1000,
+   ENET_PEER_FREE_RELIABLE_WINDOWS        = 8
+};
+
+typedef struct _ENetChannel
+{
+   enet_uint16  outgoingReliableSequenceNumber;
+   enet_uint16  outgoingUnreliableSequenceNumber;
+   enet_uint16  usedReliableWindows;
+   enet_uint16  reliableWindows [ENET_PEER_RELIABLE_WINDOWS];
+   enet_uint16  incomingReliableSequenceNumber;
+   enet_uint16  incomingUnreliableSequenceNumber;
+   ENetList     incomingReliableCommands;
+   ENetList     incomingUnreliableCommands;
+} ENetChannel;
+
+typedef enum _ENetPeerFlag
+{
+   ENET_PEER_FLAG_NEEDS_DISPATCH = (1 << 0)
+} ENetPeerFlag;
+
+/**
+ * An ENet peer which data packets may be sent or received from. 
+ *
+ * No fields should be modified unless otherwise specified. 
+ */
+typedef struct _ENetPeer
+{ 
+   ENetListNode  dispatchList;
+   struct _ENetHost * host;
+   enet_uint16   outgoingPeerID;
+   enet_uint16   incomingPeerID;
+   enet_uint32   connectID;
+   enet_uint8    outgoingSessionID;
+   enet_uint8    incomingSessionID;
+   ENetAddress   address;            /**< Internet address of the peer */
+   void *        data;               /**< Application private data, may be freely modified */
+   ENetPeerState state;
+   ENetChannel * channels;
+   size_t        channelCount;       /**< Number of channels allocated for communication with peer */
+   enet_uint32   incomingBandwidth;  /**< Downstream bandwidth of the client in bytes/second */
+   enet_uint32   outgoingBandwidth;  /**< Upstream bandwidth of the client in bytes/second */
+   enet_uint32   incomingBandwidthThrottleEpoch;
+   enet_uint32   outgoingBandwidthThrottleEpoch;
+   enet_uint32   incomingDataTotal;
+   enet_uint32   outgoingDataTotal;
+   enet_uint32   lastSendTime;
+   enet_uint32   lastReceiveTime;
+   enet_uint32   nextTimeout;
+   enet_uint32   earliestTimeout;
+   enet_uint32   packetLossEpoch;
+   enet_uint32   packetsSent;
+   enet_uint32   packetsLost;
+   enet_uint32   packetLoss;          /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */
+   enet_uint32   packetLossVariance;
+   enet_uint32   packetThrottle;
+   enet_uint32   packetThrottleLimit;
+   enet_uint32   packetThrottleCounter;
+   enet_uint32   packetThrottleEpoch;
+   enet_uint32   packetThrottleAcceleration;
+   enet_uint32   packetThrottleDeceleration;
+   enet_uint32   packetThrottleInterval;
+   enet_uint32   pingInterval;
+   enet_uint32   timeoutLimit;
+   enet_uint32   timeoutMinimum;
+   enet_uint32   timeoutMaximum;
+   enet_uint32   lastRoundTripTime;
+   enet_uint32   lowestRoundTripTime;
+   enet_uint32   lastRoundTripTimeVariance;
+   enet_uint32   highestRoundTripTimeVariance;
+   enet_uint32   roundTripTime;            /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */
+   enet_uint32   roundTripTimeVariance;
+   enet_uint32   mtu;
+   enet_uint32   windowSize;
+   enet_uint32   reliableDataInTransit;
+   enet_uint16   outgoingReliableSequenceNumber;
+   ENetList      acknowledgements;
+   ENetList      sentReliableCommands;
+   ENetList      sentUnreliableCommands;
+   ENetList      outgoingCommands;
+   ENetList      dispatchedCommands;
+   enet_uint16   flags;
+   enet_uint16   reserved;
+   enet_uint16   incomingUnsequencedGroup;
+   enet_uint16   outgoingUnsequencedGroup;
+   enet_uint32   unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; 
+   enet_uint32   eventData;
+   size_t        totalWaitingData;
+} ENetPeer;
+
+/** An ENet packet compressor for compressing UDP packets before socket sends or receives.
+ */
+typedef struct _ENetCompressor
+{
+   /** Context data for the compressor. Must be non-NULL. */
+   void * context;
+   /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
+   size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit);
+   /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
+   size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit);
+   /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */
+   void (ENET_CALLBACK * destroy) (void * context);
+} ENetCompressor;
+
+/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */
+typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount);
+
+/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */
+typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event);
+ 
+/** An ENet host for communicating with peers.
+  *
+  * No fields should be modified unless otherwise stated.
+
+    @sa enet_host_create()
+    @sa enet_host_destroy()
+    @sa enet_host_connect()
+    @sa enet_host_service()
+    @sa enet_host_flush()
+    @sa enet_host_broadcast()
+    @sa enet_host_compress()
+    @sa enet_host_compress_with_range_coder()
+    @sa enet_host_channel_limit()
+    @sa enet_host_bandwidth_limit()
+    @sa enet_host_bandwidth_throttle()
+  */
+typedef struct _ENetHost
+{
+   ENetSocket           socket;
+   ENetAddress          address;                     /**< Internet address of the host */
+   enet_uint32          incomingBandwidth;           /**< downstream bandwidth of the host */
+   enet_uint32          outgoingBandwidth;           /**< upstream bandwidth of the host */
+   enet_uint32          bandwidthThrottleEpoch;
+   enet_uint32          mtu;
+   enet_uint32          randomSeed;
+   int                  recalculateBandwidthLimits;
+   ENetPeer *           peers;                       /**< array of peers allocated for this host */
+   size_t               peerCount;                   /**< number of peers allocated for this host */
+   size_t               channelLimit;                /**< maximum number of channels allowed for connected peers */
+   enet_uint32          serviceTime;
+   ENetList             dispatchQueue;
+   int                  continueSending;
+   size_t               packetSize;
+   enet_uint16          headerFlags;
+   ENetProtocol         commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS];
+   size_t               commandCount;
+   ENetBuffer           buffers [ENET_BUFFER_MAXIMUM];
+   size_t               bufferCount;
+   ENetChecksumCallback checksum;                    /**< callback the user can set to enable packet checksums for this host */
+   ENetCompressor       compressor;
+   enet_uint8           packetData [2][ENET_PROTOCOL_MAXIMUM_MTU];
+   ENetAddress          receivedAddress;
+   enet_uint8 *         receivedData;
+   size_t               receivedDataLength;
+   enet_uint32          totalSentData;               /**< total data sent, user should reset to 0 as needed to prevent overflow */
+   enet_uint32          totalSentPackets;            /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */
+   enet_uint32          totalReceivedData;           /**< total data received, user should reset to 0 as needed to prevent overflow */
+   enet_uint32          totalReceivedPackets;        /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */
+   ENetInterceptCallback intercept;                  /**< callback the user can set to intercept received raw UDP packets */
+   size_t               connectedPeers;
+   size_t               bandwidthLimitedPeers;
+   size_t               duplicatePeers;              /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */
+   size_t               maximumPacketSize;           /**< the maximum allowable packet size that may be sent or received on a peer */
+   size_t               maximumWaitingData;          /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */
+} ENetHost;
+
+/**
+ * An ENet event type, as specified in @ref ENetEvent.
+ */
+typedef enum _ENetEventType
+{
+   /** no event occurred within the specified time limit */
+   ENET_EVENT_TYPE_NONE       = 0,  
+
+   /** a connection request initiated by enet_host_connect has completed.  
+     * The peer field contains the peer which successfully connected. 
+     */
+   ENET_EVENT_TYPE_CONNECT    = 1,  
+
+   /** a peer has disconnected.  This event is generated on a successful 
+     * completion of a disconnect initiated by enet_peer_disconnect, if 
+     * a peer has timed out, or if a connection request intialized by 
+     * enet_host_connect has timed out.  The peer field contains the peer 
+     * which disconnected. The data field contains user supplied data 
+     * describing the disconnection, or 0, if none is available.
+     */
+   ENET_EVENT_TYPE_DISCONNECT = 2,  
+
+   /** a packet has been received from a peer.  The peer field specifies the
+     * peer which sent the packet.  The channelID field specifies the channel
+     * number upon which the packet was received.  The packet field contains
+     * the packet that was received; this packet must be destroyed with
+     * enet_packet_destroy after use.
+     */
+   ENET_EVENT_TYPE_RECEIVE    = 3
+} ENetEventType;
+
+/**
+ * An ENet event as returned by enet_host_service().
+   
+   @sa enet_host_service
+ */
+typedef struct _ENetEvent 
+{
+   ENetEventType        type;      /**< type of the event */
+   ENetPeer *           peer;      /**< peer that generated a connect, disconnect or receive event */
+   enet_uint8           channelID; /**< channel on the peer that generated the event, if appropriate */
+   enet_uint32          data;      /**< data associated with the event, if appropriate */
+   ENetPacket *         packet;    /**< packet associated with the event, if appropriate */
+} ENetEvent;
+
+/** @defgroup global ENet global functions
+    @{ 
+*/
+
+/** 
+  Initializes ENet globally.  Must be called prior to using any functions in
+  ENet.
+  @returns 0 on success, < 0 on failure
+*/
+ENET_API int enet_initialize (void);
+
+/** 
+  Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored.
+
+  @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use
+  @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults
+  @returns 0 on success, < 0 on failure
+*/
+ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits);
+
+/** 
+  Shuts down ENet globally.  Should be called when a program that has
+  initialized ENet exits.
+*/
+ENET_API void enet_deinitialize (void);
+
+/**
+  Gives the linked version of the ENet library.
+  @returns the version number 
+*/
+ENET_API ENetVersion enet_linked_version (void);
+
+/** @} */
+
+/** @defgroup private ENet private implementation functions */
+
+/**
+  Returns the wall-time in milliseconds.  Its initial value is unspecified
+  unless otherwise set.
+  */
+ENET_API enet_uint32 enet_time_get (void);
+/**
+  Sets the current wall-time in milliseconds.
+  */
+ENET_API void enet_time_set (enet_uint32);
+
+/** @defgroup socket ENet socket functions
+    @{
+*/
+ENET_API ENetSocket enet_socket_create (ENetSocketType);
+ENET_API int        enet_socket_bind (ENetSocket, const ENetAddress *);
+ENET_API int        enet_socket_get_address (ENetSocket, ENetAddress *);
+ENET_API int        enet_socket_listen (ENetSocket, int);
+ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *);
+ENET_API int        enet_socket_connect (ENetSocket, const ENetAddress *);
+ENET_API int        enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t);
+ENET_API int        enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t);
+ENET_API int        enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32);
+ENET_API int        enet_socket_set_option (ENetSocket, ENetSocketOption, int);
+ENET_API int        enet_socket_get_option (ENetSocket, ENetSocketOption, int *);
+ENET_API int        enet_socket_shutdown (ENetSocket, ENetSocketShutdown);
+ENET_API void       enet_socket_destroy (ENetSocket);
+ENET_API int        enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32);
+
+/** @} */
+
+/** @defgroup Address ENet address functions
+    @{
+*/
+
+/** Attempts to parse the printable form of the IP address in the parameter hostName
+    and sets the host field in the address parameter if successful.
+    @param address destination to store the parsed IP address
+    @param hostName IP address to parse
+    @retval 0 on success
+    @retval < 0 on failure
+    @returns the address of the given hostName in address on success
+*/
+ENET_API int enet_address_set_host_ip (ENetAddress * address, const char * hostName);
+
+/** Attempts to resolve the host named by the parameter hostName and sets
+    the host field in the address parameter if successful.
+    @param address destination to store resolved address
+    @param hostName host name to lookup
+    @retval 0 on success
+    @retval < 0 on failure
+    @returns the address of the given hostName in address on success
+*/
+ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName);
+
+/** Gives the printable form of the IP address specified in the address parameter.
+    @param address    address printed
+    @param hostName   destination for name, must not be NULL
+    @param nameLength maximum length of hostName.
+    @returns the null-terminated name of the host in hostName on success
+    @retval 0 on success
+    @retval < 0 on failure
+*/
+ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength);
+
+/** Attempts to do a reverse lookup of the host field in the address parameter.
+    @param address    address used for reverse lookup
+    @param hostName   destination for name, must not be NULL
+    @param nameLength maximum length of hostName.
+    @returns the null-terminated name of the host in hostName on success
+    @retval 0 on success
+    @retval < 0 on failure
+*/
+ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength);
+
+/** @} */
+
+ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32);
+ENET_API void         enet_packet_destroy (ENetPacket *);
+ENET_API int          enet_packet_resize  (ENetPacket *, size_t);
+ENET_API enet_uint32  enet_crc32 (const ENetBuffer *, size_t);
+                
+ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32);
+ENET_API void       enet_host_destroy (ENetHost *);
+ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32);
+ENET_API int        enet_host_check_events (ENetHost *, ENetEvent *);
+ENET_API int        enet_host_service (ENetHost *, ENetEvent *, enet_uint32);
+ENET_API void       enet_host_flush (ENetHost *);
+ENET_API void       enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *);
+ENET_API void       enet_host_compress (ENetHost *, const ENetCompressor *);
+ENET_API int        enet_host_compress_with_range_coder (ENetHost * host);
+ENET_API void       enet_host_channel_limit (ENetHost *, size_t);
+ENET_API void       enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
+extern   void       enet_host_bandwidth_throttle (ENetHost *);
+extern  enet_uint32 enet_host_random_seed (void);
+
+ENET_API int                 enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
+ENET_API ENetPacket *        enet_peer_receive (ENetPeer *, enet_uint8 * channelID);
+ENET_API void                enet_peer_ping (ENetPeer *);
+ENET_API void                enet_peer_ping_interval (ENetPeer *, enet_uint32);
+ENET_API void                enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
+ENET_API void                enet_peer_reset (ENetPeer *);
+ENET_API void                enet_peer_disconnect (ENetPeer *, enet_uint32);
+ENET_API void                enet_peer_disconnect_now (ENetPeer *, enet_uint32);
+ENET_API void                enet_peer_disconnect_later (ENetPeer *, enet_uint32);
+ENET_API void                enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
+extern int                   enet_peer_throttle (ENetPeer *, enet_uint32);
+extern void                  enet_peer_reset_queues (ENetPeer *);
+extern void                  enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *);
+extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
+extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32);
+extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16);
+extern void                  enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *, ENetIncomingCommand *);
+extern void                  enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *, ENetIncomingCommand *);
+extern void                  enet_peer_on_connect (ENetPeer *);
+extern void                  enet_peer_on_disconnect (ENetPeer *);
+
+ENET_API void * enet_range_coder_create (void);
+ENET_API void   enet_range_coder_destroy (void *);
+ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t);
+ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t);
+   
+extern size_t enet_protocol_command_size (enet_uint8);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ENET_ENET_H__ */
+
diff --git a/backends/networking/enet/source/host.cpp b/backends/networking/enet/source/host.cpp
new file mode 100644
index 00000000000..ee6bfd8c580
--- /dev/null
+++ b/backends/networking/enet/source/host.cpp
@@ -0,0 +1,491 @@
+/** 
+ @file host.c
+ @brief ENet host management functions
+*/
+#define ENET_BUILDING_LIB 1
+#include <string.h>
+#include "enet.h"
+
+/** @defgroup host ENet host functions
+    @{
+*/
+
+/** Creates a host for communicating to peers.  
+
+    @param address   the address at which other peers may connect to this host.  If NULL, then no peers may connect to the host.
+    @param peerCount the maximum number of peers that should be allocated for the host.
+    @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
+    @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+    @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+
+    @returns the host on success and NULL on failure
+
+    @remarks ENet will strategically drop packets on specific sides of a connection between hosts
+    to ensure the host's bandwidth is not overwhelmed.  The bandwidth parameters also determine
+    the window size of a connection which limits the amount of reliable packets that may be in transit
+    at any given time.
+*/
+ENetHost *
+enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+    ENetHost * host;
+    ENetPeer * currentPeer;
+
+    if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
+      return NULL;
+
+    host = (ENetHost *) enet_malloc (sizeof (ENetHost));
+    if (host == NULL)
+      return NULL;
+    memset (host, 0, sizeof (ENetHost));
+
+    host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));
+    if (host -> peers == NULL)
+    {
+       enet_free (host);
+
+       return NULL;
+    }
+    memset (host -> peers, 0, peerCount * sizeof (ENetPeer));
+
+    host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM);
+    if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0))
+    {
+       if (host -> socket != ENET_SOCKET_NULL)
+         enet_socket_destroy (host -> socket);
+
+       enet_free (host -> peers);
+       enet_free (host);
+
+       return NULL;
+    }
+
+    enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1);
+    enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1);
+    enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
+    enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
+
+    if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0)   
+      host -> address = * address;
+
+    if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+    else
+    if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+      channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+
+    host -> randomSeed = (enet_uint32) (size_t) host;
+    host -> randomSeed += enet_host_random_seed ();
+    host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16);
+    host -> channelLimit = channelLimit;
+    host -> incomingBandwidth = incomingBandwidth;
+    host -> outgoingBandwidth = outgoingBandwidth;
+    host -> bandwidthThrottleEpoch = 0;
+    host -> recalculateBandwidthLimits = 0;
+    host -> mtu = ENET_HOST_DEFAULT_MTU;
+    host -> peerCount = peerCount;
+    host -> commandCount = 0;
+    host -> bufferCount = 0;
+    host -> checksum = NULL;
+    host -> receivedAddress.host = ENET_HOST_ANY;
+    host -> receivedAddress.port = 0;
+    host -> receivedData = NULL;
+    host -> receivedDataLength = 0;
+     
+    host -> totalSentData = 0;
+    host -> totalSentPackets = 0;
+    host -> totalReceivedData = 0;
+    host -> totalReceivedPackets = 0;
+
+    host -> connectedPeers = 0;
+    host -> bandwidthLimitedPeers = 0;
+    host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID;
+    host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE;
+    host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA;
+
+    host -> compressor.context = NULL;
+    host -> compressor.compress = NULL;
+    host -> compressor.decompress = NULL;
+    host -> compressor.destroy = NULL;
+
+    host -> intercept = NULL;
+
+    enet_list_clear (& host -> dispatchQueue);
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       currentPeer -> host = host;
+       currentPeer -> incomingPeerID = currentPeer - host -> peers;
+       currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF;
+       currentPeer -> data = NULL;
+
+       enet_list_clear (& currentPeer -> acknowledgements);
+       enet_list_clear (& currentPeer -> sentReliableCommands);
+       enet_list_clear (& currentPeer -> sentUnreliableCommands);
+       enet_list_clear (& currentPeer -> outgoingCommands);
+       enet_list_clear (& currentPeer -> dispatchedCommands);
+
+       enet_peer_reset (currentPeer);
+    }
+
+    return host;
+}
+
+/** Destroys the host and all resources associated with it.
+    @param host pointer to the host to destroy
+*/
+void
+enet_host_destroy (ENetHost * host)
+{
+    ENetPeer * currentPeer;
+
+    if (host == NULL)
+      return;
+
+    enet_socket_destroy (host -> socket);
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       enet_peer_reset (currentPeer);
+    }
+
+    if (host -> compressor.context != NULL && host -> compressor.destroy)
+      (* host -> compressor.destroy) (host -> compressor.context);
+
+    enet_free (host -> peers);
+    enet_free (host);
+}
+
+/** Initiates a connection to a foreign host.
+    @param host host seeking the connection
+    @param address destination for the connection
+    @param channelCount number of channels to allocate
+    @param data user data supplied to the receiving host 
+    @returns a peer representing the foreign host on success, NULL on failure
+    @remarks The peer returned will have not completed the connection until enet_host_service()
+    notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
+*/
+ENetPeer *
+enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data)
+{
+    ENetPeer * currentPeer;
+    ENetChannel * channel;
+    ENetProtocol command;
+
+    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+      channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+    else
+    if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+         break;
+    }
+
+    if (currentPeer >= & host -> peers [host -> peerCount])
+      return NULL;
+
+    currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+    if (currentPeer -> channels == NULL)
+      return NULL;
+    currentPeer -> channelCount = channelCount;
+    currentPeer -> state = ENET_PEER_STATE_CONNECTING;
+    currentPeer -> address = * address;
+    currentPeer -> connectID = ++ host -> randomSeed;
+
+    if (host -> outgoingBandwidth == 0)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      currentPeer -> windowSize = (host -> outgoingBandwidth /
+                                    ENET_PEER_WINDOW_SIZE_SCALE) * 
+                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+         
+    for (channel = currentPeer -> channels;
+         channel < & currentPeer -> channels [channelCount];
+         ++ channel)
+    {
+        channel -> outgoingReliableSequenceNumber = 0;
+        channel -> outgoingUnreliableSequenceNumber = 0;
+        channel -> incomingReliableSequenceNumber = 0;
+        channel -> incomingUnreliableSequenceNumber = 0;
+
+        enet_list_clear (& channel -> incomingReliableCommands);
+        enet_list_clear (& channel -> incomingUnreliableCommands);
+
+        channel -> usedReliableWindows = 0;
+        memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
+    }
+        
+    command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+    command.header.channelID = 0xFF;
+    command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
+    command.connect.incomingSessionID = currentPeer -> incomingSessionID;
+    command.connect.outgoingSessionID = currentPeer -> outgoingSessionID;
+    command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu);
+    command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
+    command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
+    command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+    command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+    command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
+    command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
+    command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
+    command.connect.connectID = currentPeer -> connectID;
+    command.connect.data = ENET_HOST_TO_NET_32 (data);
+ 
+    enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
+
+    return currentPeer;
+}
+
+/** Queues a packet to be sent to all peers associated with the host.
+    @param host host on which to broadcast the packet
+    @param channelID channel on which to broadcast
+    @param packet packet to broadcast
+*/
+void
+enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
+{
+    ENetPeer * currentPeer;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+       if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
+         continue;
+
+       enet_peer_send (currentPeer, channelID, packet);
+    }
+
+    if (packet -> referenceCount == 0)
+      enet_packet_destroy (packet);
+}
+
+/** Sets the packet compressor the host should use to compress and decompress packets.
+    @param host host to enable or disable compression for
+    @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled
+*/
+void
+enet_host_compress (ENetHost * host, const ENetCompressor * compressor)
+{
+    if (host -> compressor.context != NULL && host -> compressor.destroy)
+      (* host -> compressor.destroy) (host -> compressor.context);
+
+    if (compressor)
+      host -> compressor = * compressor;
+    else
+      host -> compressor.context = NULL;
+}
+
+/** Limits the maximum allowed channels of future incoming connections.
+    @param host host to limit
+    @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
+*/
+void
+enet_host_channel_limit (ENetHost * host, size_t channelLimit)
+{
+    if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
+    else
+    if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
+      channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
+
+    host -> channelLimit = channelLimit;
+}
+
+
+/** Adjusts the bandwidth limits of a host.
+    @param host host to adjust
+    @param incomingBandwidth new incoming bandwidth
+    @param outgoingBandwidth new outgoing bandwidth
+    @remarks the incoming and outgoing bandwidth parameters are identical in function to those
+    specified in enet_host_create().
+*/
+void
+enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
+{
+    host -> incomingBandwidth = incomingBandwidth;
+    host -> outgoingBandwidth = outgoingBandwidth;
+    host -> recalculateBandwidthLimits = 1;
+}
+
+void
+enet_host_bandwidth_throttle (ENetHost * host)
+{
+    enet_uint32 timeCurrent = enet_time_get (),
+           elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
+           peersRemaining = (enet_uint32) host -> connectedPeers,
+           dataTotal = ~0,
+           bandwidth = ~0,
+           throttle = 0,
+           bandwidthLimit = 0;
+    int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0;
+    ENetPeer * peer;
+    ENetProtocol command;
+
+    if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+      return;
+
+    host -> bandwidthThrottleEpoch = timeCurrent;
+
+    if (peersRemaining == 0)
+      return;
+
+    if (host -> outgoingBandwidth != 0)
+    {
+        dataTotal = 0;
+        bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
+
+        for (peer = host -> peers;
+             peer < & host -> peers [host -> peerCount];
+            ++ peer)
+        {
+            if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+              continue;
+
+            dataTotal += peer -> outgoingDataTotal;
+        }
+    }
+
+    while (peersRemaining > 0 && needsAdjustment != 0)
+    {
+        needsAdjustment = 0;
+        
+        if (dataTotal <= bandwidth)
+          throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
+        else
+          throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
+
+        for (peer = host -> peers;
+             peer < & host -> peers [host -> peerCount];
+             ++ peer)
+        {
+            enet_uint32 peerBandwidth;
+            
+            if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
+                peer -> incomingBandwidth == 0 ||
+                peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+              continue;
+
+            peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
+            if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
+              continue;
+
+            peer -> packetThrottleLimit = (peerBandwidth * 
+                                            ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
+            
+            if (peer -> packetThrottleLimit == 0)
+              peer -> packetThrottleLimit = 1;
+            
+            if (peer -> packetThrottle > peer -> packetThrottleLimit)
+              peer -> packetThrottle = peer -> packetThrottleLimit;
+
+            peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
+
+            peer -> incomingDataTotal = 0;
+            peer -> outgoingDataTotal = 0;
+
+            needsAdjustment = 1;
+            -- peersRemaining;
+            bandwidth -= peerBandwidth;
+            dataTotal -= peerBandwidth;
+        }
+    }
+
+    if (peersRemaining > 0)
+    {
+        if (dataTotal <= bandwidth)
+          throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
+        else
+          throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
+
+        for (peer = host -> peers;
+             peer < & host -> peers [host -> peerCount];
+             ++ peer)
+        {
+            if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
+                peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
+              continue;
+
+            peer -> packetThrottleLimit = throttle;
+
+            if (peer -> packetThrottle > peer -> packetThrottleLimit)
+              peer -> packetThrottle = peer -> packetThrottleLimit;
+
+            peer -> incomingDataTotal = 0;
+            peer -> outgoingDataTotal = 0;
+        }
+    }
+
+    if (host -> recalculateBandwidthLimits)
+    {
+       host -> recalculateBandwidthLimits = 0;
+
+       peersRemaining = (enet_uint32) host -> connectedPeers;
+       bandwidth = host -> incomingBandwidth;
+       needsAdjustment = 1;
+
+       if (bandwidth == 0)
+         bandwidthLimit = 0;
+       else
+       while (peersRemaining > 0 && needsAdjustment != 0)
+       {
+           needsAdjustment = 0;
+           bandwidthLimit = bandwidth / peersRemaining;
+
+           for (peer = host -> peers;
+                peer < & host -> peers [host -> peerCount];
+                ++ peer)
+           {
+               if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
+                   peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+                 continue;
+
+               if (peer -> outgoingBandwidth > 0 &&
+                   peer -> outgoingBandwidth >= bandwidthLimit)
+                 continue;
+
+               peer -> incomingBandwidthThrottleEpoch = timeCurrent;
+ 
+               needsAdjustment = 1;
+               -- peersRemaining;
+               bandwidth -= peer -> outgoingBandwidth;
+           }
+       }
+
+       for (peer = host -> peers;
+            peer < & host -> peers [host -> peerCount];
+            ++ peer)
+       {
+           if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+             continue;
+
+           command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+           command.header.channelID = 0xFF;
+           command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+
+           if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
+             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
+           else
+             command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
+
+           enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+       } 
+    }
+}
+    
+/** @} */
diff --git a/backends/networking/enet/source/list.cpp b/backends/networking/enet/source/list.cpp
new file mode 100644
index 00000000000..c52837adbf5
--- /dev/null
+++ b/backends/networking/enet/source/list.cpp
@@ -0,0 +1,75 @@
+/** 
+ @file list.c
+ @brief ENet linked list functions
+*/
+#define ENET_BUILDING_LIB 1
+#include "enet.h"
+
+/** 
+    @defgroup list ENet linked list utility functions
+    @ingroup private
+    @{
+*/
+void
+enet_list_clear (ENetList * list)
+{
+   list -> sentinel.next = & list -> sentinel;
+   list -> sentinel.previous = & list -> sentinel;
+}
+
+ENetListIterator
+enet_list_insert (ENetListIterator position, void * data)
+{
+   ENetListIterator result = (ENetListIterator) data;
+
+   result -> previous = position -> previous;
+   result -> next = position;
+
+   result -> previous -> next = result;
+   position -> previous = result;
+
+   return result;
+}
+
+void *
+enet_list_remove (ENetListIterator position)
+{
+   position -> previous -> next = position -> next;
+   position -> next -> previous = position -> previous;
+
+   return position;
+}
+
+ENetListIterator
+enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast)
+{
+   ENetListIterator first = (ENetListIterator) dataFirst,
+                    last = (ENetListIterator) dataLast;
+
+   first -> previous -> next = last -> next;
+   last -> next -> previous = first -> previous;
+
+   first -> previous = position -> previous;
+   last -> next = position;
+
+   first -> previous -> next = first;
+   position -> previous = last;
+    
+   return first;
+}
+
+size_t
+enet_list_size (ENetList * list)
+{
+   size_t size = 0;
+   ENetListIterator position;
+
+   for (position = enet_list_begin (list);
+        position != enet_list_end (list);
+        position = enet_list_next (position))
+     ++ size;
+   
+   return size;
+}
+
+/** @} */
diff --git a/backends/networking/enet/source/list.h b/backends/networking/enet/source/list.h
new file mode 100644
index 00000000000..d7b2600848f
--- /dev/null
+++ b/backends/networking/enet/source/list.h
@@ -0,0 +1,43 @@
+/** 
+ @file  list.h
+ @brief ENet list management 
+*/
+#ifndef __ENET_LIST_H__
+#define __ENET_LIST_H__
+
+#include <stdlib.h>
+
+typedef struct _ENetListNode
+{
+   struct _ENetListNode * next;
+   struct _ENetListNode * previous;
+} ENetListNode;
+
+typedef ENetListNode * ENetListIterator;
+
+typedef struct _ENetList
+{
+   ENetListNode sentinel;
+} ENetList;
+
+extern void enet_list_clear (ENetList *);
+
+extern ENetListIterator enet_list_insert (ENetListIterator, void *);
+extern void * enet_list_remove (ENetListIterator);
+extern ENetListIterator enet_list_move (ENetListIterator, void *, void *);
+
+extern size_t enet_list_size (ENetList *);
+
+#define enet_list_begin(list) ((list) -> sentinel.next)
+#define enet_list_end(list) (& (list) -> sentinel)
+
+#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list))
+
+#define enet_list_next(iterator) ((iterator) -> next)
+#define enet_list_previous(iterator) ((iterator) -> previous)
+
+#define enet_list_front(list) ((void *) (list) -> sentinel.next)
+#define enet_list_back(list) ((void *) (list) -> sentinel.previous)
+
+#endif /* __ENET_LIST_H__ */
+
diff --git a/backends/networking/enet/source/packet.cpp b/backends/networking/enet/source/packet.cpp
new file mode 100644
index 00000000000..f107f59c663
--- /dev/null
+++ b/backends/networking/enet/source/packet.cpp
@@ -0,0 +1,165 @@
+/** 
+ @file  packet.c
+ @brief ENet packet management functions
+*/
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "enet.h"
+
+/** @defgroup Packet ENet packet functions 
+    @{ 
+*/
+
+/** Creates a packet that may be sent to a peer.
+    @param data         initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL.
+    @param dataLength   size of the data allocated for this packet
+    @param flags        flags for this packet as described for the ENetPacket structure.
+    @returns the packet on success, NULL on failure
+*/
+ENetPacket *
+enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags)
+{
+    ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket));
+    if (packet == NULL)
+      return NULL;
+
+    if (flags & ENET_PACKET_FLAG_NO_ALLOCATE)
+      packet -> data = const_cast<enet_uint8 *>(static_cast<const enet_uint8 *>(data));
+    else
+    if (dataLength <= 0)
+      packet -> data = NULL;
+    else
+    {
+       packet -> data = (enet_uint8 *) enet_malloc (dataLength);
+       if (packet -> data == NULL)
+       {
+          enet_free (packet);
+          return NULL;
+       }
+
+       if (data != NULL)
+         memcpy (packet -> data, data, dataLength);
+    }
+
+    packet -> referenceCount = 0;
+    packet -> flags = flags;
+    packet -> dataLength = dataLength;
+    packet -> freeCallback = NULL;
+    packet -> userData = NULL;
+
+    return packet;
+}
+
+/** Destroys the packet and deallocates its data.
+    @param packet packet to be destroyed
+*/
+void
+enet_packet_destroy (ENetPacket * packet)
+{
+    if (packet == NULL)
+      return;
+
+    if (packet -> freeCallback != NULL)
+      (* packet -> freeCallback) (packet);
+    if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) &&
+        packet -> data != NULL)
+      enet_free (packet -> data);
+    enet_free (packet);
+}
+
+/** Attempts to resize the data in the packet to length specified in the 
+    dataLength parameter 
+    @param packet packet to resize
+    @param dataLength new size for the packet data
+    @returns 0 on success, < 0 on failure
+*/
+int
+enet_packet_resize (ENetPacket * packet, size_t dataLength)
+{
+    enet_uint8 * newData;
+   
+    if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE))
+    {
+       packet -> dataLength = dataLength;
+
+       return 0;
+    }
+
+    newData = (enet_uint8 *) enet_malloc (dataLength);
+    if (newData == NULL)
+      return -1;
+
+    memcpy (newData, packet -> data, packet -> dataLength);
+    enet_free (packet -> data);
+    
+    packet -> data = newData;
+    packet -> dataLength = dataLength;
+
+    return 0;
+}
+
+static int initializedCRC32 = 0;
+static enet_uint32 crcTable [256];
+
+static enet_uint32 
+reflect_crc (int val, int bits)
+{
+    int result = 0, bit;
+
+    for (bit = 0; bit < bits; bit ++)
+    {
+        if(val & 1) result |= 1 << (bits - 1 - bit); 
+        val >>= 1;
+    }
+
+    return result;
+}
+
+static void 
+initialize_crc32 (void)
+{
+    int byte;
+
+    for (byte = 0; byte < 256; ++ byte)
+    {
+        enet_uint32 crc = reflect_crc (byte, 8) << 24;
+        int offset;
+
+        for(offset = 0; offset < 8; ++ offset)
+        {
+            if (crc & 0x80000000)
+                crc = (crc << 1) ^ 0x04c11db7;
+            else
+                crc <<= 1;
+        }
+
+        crcTable [byte] = reflect_crc (crc, 32);
+    }
+
+    initializedCRC32 = 1;
+}
+    
+enet_uint32
+enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
+{
+    enet_uint32 crc = 0xFFFFFFFF;
+    
+    if (! initializedCRC32) initialize_crc32 ();
+
+    while (bufferCount -- > 0)
+    {
+        const enet_uint8 * data = (const enet_uint8 *) buffers -> data,
+                         * dataEnd = & data [buffers -> dataLength];
+
+        while (data < dataEnd)
+        {
+            crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];        
+        }
+
+        ++ buffers;
+    }
+
+    return ENET_HOST_TO_NET_32 (~ crc);
+}
+
+/** @} */
diff --git a/backends/networking/enet/source/peer.cpp b/backends/networking/enet/source/peer.cpp
new file mode 100644
index 00000000000..dd1106e20a9
--- /dev/null
+++ b/backends/networking/enet/source/peer.cpp
@@ -0,0 +1,1015 @@
+/** 
+ @file  peer.c
+ @brief ENet peer management functions
+*/
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "enet.h"
+
+/** @defgroup peer ENet peer functions 
+    @{
+*/
+
+/** Configures throttle parameter for a peer.
+
+    Unreliable packets are dropped by ENet in response to the varying conditions
+    of the Internet connection to the peer.  The throttle represents a probability
+    that an unreliable packet should not be dropped and thus sent by ENet to the peer.
+    The lowest mean round trip time from the sending of a reliable packet to the
+    receipt of its acknowledgement is measured over an amount of time specified by
+    the interval parameter in milliseconds.  If a measured round trip time happens to
+    be significantly less than the mean round trip time measured over the interval, 
+    then the throttle probability is increased to allow more traffic by an amount
+    specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE
+    constant.  If a measured round trip time happens to be significantly greater than
+    the mean round trip time measured over the interval, then the throttle probability
+    is decreased to limit traffic by an amount specified in the deceleration parameter, which
+    is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant.  When the throttle has
+    a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by 
+    ENet, and so 100% of all unreliable packets will be sent.  When the throttle has a
+    value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable
+    packets will be sent.  Intermediate values for the throttle represent intermediate
+    probabilities between 0% and 100% of unreliable packets being sent.  The bandwidth
+    limits of the local and foreign hosts are taken into account to determine a 
+    sensible limit for the throttle probability above which it should not raise even in
+    the best of conditions.
+
+    @param peer peer to configure 
+    @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL.
+    @param acceleration rate at which to increase the throttle probability as mean RTT declines
+    @param deceleration rate at which to decrease the throttle probability as mean RTT increases
+*/
+void
+enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration)
+{
+    ENetProtocol command;
+
+    peer -> packetThrottleInterval = interval;
+    peer -> packetThrottleAcceleration = acceleration;
+    peer -> packetThrottleDeceleration = deceleration;
+
+    command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+    command.header.channelID = 0xFF;
+
+    command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval);
+    command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration);
+    command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration);
+
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+int
+enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
+{
+    if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance)
+    {
+        peer -> packetThrottle = peer -> packetThrottleLimit;
+    }
+    else
+    if (rtt <= peer -> lastRoundTripTime)
+    {
+        peer -> packetThrottle += peer -> packetThrottleAcceleration;
+
+        if (peer -> packetThrottle > peer -> packetThrottleLimit)
+          peer -> packetThrottle = peer -> packetThrottleLimit;
+
+        return 1;
+    }
+    else
+    if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance)
+    {
+        if (peer -> packetThrottle > peer -> packetThrottleDeceleration)
+          peer -> packetThrottle -= peer -> packetThrottleDeceleration;
+        else
+          peer -> packetThrottle = 0;
+
+        return -1;
+    }
+
+    return 0;
+}
+
+/** Queues a packet to be sent.
+    @param peer destination for the packet
+    @param channelID channel on which to send
+    @param packet packet to send
+    @retval 0 on success
+    @retval < 0 on failure
+*/
+int
+enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
+{
+   ENetChannel * channel = & peer -> channels [channelID];
+   ENetProtocol command;
+   size_t fragmentLength;
+
+   if (peer -> state != ENET_PEER_STATE_CONNECTED ||
+       channelID >= peer -> channelCount ||
+       packet -> dataLength > peer -> host -> maximumPacketSize)
+     return -1;
+
+   fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
+   if (peer -> host -> checksum != NULL)
+     fragmentLength -= sizeof(enet_uint32);
+
+   if (packet -> dataLength > fragmentLength)
+   {
+      enet_uint32 fragmentCount = (packet -> dataLength + fragmentLength - 1) / fragmentLength,
+             fragmentNumber,
+             fragmentOffset;
+      enet_uint8 commandNumber;
+      enet_uint16 startSequenceNumber; 
+      ENetList fragments;
+      ENetOutgoingCommand * fragment;
+
+      if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT)
+        return -1;
+
+      if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT &&
+          channel -> outgoingUnreliableSequenceNumber < 0xFFFF)
+      {
+         commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT;
+         startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1);
+      }
+      else
+      {
+         commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+         startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1);
+      }
+        
+      enet_list_clear (& fragments);
+
+      for (fragmentNumber = 0,
+             fragmentOffset = 0;
+           fragmentOffset < packet -> dataLength;
+           ++ fragmentNumber,
+             fragmentOffset += fragmentLength)
+      {
+         if (packet -> dataLength - fragmentOffset < fragmentLength)
+           fragmentLength = packet -> dataLength - fragmentOffset;
+
+         fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
+         if (fragment == NULL)
+         {
+            while (! enet_list_empty (& fragments))
+            {
+               fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments));
+               
+               enet_free (fragment);
+            }
+            
+            return -1;
+         }
+         
+         fragment -> fragmentOffset = fragmentOffset;
+         fragment -> fragmentLength = fragmentLength;
+         fragment -> packet = packet;
+         fragment -> command.header.command = commandNumber;
+         fragment -> command.header.channelID = channelID;
+         fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber;
+         fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength);
+         fragment -> command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32 (fragmentCount);
+         fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber);
+         fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength);
+         fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset);
+        
+         enet_list_insert (enet_list_end (& fragments), fragment);
+      }
+
+      packet -> referenceCount += fragmentNumber;
+
+      while (! enet_list_empty (& fragments))
+      {
+         fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments));
+ 
+         enet_peer_setup_outgoing_command (peer, fragment);
+      }
+
+      return 0;
+   }
+
+   command.header.channelID = channelID;
+
+   if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED)
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
+      command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
+   }
+   else 
+   if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF)
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+      command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
+   }
+   else
+   {
+      command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE;
+      command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
+   }
+
+   if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL)
+     return -1;
+
+   return 0;
+}
+
+/** Attempts to dequeue any incoming queued packet.
+    @param peer peer to dequeue packets from
+    @param channelID holds the channel ID of the channel the packet was received on success
+    @returns a pointer to the packet, or NULL if there are no available incoming queued packets
+*/
+ENetPacket *
+enet_peer_receive (ENetPeer * peer, enet_uint8 * channelID)
+{
+   ENetIncomingCommand * incomingCommand;
+   ENetPacket * packet;
+   
+   if (enet_list_empty (& peer -> dispatchedCommands))
+     return NULL;
+
+   incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (& peer -> dispatchedCommands));
+
+   if (channelID != NULL)
+     * channelID = incomingCommand -> command.header.channelID;
+
+   packet = incomingCommand -> packet;
+
+   -- packet -> referenceCount;
+
+   if (incomingCommand -> fragments != NULL)
+     enet_free (incomingCommand -> fragments);
+
+   enet_free (incomingCommand);
+
+   peer -> totalWaitingData -= packet -> dataLength;
+
+   return packet;
+}
+
+static void
+enet_peer_reset_outgoing_commands (ENetList * queue)
+{
+    ENetOutgoingCommand * outgoingCommand;
+
+    while (! enet_list_empty (queue))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue));
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          -- outgoingCommand -> packet -> referenceCount;
+
+          if (outgoingCommand -> packet -> referenceCount == 0)
+            enet_packet_destroy (outgoingCommand -> packet);
+       }
+
+       enet_free (outgoingCommand);
+    }
+}
+
+static void
+enet_peer_remove_incoming_commands (ENetList * queue, ENetListIterator startCommand, ENetListIterator endCommand, ENetIncomingCommand * excludeCommand)
+{
+    ENetListIterator currentCommand;    
+    
+    for (currentCommand = startCommand; currentCommand != endCommand; )
+    {
+       ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (incomingCommand == excludeCommand)
+         continue;
+
+       enet_list_remove (& incomingCommand -> incomingCommandList);
+ 
+       if (incomingCommand -> packet != NULL)
+       {
+          -- incomingCommand -> packet -> referenceCount;
+
+          if (incomingCommand -> packet -> referenceCount == 0)
+            enet_packet_destroy (incomingCommand -> packet);
+       }
+
+       if (incomingCommand -> fragments != NULL)
+         enet_free (incomingCommand -> fragments);
+
+       enet_free (incomingCommand);
+    }
+}
+
+static void
+enet_peer_reset_incoming_commands (ENetList * queue)
+{
+    enet_peer_remove_incoming_commands(queue, enet_list_begin (queue), enet_list_end (queue), NULL);
+}
+ 
+void
+enet_peer_reset_queues (ENetPeer * peer)
+{
+    ENetChannel * channel;
+
+    if (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH)
+    {
+       enet_list_remove (& peer -> dispatchList);
+
+       peer -> flags &= ~ ENET_PEER_FLAG_NEEDS_DISPATCH;
+    }
+
+    while (! enet_list_empty (& peer -> acknowledgements))
+      enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));
+
+    enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands);
+    enet_peer_reset_outgoing_commands (& peer -> outgoingCommands);
+    enet_peer_reset_incoming_commands (& peer -> dispatchedCommands);
+
+    if (peer -> channels != NULL && peer -> channelCount > 0)
+    {
+        for (channel = peer -> channels;
+             channel < & peer -> channels [peer -> channelCount];
+             ++ channel)
+        {
+            enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands);
+            enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands);
+        }
+
+        enet_free (peer -> channels);
+    }
+
+    peer -> channels = NULL;
+    peer -> channelCount = 0;
+}
+
+void
+enet_peer_on_connect (ENetPeer * peer)
+{
+    if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+    {
+        if (peer -> incomingBandwidth != 0)
+          ++ peer -> host -> bandwidthLimitedPeers;
+
+        ++ peer -> host -> connectedPeers;
+    }
+}
+
+void
+enet_peer_on_disconnect (ENetPeer * peer)
+{
+    if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+    {
+        if (peer -> incomingBandwidth != 0)
+          -- peer -> host -> bandwidthLimitedPeers;
+
+        -- peer -> host -> connectedPeers;
+    }
+}
+
+/** Forcefully disconnects a peer.
+    @param peer peer to forcefully disconnect
+    @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout
+    on its connection to the local host.
+*/
+void
+enet_peer_reset (ENetPeer * peer)
+{
+    enet_peer_on_disconnect (peer);
+        
+    peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID;
+    peer -> connectID = 0;
+
+    peer -> state = ENET_PEER_STATE_DISCONNECTED;
+
+    peer -> incomingBandwidth = 0;
+    peer -> outgoingBandwidth = 0;
+    peer -> incomingBandwidthThrottleEpoch = 0;
+    peer -> outgoingBandwidthThrottleEpoch = 0;
+    peer -> incomingDataTotal = 0;
+    peer -> outgoingDataTotal = 0;
+    peer -> lastSendTime = 0;
+    peer -> lastReceiveTime = 0;
+    peer -> nextTimeout = 0;
+    peer -> earliestTimeout = 0;
+    peer -> packetLossEpoch = 0;
+    peer -> packetsSent = 0;
+    peer -> packetsLost = 0;
+    peer -> packetLoss = 0;
+    peer -> packetLossVariance = 0;
+    peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE;
+    peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE;
+    peer -> packetThrottleCounter = 0;
+    peer -> packetThrottleEpoch = 0;
+    peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION;
+    peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION;
+    peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL;
+    peer -> pingInterval = ENET_PEER_PING_INTERVAL;
+    peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT;
+    peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM;
+    peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM;
+    peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> lastRoundTripTimeVariance = 0;
+    peer -> highestRoundTripTimeVariance = 0;
+    peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
+    peer -> roundTripTimeVariance = 0;
+    peer -> mtu = peer -> host -> mtu;
+    peer -> reliableDataInTransit = 0;
+    peer -> outgoingReliableSequenceNumber = 0;
+    peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    peer -> incomingUnsequencedGroup = 0;
+    peer -> outgoingUnsequencedGroup = 0;
+    peer -> eventData = 0;
+    peer -> totalWaitingData = 0;
+    peer -> flags = 0;
+
+    memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
+    
+    enet_peer_reset_queues (peer);
+}
+
+/** Sends a ping request to a peer.
+    @param peer destination for the ping request
+    @remarks ping requests factor into the mean round trip time as designated by the 
+    roundTripTime field in the ENetPeer structure.  ENet automatically pings all connected
+    peers at regular intervals, however, this function may be called to ensure more
+    frequent ping requests.
+*/
+void
+enet_peer_ping (ENetPeer * peer)
+{
+    ENetProtocol command;
+
+    if (peer -> state != ENET_PEER_STATE_CONNECTED)
+      return;
+
+    command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+    command.header.channelID = 0xFF;
+   
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+}
+
+/** Sets the interval at which pings will be sent to a peer. 
+    
+    Pings are used both to monitor the liveness of the connection and also to dynamically
+    adjust the throttle during periods of low traffic so that the throttle has reasonable
+    responsiveness during traffic spikes.
+
+    @param peer the peer to adjust
+    @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0
+*/
+void
+enet_peer_ping_interval (ENetPeer * peer, enet_uint32 pingInterval)
+{
+	if (pingInterval)
+		peer -> pingInterval = pingInterval;
+	else
+		peer -> pingInterval = ENET_PEER_PING_INTERVAL;
+}
+
+/** Sets the timeout parameters for a peer.
+
+    The timeout parameter control how and when a peer will timeout from a failure to acknowledge
+    reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable
+    packet is not acknowledge within some multiple of the average RTT plus a variance tolerance, 
+    the timeout will be doubled until it reaches a set limit. If the timeout is thus at this
+    limit and reliable packets have been sent but not acknowledged within a certain minimum time 
+    period, the peer will be disconnected. Alternatively, if reliable packets have been sent
+    but not acknowledged for a certain maximum time period, the peer will be disconnected regardless
+    of the current timeout limit value.
+    
+    @param peer the peer to adjust
+    @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0
+    @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0
+    @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0
+*/
+
+void
+enet_peer_timeout (ENetPeer * peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum)
+{
+	if (timeoutLimit)
+		peer -> timeoutLimit = timeoutLimit;
+	else
+		peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT;
+	if (timeoutMinimum)
+		peer -> timeoutMinimum = timeoutMinimum;
+	else
+		peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM;
+	if (timeoutMaximum)
+		peer -> timeoutMaximum = timeoutMaximum;
+	else
+		peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM;
+}
+
+/** Force an immediate disconnection from a peer.
+    @param peer peer to disconnect
+    @param data data describing the disconnection
+    @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not
+    guaranteed to receive the disconnect notification, and is reset immediately upon
+    return from this function.
+*/
+void
+enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data)
+{
+    ENetProtocol command;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECTED)
+      return;
+
+    if (peer -> state != ENET_PEER_STATE_ZOMBIE &&
+        peer -> state != ENET_PEER_STATE_DISCONNECTING)
+    {
+        enet_peer_reset_queues (peer);
+
+        command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
+        command.header.channelID = 0xFF;
+        command.disconnect.data = ENET_HOST_TO_NET_32 (data);
+
+        enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+        enet_host_flush (peer -> host);
+    }
+
+    enet_peer_reset (peer);
+}
+
+/** Request a disconnection from a peer.
+    @param peer peer to request a disconnection
+    @param data data describing the disconnection
+    @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
+    once the disconnection is complete.
+*/
+void
+enet_peer_disconnect (ENetPeer * peer, enet_uint32 data)
+{
+    ENetProtocol command;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECTING ||
+        peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+        peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT ||
+        peer -> state == ENET_PEER_STATE_ZOMBIE)
+      return;
+
+    enet_peer_reset_queues (peer);
+
+    command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT;
+    command.header.channelID = 0xFF;
+    command.disconnect.data = ENET_HOST_TO_NET_32 (data);
+
+    if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+      command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+    else
+      command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;      
+    
+    enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
+
+    if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+    {
+        enet_peer_on_disconnect (peer);
+
+        peer -> state = ENET_PEER_STATE_DISCONNECTING;
+    }
+    else
+    {
+        enet_host_flush (peer -> host);
+        enet_peer_reset (peer);
+    }
+}
+
+/** Request a disconnection from a peer, but only after all queued outgoing packets are sent.
+    @param peer peer to request a disconnection
+    @param data data describing the disconnection
+    @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
+    once the disconnection is complete.
+*/
+void
+enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data)
+{   
+    if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && 
+        ! (enet_list_empty (& peer -> outgoingCommands) &&
+           enet_list_empty (& peer -> sentReliableCommands)))
+    {
+        peer -> state = ENET_PEER_STATE_DISCONNECT_LATER;
+        peer -> eventData = data;
+    }
+    else
+      enet_peer_disconnect (peer, data);
+}
+
+ENetAcknowledgement *
+enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint16 sentTime)
+{
+    ENetAcknowledgement * acknowledgement;
+
+    if (command -> header.channelID < peer -> channelCount)
+    {
+        ENetChannel * channel = & peer -> channels [command -> header.channelID];
+        enet_uint16 reliableWindow = command -> header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE,
+                    currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+        if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+           reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+        if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS)
+          return NULL;
+    }
+
+    acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement));
+    if (acknowledgement == NULL)
+      return NULL;
+
+    peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge);
+
+    acknowledgement -> sentTime = sentTime;
+    acknowledgement -> command = * command;
+    
+    enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement);
+    
+    return acknowledgement;
+}
+
+void
+enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand)
+{
+    ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
+    
+    peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength;
+
+    if (outgoingCommand -> command.header.channelID == 0xFF)
+    {
+       ++ peer -> outgoingReliableSequenceNumber;
+
+       outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+    {
+       ++ channel -> outgoingReliableSequenceNumber;
+       channel -> outgoingUnreliableSequenceNumber = 0;
+
+       outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
+    {
+       ++ peer -> outgoingUnsequencedGroup;
+
+       outgoingCommand -> reliableSequenceNumber = 0;
+       outgoingCommand -> unreliableSequenceNumber = 0;
+    }
+    else
+    {
+       if (outgoingCommand -> fragmentOffset == 0)
+         ++ channel -> outgoingUnreliableSequenceNumber;
+        
+       outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
+       outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
+    }
+   
+    outgoingCommand -> sendAttempts = 0;
+    outgoingCommand -> sentTime = 0;
+    outgoingCommand -> roundTripTimeout = 0;
+    outgoingCommand -> roundTripTimeoutLimit = 0;
+    outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber);
+
+    switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK)
+    {
+    case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+        outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber);
+        break;
+
+    case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+        outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup);
+        break;
+    
+    default:
+        break;
+    }
+
+    enet_list_insert (enet_list_end (& peer -> outgoingCommands), outgoingCommand);
+}
+
+ENetOutgoingCommand *
+enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length)
+{
+    ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
+    if (outgoingCommand == NULL)
+      return NULL;
+
+    outgoingCommand -> command = * command;
+    outgoingCommand -> fragmentOffset = offset;
+    outgoingCommand -> fragmentLength = length;
+    outgoingCommand -> packet = packet;
+    if (packet != NULL)
+      ++ packet -> referenceCount;
+
+    enet_peer_setup_outgoing_command (peer, outgoingCommand);
+
+    return outgoingCommand;
+}
+
+void
+enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel, ENetIncomingCommand * queuedCommand)
+{
+    ENetListIterator droppedCommand, startCommand, currentCommand;
+
+    for (droppedCommand = startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands);
+         currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+         currentCommand = enet_list_next (currentCommand))
+    {
+       ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+       if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
+         continue;
+
+       if (incomingCommand -> reliableSequenceNumber == channel -> incomingReliableSequenceNumber)
+       {
+          if (incomingCommand -> fragmentsRemaining <= 0)
+          {
+             channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber;
+             continue;
+          }
+
+          if (startCommand != currentCommand)
+          {
+             enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
+
+             if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH))
+             {
+                enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+                peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH;
+             }
+
+             droppedCommand = currentCommand;
+          }
+          else
+          if (droppedCommand != currentCommand)
+            droppedCommand = enet_list_previous (currentCommand);
+       }
+       else 
+       {
+          enet_uint16 reliableWindow = incomingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE,
+                      currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+          if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+            reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+          if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+            break;
+
+          droppedCommand = enet_list_next (currentCommand);
+
+          if (startCommand != currentCommand)
+          {
+             enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
+
+             if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH))
+             {
+                enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+                peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH;
+             }
+          }
+       }
+          
+       startCommand = enet_list_next (currentCommand);
+    }
+
+    if (startCommand != currentCommand)
+    {
+       enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
+
+       if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH))
+       {
+           enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+           peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH;
+       }
+
+       droppedCommand = currentCommand;
+    }
+
+    enet_peer_remove_incoming_commands (& channel -> incomingUnreliableCommands, enet_list_begin (& channel -> incomingUnreliableCommands), droppedCommand, queuedCommand);
+}
+
+void
+enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * channel, ENetIncomingCommand * queuedCommand)
+{
+    ENetListIterator currentCommand;
+
+    for (currentCommand = enet_list_begin (& channel -> incomingReliableCommands);
+         currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+         currentCommand = enet_list_next (currentCommand))
+    {
+       ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+         
+       if (incomingCommand -> fragmentsRemaining > 0 ||
+           incomingCommand -> reliableSequenceNumber != (enet_uint16) (channel -> incomingReliableSequenceNumber + 1))
+         break;
+
+       channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber;
+
+       if (incomingCommand -> fragmentCount > 0)
+         channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1;
+    } 
+
+    if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands))
+      return;
+
+    channel -> incomingUnreliableSequenceNumber = 0;
+
+    enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand));
+
+    if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH))
+    {
+       enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+
+       peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH;
+    }
+
+    if (! enet_list_empty (& channel -> incomingUnreliableCommands))
+       enet_peer_dispatch_incoming_unreliable_commands (peer, channel, queuedCommand);
+}
+
+ENetIncomingCommand *
+enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, const void * data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount)
+{
+    static ENetIncomingCommand dummyCommand;
+
+    ENetChannel * channel = & peer -> channels [command -> header.channelID];
+    enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0;
+    enet_uint16 reliableWindow, currentWindow;
+    ENetIncomingCommand * incomingCommand;
+    ENetListIterator currentCommand;
+    ENetPacket * packet = NULL;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
+      goto discardCommand;
+
+    if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
+    {
+        reliableSequenceNumber = command -> header.reliableSequenceNumber;
+        reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+        currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+        if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+           reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+        if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+          goto discardCommand;
+    }
+                    
+    switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK)
+    {
+    case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+    case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+       if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber)
+         goto discardCommand;
+       
+       for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+            currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+            currentCommand = enet_list_previous (currentCommand))
+       {
+          incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+          if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+          {
+             if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+               continue;
+          }
+          else
+          if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+            break;
+
+          if (incomingCommand -> reliableSequenceNumber <= reliableSequenceNumber)
+          {
+             if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
+               break;
+
+             goto discardCommand;
+          }
+       }
+       break;
+
+    case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+    case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT:
+       unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber);
+
+       if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && 
+           unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
+         goto discardCommand;
+
+       for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
+            currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+            currentCommand = enet_list_previous (currentCommand))
+       {
+          incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+          if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
+            continue;
+
+          if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+          {
+             if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+               continue;
+          }
+          else
+          if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+            break;
+
+          if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
+            break;
+
+          if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber)
+            continue;
+
+          if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber)
+          {
+             if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber)
+               break;
+
+             goto discardCommand;
+          }
+       }
+       break;
+
+    case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+       currentCommand = enet_list_end (& channel -> incomingUnreliableCommands);
+       break;
+
+    default:
+       goto discardCommand;
+    }
+
+    if (peer -> totalWaitingData >= peer -> host -> maximumWaitingData)
+      goto notifyError;
+
+    packet = enet_packet_create (data, dataLength, flags);
+    if (packet == NULL)
+      goto notifyError;
+
+    incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand));
+    if (incomingCommand == NULL)
+      goto notifyError;
+
+    incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber;
+    incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF;
+    incomingCommand -> command = * command;
+    incomingCommand -> fragmentCount = fragmentCount;
+    incomingCommand -> fragmentsRemaining = fragmentCount;
+    incomingCommand -> packet = packet;
+    incomingCommand -> fragments = NULL;
+    
+    if (fragmentCount > 0)
+    { 
+       if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT)
+         incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32));
+       if (incomingCommand -> fragments == NULL)
+       {
+          enet_free (incomingCommand);
+
+          goto notifyError;
+       }
+       memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32));
+    }
+
+    if (packet != NULL)
+    {
+       ++ packet -> referenceCount;
+      
+       peer -> totalWaitingData += packet -> dataLength;
+    }
+
+    enet_list_insert (enet_list_next (currentCommand), incomingCommand);
+
+    switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK)
+    {
+    case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+    case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+       enet_peer_dispatch_incoming_reliable_commands (peer, channel, incomingCommand);
+       break;
+
+    default:
+       enet_peer_dispatch_incoming_unreliable_commands (peer, channel, incomingCommand);
+       break;
+    }
+
+    return incomingCommand;
+
+discardCommand:
+    if (fragmentCount > 0)
+      goto notifyError;
+
+    if (packet != NULL && packet -> referenceCount == 0)
+      enet_packet_destroy (packet);
+
+    return & dummyCommand;
+
+notifyError:
+    if (packet != NULL && packet -> referenceCount == 0)
+      enet_packet_destroy (packet);
+
+    return NULL;
+}
+
+/** @} */
diff --git a/backends/networking/enet/source/protocol.cpp b/backends/networking/enet/source/protocol.cpp
new file mode 100644
index 00000000000..de55a083535
--- /dev/null
+++ b/backends/networking/enet/source/protocol.cpp
@@ -0,0 +1,1877 @@
+/** 
+ @file  protocol.c
+ @brief ENet protocol functions
+*/
+#include <stdio.h>
+#include <string.h>
+#define ENET_BUILDING_LIB 1
+#include "utility.h"
+#include "time.h"
+#include "enet.h"
+
+static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
+{
+    0,
+    sizeof (ENetProtocolAcknowledge),
+    sizeof (ENetProtocolConnect),
+    sizeof (ENetProtocolVerifyConnect),
+    sizeof (ENetProtocolDisconnect),
+    sizeof (ENetProtocolPing),
+    sizeof (ENetProtocolSendReliable),
+    sizeof (ENetProtocolSendUnreliable),
+    sizeof (ENetProtocolSendFragment),
+    sizeof (ENetProtocolSendUnsequenced),
+    sizeof (ENetProtocolBandwidthLimit),
+    sizeof (ENetProtocolThrottleConfigure),
+    sizeof (ENetProtocolSendFragment)
+};
+
+size_t
+enet_protocol_command_size (enet_uint8 commandNumber)
+{
+    return commandSizes [commandNumber & ENET_PROTOCOL_COMMAND_MASK];
+}
+
+static void
+enet_protocol_change_state (ENetHost * host, ENetPeer * peer, ENetPeerState state)
+{
+    if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER)
+      enet_peer_on_connect (peer);
+    else
+      enet_peer_on_disconnect (peer);
+
+    peer -> state = state;
+}
+
+static void
+enet_protocol_dispatch_state (ENetHost * host, ENetPeer * peer, ENetPeerState state)
+{
+    enet_protocol_change_state (host, peer, state);
+
+    if (! (peer -> flags & ENET_PEER_FLAG_NEEDS_DISPATCH))
+    {
+       enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList);
+
+       peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH;
+    }
+}
+
+static int
+enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    while (! enet_list_empty (& host -> dispatchQueue))
+    {
+       ENetPeer * peer = (ENetPeer *) enet_list_remove (enet_list_begin (& host -> dispatchQueue));
+
+       peer -> flags &= ~ ENET_PEER_FLAG_NEEDS_DISPATCH;
+
+       switch (peer -> state)
+       {
+       case ENET_PEER_STATE_CONNECTION_PENDING:
+       case ENET_PEER_STATE_CONNECTION_SUCCEEDED:
+           enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED);
+
+           event -> type = ENET_EVENT_TYPE_CONNECT;
+           event -> peer = peer;
+           event -> data = peer -> eventData;
+
+           return 1;
+           
+       case ENET_PEER_STATE_ZOMBIE:
+           host -> recalculateBandwidthLimits = 1;
+
+           event -> type = ENET_EVENT_TYPE_DISCONNECT;
+           event -> peer = peer;
+           event -> data = peer -> eventData;
+
+           enet_peer_reset (peer);
+
+           return 1;
+
+       case ENET_PEER_STATE_CONNECTED:
+           if (enet_list_empty (& peer -> dispatchedCommands))
+             continue;
+
+           event -> packet = enet_peer_receive (peer, & event -> channelID);
+           if (event -> packet == NULL)
+             continue;
+             
+           event -> type = ENET_EVENT_TYPE_RECEIVE;
+           event -> peer = peer;
+
+           if (! enet_list_empty (& peer -> dispatchedCommands))
+           {
+              peer -> flags |= ENET_PEER_FLAG_NEEDS_DISPATCH;
+         
+              enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList);
+           }
+
+           return 1;
+
+       default:
+           break;
+       }
+    }
+
+    return 0;
+}
+
+static void
+enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+    host -> recalculateBandwidthLimits = 1;
+
+    if (event != NULL)
+    {
+        enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED);
+
+        event -> type = ENET_EVENT_TYPE_CONNECT;
+        event -> peer = peer;
+        event -> data = peer -> eventData;
+    }
+    else 
+        enet_protocol_dispatch_state (host, peer, peer -> state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING);
+}
+
+static void
+enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+    if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING)
+       host -> recalculateBandwidthLimits = 1;
+
+    if (peer -> state != ENET_PEER_STATE_CONNECTING && peer -> state < ENET_PEER_STATE_CONNECTION_SUCCEEDED)
+        enet_peer_reset (peer);
+    else
+    if (event != NULL)
+    {
+        event -> type = ENET_EVENT_TYPE_DISCONNECT;
+        event -> peer = peer;
+        event -> data = 0;
+
+        enet_peer_reset (peer);
+    }
+    else 
+    {
+        peer -> eventData = 0;
+
+        enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+    }
+}
+
+static void
+enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
+{
+    ENetOutgoingCommand * outgoingCommand;
+
+    if (enet_list_empty (& peer -> sentUnreliableCommands))
+      return;
+
+    do
+    {
+        outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands);
+        
+        enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+        if (outgoingCommand -> packet != NULL)
+        {
+           -- outgoingCommand -> packet -> referenceCount;
+
+           if (outgoingCommand -> packet -> referenceCount == 0)
+           {
+              outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT;
+ 
+              enet_packet_destroy (outgoingCommand -> packet);
+           }
+        }
+
+        enet_free (outgoingCommand);
+    } while (! enet_list_empty (& peer -> sentUnreliableCommands));
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
+        enet_list_empty (& peer -> outgoingCommands) &&
+        enet_list_empty (& peer -> sentReliableCommands))
+      enet_peer_disconnect (peer, peer -> eventData);
+}
+
+static ENetProtocolCommand
+enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID)
+{
+    ENetOutgoingCommand * outgoingCommand = NULL;
+    ENetListIterator currentCommand;
+    ENetProtocolCommand commandNumber;
+    int wasSent = 1;
+
+    for (currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+         currentCommand != enet_list_end (& peer -> sentReliableCommands);
+         currentCommand = enet_list_next (currentCommand))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+        
+       if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
+           outgoingCommand -> command.header.channelID == channelID)
+         break;
+    }
+
+    if (currentCommand == enet_list_end (& peer -> sentReliableCommands))
+    {
+       for (currentCommand = enet_list_begin (& peer -> outgoingCommands);
+            currentCommand != enet_list_end (& peer -> outgoingCommands);
+            currentCommand = enet_list_next (currentCommand))
+       {
+          outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+          if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE))
+            continue;
+
+          if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE;
+
+          if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
+              outgoingCommand -> command.header.channelID == channelID)
+            break;
+       }
+
+       if (currentCommand == enet_list_end (& peer -> outgoingCommands))
+         return ENET_PROTOCOL_COMMAND_NONE;
+
+       wasSent = 0;
+    }
+
+    if (outgoingCommand == NULL)
+      return ENET_PROTOCOL_COMMAND_NONE;
+
+    if (channelID < peer -> channelCount)
+    {
+       ENetChannel * channel = & peer -> channels [channelID];
+       enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+       if (channel -> reliableWindows [reliableWindow] > 0)
+       {
+          -- channel -> reliableWindows [reliableWindow];
+          if (! channel -> reliableWindows [reliableWindow])
+            channel -> usedReliableWindows &= ~ (1 << reliableWindow);
+       }
+    }
+
+    commandNumber = (ENetProtocolCommand) (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK);
+    
+    enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+    if (outgoingCommand -> packet != NULL)
+    {
+       if (wasSent)
+         peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+
+       -- outgoingCommand -> packet -> referenceCount;
+
+       if (outgoingCommand -> packet -> referenceCount == 0)
+       {
+          outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT;
+
+          enet_packet_destroy (outgoingCommand -> packet);
+       }
+    }
+
+    enet_free (outgoingCommand);
+
+    if (enet_list_empty (& peer -> sentReliableCommands))
+      return commandNumber;
+    
+    outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands);
+    
+    peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+
+    return commandNumber;
+} 
+
+static ENetPeer *
+enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command)
+{
+    enet_uint8 incomingSessionID, outgoingSessionID;
+    enet_uint32 mtu, windowSize;
+    ENetChannel * channel;
+    size_t channelCount, duplicatePeers = 0;
+    ENetPeer * currentPeer, * peer = NULL;
+    ENetProtocol verifyCommand;
+
+    channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount);
+
+    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT ||
+        channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
+      return NULL;
+
+    for (currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+        if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
+        {
+            if (peer == NULL)
+              peer = currentPeer;
+        }
+        else 
+        if (currentPeer -> state != ENET_PEER_STATE_CONNECTING &&
+            currentPeer -> address.host == host -> receivedAddress.host)
+        {
+            if (currentPeer -> address.port == host -> receivedAddress.port &&
+                currentPeer -> connectID == command -> connect.connectID)
+              return NULL;
+
+            ++ duplicatePeers;
+        }
+    }
+
+    if (peer == NULL || duplicatePeers >= host -> duplicatePeers)
+      return NULL;
+
+    if (channelCount > host -> channelLimit)
+      channelCount = host -> channelLimit;
+    peer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
+    if (peer -> channels == NULL)
+      return NULL;
+    peer -> channelCount = channelCount;
+    peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
+    peer -> connectID = command -> connect.connectID;
+    peer -> address = host -> receivedAddress;
+    peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
+    peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
+    peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth);
+    peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval);
+    peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration);
+    peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration);
+    peer -> eventData = ENET_NET_TO_HOST_32 (command -> connect.data);
+
+    incomingSessionID = command -> connect.incomingSessionID == 0xFF ? peer -> outgoingSessionID : command -> connect.incomingSessionID;
+    incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+    if (incomingSessionID == peer -> outgoingSessionID)
+      incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+    peer -> outgoingSessionID = incomingSessionID;
+
+    outgoingSessionID = command -> connect.outgoingSessionID == 0xFF ? peer -> incomingSessionID : command -> connect.outgoingSessionID;
+    outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+    if (outgoingSessionID == peer -> incomingSessionID)
+      outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
+    peer -> incomingSessionID = outgoingSessionID;
+
+    for (channel = peer -> channels;
+         channel < & peer -> channels [channelCount];
+         ++ channel)
+    {
+        channel -> outgoingReliableSequenceNumber = 0;
+        channel -> outgoingUnreliableSequenceNumber = 0;
+        channel -> incomingReliableSequenceNumber = 0;
+        channel -> incomingUnreliableSequenceNumber = 0;
+
+        enet_list_clear (& channel -> incomingReliableCommands);
+        enet_list_clear (& channel -> incomingUnreliableCommands);
+
+        channel -> usedReliableWindows = 0;
+        memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
+    }
+
+    mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu);
+
+    if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+      mtu = ENET_PROTOCOL_MINIMUM_MTU;
+    else
+    if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+      mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+    peer -> mtu = mtu;
+
+    if (host -> outgoingBandwidth == 0 &&
+        peer -> incomingBandwidth == 0)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+    if (host -> outgoingBandwidth == 0 ||
+        peer -> incomingBandwidth == 0)
+      peer -> windowSize = (ENET_MAX (host -> outgoingBandwidth, peer -> incomingBandwidth) /
+                                    ENET_PEER_WINDOW_SIZE_SCALE) *
+                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+      peer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, peer -> incomingBandwidth) /
+                                    ENET_PEER_WINDOW_SIZE_SCALE) * 
+                                      ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    if (host -> incomingBandwidth == 0)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+      windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) *
+                     ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize))
+      windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize);
+
+    if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
+    verifyCommand.header.channelID = 0xFF;
+    verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (peer -> incomingPeerID);
+    verifyCommand.verifyConnect.incomingSessionID = incomingSessionID;
+    verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID;
+    verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (peer -> mtu);
+    verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize);
+    verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
+    verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
+    verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
+    verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (peer -> packetThrottleInterval);
+    verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleAcceleration);
+    verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleDeceleration);
+    verifyCommand.verifyConnect.connectID = peer -> connectID;
+
+    enet_peer_queue_outgoing_command (peer, & verifyCommand, NULL, 0, 0);
+
+    return peer;
+}
+
+static int
+enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+    size_t dataLength;
+
+    if (command -> header.channelID >= peer -> channelCount ||
+        (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+      return -1;
+
+    dataLength = ENET_NET_TO_HOST_16 (command -> sendReliable.dataLength);
+    * currentData += dataLength;
+    if (dataLength > host -> maximumPacketSize ||
+        * currentData < host -> receivedData ||
+        * currentData > & host -> receivedData [host -> receivedDataLength])
+      return -1;
+
+    if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL)
+      return -1;
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+    enet_uint32 unsequencedGroup, index;
+    size_t dataLength;
+
+    if (command -> header.channelID >= peer -> channelCount ||
+        (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+      return -1;
+
+    dataLength = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.dataLength);
+    * currentData += dataLength;
+    if (dataLength > host -> maximumPacketSize ||
+        * currentData < host -> receivedData ||
+        * currentData > & host -> receivedData [host -> receivedDataLength])
+      return -1; 
+
+    unsequencedGroup = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.unsequencedGroup);
+    index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE;
+   
+    if (unsequencedGroup < peer -> incomingUnsequencedGroup)
+      unsequencedGroup += 0x10000;
+
+    if (unsequencedGroup >= (enet_uint32) peer -> incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE)
+      return 0;
+
+    unsequencedGroup &= 0xFFFF;
+
+    if (unsequencedGroup - index != peer -> incomingUnsequencedGroup)
+    {
+        peer -> incomingUnsequencedGroup = unsequencedGroup - index;
+
+        memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
+    }
+    else
+    if (peer -> unsequencedWindow [index / 32] & (1 << (index % 32)))
+      return 0;
+      
+    if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED, 0) == NULL)
+      return -1;
+   
+    peer -> unsequencedWindow [index / 32] |= 1 << (index % 32);
+ 
+    return 0;
+}
+
+static int
+enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+    size_t dataLength;
+
+    if (command -> header.channelID >= peer -> channelCount ||
+        (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+      return -1;
+
+    dataLength = ENET_NET_TO_HOST_16 (command -> sendUnreliable.dataLength);
+    * currentData += dataLength;
+    if (dataLength > host -> maximumPacketSize ||
+        * currentData < host -> receivedData ||
+        * currentData > & host -> receivedData [host -> receivedDataLength])
+      return -1;
+
+    if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL)
+      return -1;
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+    enet_uint32 fragmentNumber,
+           fragmentCount,
+           fragmentOffset,
+           fragmentLength,
+           startSequenceNumber,
+           totalLength;
+    ENetChannel * channel;
+    enet_uint16 startWindow, currentWindow;
+    ENetListIterator currentCommand;
+    ENetIncomingCommand * startCommand = NULL;
+
+    if (command -> header.channelID >= peer -> channelCount ||
+        (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+      return -1;
+
+    fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
+    * currentData += fragmentLength;
+    if (fragmentLength > host -> maximumPacketSize ||
+        * currentData < host -> receivedData ||
+        * currentData > & host -> receivedData [host -> receivedDataLength])
+      return -1;
+
+    channel = & peer -> channels [command -> header.channelID];
+    startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber);
+    startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+    currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+    if (startSequenceNumber < channel -> incomingReliableSequenceNumber)
+      startWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+    if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+      return 0;
+
+    fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
+    fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
+    fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
+    totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
+    
+    if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
+        fragmentNumber >= fragmentCount ||
+        totalLength > host -> maximumPacketSize ||
+        fragmentOffset >= totalLength ||
+        fragmentLength > totalLength - fragmentOffset)
+      return -1;
+ 
+    for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
+         currentCommand != enet_list_end (& channel -> incomingReliableCommands);
+         currentCommand = enet_list_previous (currentCommand))
+    {
+       ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+       if (startSequenceNumber >= channel -> incomingReliableSequenceNumber)
+       {
+          if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+            continue;
+       }
+       else
+       if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+         break;
+
+       if (incomingCommand -> reliableSequenceNumber <= startSequenceNumber)
+       {
+          if (incomingCommand -> reliableSequenceNumber < startSequenceNumber)
+            break;
+        
+          if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT ||
+              totalLength != incomingCommand -> packet -> dataLength ||
+              fragmentCount != incomingCommand -> fragmentCount)
+            return -1;
+
+          startCommand = incomingCommand;
+          break;
+       }
+    }
+ 
+    if (startCommand == NULL)
+    {
+       ENetProtocol hostCommand = * command;
+
+       hostCommand.header.reliableSequenceNumber = startSequenceNumber;
+
+       startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount);
+       if (startCommand == NULL)
+         return -1;
+    }
+    
+    if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0)
+    {
+       -- startCommand -> fragmentsRemaining;
+
+       startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32));
+
+       if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
+         fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
+
+       memcpy (startCommand -> packet -> data + fragmentOffset,
+               static_cast<const ENetProtocol *>(command) + sizeof (ENetProtocolSendFragment),
+               fragmentLength);
+
+        if (startCommand -> fragmentsRemaining <= 0)
+          enet_peer_dispatch_incoming_reliable_commands (peer, channel, NULL);
+    }
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
+{
+    enet_uint32 fragmentNumber,
+           fragmentCount,
+           fragmentOffset,
+           fragmentLength,
+           reliableSequenceNumber,
+           startSequenceNumber,
+           totalLength;
+    enet_uint16 reliableWindow, currentWindow;
+    ENetChannel * channel;
+    ENetListIterator currentCommand;
+    ENetIncomingCommand * startCommand = NULL;
+
+    if (command -> header.channelID >= peer -> channelCount ||
+        (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
+      return -1;
+
+    fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
+    * currentData += fragmentLength;
+    if (fragmentLength > host -> maximumPacketSize ||
+        * currentData < host -> receivedData ||
+        * currentData > & host -> receivedData [host -> receivedDataLength])
+      return -1;
+
+    channel = & peer -> channels [command -> header.channelID];
+    reliableSequenceNumber = command -> header.reliableSequenceNumber;
+    startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber);
+
+    reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+    currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+
+    if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+      reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
+
+    if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
+      return 0;
+
+    if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber &&
+        startSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
+      return 0;
+
+    fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
+    fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
+    fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
+    totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
+
+    if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
+        fragmentNumber >= fragmentCount ||
+        totalLength > host -> maximumPacketSize ||
+        fragmentOffset >= totalLength ||
+        fragmentLength > totalLength - fragmentOffset)
+      return -1;
+
+    for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
+         currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
+         currentCommand = enet_list_previous (currentCommand))
+    {
+       ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
+
+       if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+       {
+          if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
+            continue;
+       }
+       else
+       if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
+         break;
+
+       if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
+         break;
+
+       if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber)
+         continue;
+
+       if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber)
+       {
+          if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber)
+            break;
+
+          if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT ||
+              totalLength != incomingCommand -> packet -> dataLength ||
+              fragmentCount != incomingCommand -> fragmentCount)
+            return -1;
+
+          startCommand = incomingCommand;
+          break;
+       }
+    }
+
+    if (startCommand == NULL)
+    {
+       startCommand = enet_peer_queue_incoming_command (peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount);
+       if (startCommand == NULL)
+         return -1;
+    }
+
+    if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0)
+    {
+       -- startCommand -> fragmentsRemaining;
+
+       startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32));
+
+       if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
+         fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
+
+       memcpy (startCommand -> packet -> data + fragmentOffset,
+               reinterpret_cast<const enet_uint8 *>(command) + sizeof (ENetProtocolSendFragment),
+               fragmentLength);
+
+        if (startCommand -> fragmentsRemaining <= 0)
+          enet_peer_dispatch_incoming_unreliable_commands (peer, channel, NULL);
+    }
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+      return -1;
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+      return -1;
+
+    if (peer -> incomingBandwidth != 0)
+      -- host -> bandwidthLimitedPeers;
+
+    peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth);
+    peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth);
+
+    if (peer -> incomingBandwidth != 0)
+      ++ host -> bandwidthLimitedPeers;
+
+    if (peer -> incomingBandwidth == 0 && host -> outgoingBandwidth == 0)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+    else
+    if (peer -> incomingBandwidth == 0 || host -> outgoingBandwidth == 0)
+      peer -> windowSize = (ENET_MAX (peer -> incomingBandwidth, host -> outgoingBandwidth) /
+                             ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+      peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) /
+                             ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+    else
+    if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+      return -1;
+
+    peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval);
+    peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration);
+    peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration);
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
+{
+    if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT)
+      return 0;
+
+    enet_peer_reset_queues (peer);
+
+    if (peer -> state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer -> state == ENET_PEER_STATE_DISCONNECTING || peer -> state == ENET_PEER_STATE_CONNECTING)
+        enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+    else
+    if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
+    {
+        if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1;
+
+        enet_peer_reset (peer);
+    }
+    else
+    if (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+      enet_protocol_change_state (host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT);
+    else
+      enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+
+    if (peer -> state != ENET_PEER_STATE_DISCONNECTED)
+      peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data);
+
+    return 0;
+}
+
+static int
+enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint32 roundTripTime,
+           receivedSentTime,
+           receivedReliableSequenceNumber;
+    ENetProtocolCommand commandNumber;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE)
+      return 0;
+
+    receivedSentTime = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedSentTime);
+    receivedSentTime |= host -> serviceTime & 0xFFFF0000;
+    if ((receivedSentTime & 0x8000) > (host -> serviceTime & 0x8000))
+        receivedSentTime -= 0x10000;
+
+    if (ENET_TIME_LESS (host -> serviceTime, receivedSentTime))
+      return 0;
+
+    roundTripTime = ENET_TIME_DIFFERENCE (host -> serviceTime, receivedSentTime);
+    roundTripTime = ENET_MAX (roundTripTime, 1);
+
+    if (peer -> lastReceiveTime > 0)
+    {
+       enet_peer_throttle (peer, roundTripTime);
+
+       peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4;
+
+       if (roundTripTime >= peer -> roundTripTime)
+       {
+          enet_uint32 diff = roundTripTime - peer -> roundTripTime;
+          peer -> roundTripTimeVariance += diff / 4;
+          peer -> roundTripTime += diff / 8;
+       }
+       else
+       {
+          enet_uint32 diff = peer -> roundTripTime - roundTripTime;
+          peer -> roundTripTimeVariance += diff / 4;
+          peer -> roundTripTime -= diff / 8;
+       }
+    }
+    else
+    {
+       peer -> roundTripTime = roundTripTime;
+       peer -> roundTripTimeVariance = (roundTripTime + 1) / 2;
+    }
+
+    if (peer -> roundTripTime < peer -> lowestRoundTripTime)
+      peer -> lowestRoundTripTime = peer -> roundTripTime;
+
+    if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance)
+      peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+
+    if (peer -> packetThrottleEpoch == 0 ||
+        ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval)
+    {
+        peer -> lastRoundTripTime = peer -> lowestRoundTripTime;
+        peer -> lastRoundTripTimeVariance = ENET_MAX (peer -> highestRoundTripTimeVariance, 1);
+        peer -> lowestRoundTripTime = peer -> roundTripTime;
+        peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
+        peer -> packetThrottleEpoch = host -> serviceTime;
+    }
+
+    peer -> lastReceiveTime = ENET_MAX (host -> serviceTime, 1);
+    peer -> earliestTimeout = 0;
+
+    receivedReliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedReliableSequenceNumber);
+
+    commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID);
+
+    switch (peer -> state)
+    {
+    case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
+       if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT)
+         return -1;
+
+       enet_protocol_notify_connect (host, peer, event);
+       break;
+
+    case ENET_PEER_STATE_DISCONNECTING:
+       if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT)
+         return -1;
+
+       enet_protocol_notify_disconnect (host, peer, event);
+       break;
+
+    case ENET_PEER_STATE_DISCONNECT_LATER:
+       if (enet_list_empty (& peer -> outgoingCommands) &&
+           enet_list_empty (& peer -> sentReliableCommands))
+         enet_peer_disconnect (peer, peer -> eventData);
+       break;
+
+    default:
+       break;
+    }
+   
+    return 0;
+}
+
+static int
+enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
+{
+    enet_uint32 mtu, windowSize;
+    size_t channelCount;
+
+    if (peer -> state != ENET_PEER_STATE_CONNECTING)
+      return 0;
+
+    channelCount = ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount);
+
+    if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT ||
+        ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval ||
+        ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration ||
+        ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration ||
+        command -> verifyConnect.connectID != peer -> connectID)
+    {
+        peer -> eventData = 0;
+
+        enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+
+        return -1;
+    }
+
+    enet_protocol_remove_sent_reliable_command (peer, 1, 0xFF);
+    
+    if (channelCount < peer -> channelCount)
+      peer -> channelCount = channelCount;
+
+    peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID);
+    peer -> incomingSessionID = command -> verifyConnect.incomingSessionID;
+    peer -> outgoingSessionID = command -> verifyConnect.outgoingSessionID;
+
+    mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu);
+
+    if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
+      mtu = ENET_PROTOCOL_MINIMUM_MTU;
+    else 
+    if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
+      mtu = ENET_PROTOCOL_MAXIMUM_MTU;
+
+    if (mtu < peer -> mtu)
+      peer -> mtu = mtu;
+
+    windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize);
+
+    if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
+
+    if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
+      windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
+
+    if (windowSize < peer -> windowSize)
+      peer -> windowSize = windowSize;
+
+    peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth);
+    peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth);
+
+    enet_protocol_notify_connect (host, peer, event);
+    return 0;
+}
+
+static int
+enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    ENetProtocolHeader * header;
+    ENetProtocol * command;
+    ENetPeer * peer;
+    enet_uint8 * currentData;
+    size_t headerSize;
+    enet_uint16 peerID, flags;
+    enet_uint8 sessionID;
+
+    if (host -> receivedDataLength < (size_t) & ((ENetProtocolHeader *) 0) -> sentTime)
+      return 0;
+
+    header = (ENetProtocolHeader *) host -> receivedData;
+
+    peerID = ENET_NET_TO_HOST_16 (header -> peerID);
+    sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT;
+    flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK;
+    peerID &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK);
+
+    headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime);
+    if (host -> checksum != NULL)
+      headerSize += sizeof (enet_uint32);
+
+    if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID)
+      peer = NULL;
+    else
+    if (peerID >= host -> peerCount)
+      return 0;
+    else
+    {
+       peer = & host -> peers [peerID];
+
+       if (peer -> state == ENET_PEER_STATE_DISCONNECTED ||
+           peer -> state == ENET_PEER_STATE_ZOMBIE ||
+           ((host -> receivedAddress.host != peer -> address.host ||
+             host -> receivedAddress.port != peer -> address.port) &&
+             peer -> address.host != ENET_HOST_BROADCAST) ||
+           (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID &&
+            sessionID != peer -> incomingSessionID))
+         return 0;
+    }
+ 
+    if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED)
+    {
+        size_t originalSize;
+        if (host -> compressor.context == NULL || host -> compressor.decompress == NULL)
+          return 0;
+
+        originalSize = host -> compressor.decompress (host -> compressor.context,
+                                    host -> receivedData + headerSize, 
+                                    host -> receivedDataLength - headerSize, 
+                                    host -> packetData [1] + headerSize, 
+                                    sizeof (host -> packetData [1]) - headerSize);
+        if (originalSize <= 0 || originalSize > sizeof (host -> packetData [1]) - headerSize)
+          return 0;
+
+        memcpy (host -> packetData [1], header, headerSize);
+        host -> receivedData = host -> packetData [1];
+        host -> receivedDataLength = headerSize + originalSize;
+    }
+
+    if (host -> checksum != NULL)
+    {
+        enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)],
+                    desiredChecksum = * checksum;
+        ENetBuffer buffer;
+
+        * checksum = peer != NULL ? peer -> connectID : 0;
+
+        buffer.data = host -> receivedData;
+        buffer.dataLength = host -> receivedDataLength;
+
+        if (host -> checksum (& buffer, 1) != desiredChecksum)
+          return 0;
+    }
+       
+    if (peer != NULL)
+    {
+       peer -> address.host = host -> receivedAddress.host;
+       peer -> address.port = host -> receivedAddress.port;
+       peer -> incomingDataTotal += host -> receivedDataLength;
+    }
+    
+    currentData = host -> receivedData + headerSize;
+  
+    while (currentData < & host -> receivedData [host -> receivedDataLength])
+    {
+       enet_uint8 commandNumber;
+       size_t commandSize;
+
+       command = (ENetProtocol *) currentData;
+
+       if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength])
+         break;
+
+       commandNumber = command -> header.command & ENET_PROTOCOL_COMMAND_MASK;
+       if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT) 
+         break;
+       
+       commandSize = commandSizes [commandNumber];
+       if (commandSize == 0 || currentData + commandSize > & host -> receivedData [host -> receivedDataLength])
+         break;
+
+       currentData += commandSize;
+
+       if (peer == NULL && commandNumber != ENET_PROTOCOL_COMMAND_CONNECT)
+         break;
+         
+       command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber);
+
+       switch (commandNumber)
+       {
+       case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE:
+          if (enet_protocol_handle_acknowledge (host, event, peer, command))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_CONNECT:
+          if (peer != NULL)
+            goto commandError;
+          peer = enet_protocol_handle_connect (host, header, command);
+          if (peer == NULL)
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT:
+          if (enet_protocol_handle_verify_connect (host, event, peer, command))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_DISCONNECT:
+          if (enet_protocol_handle_disconnect (host, peer, command))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_PING:
+          if (enet_protocol_handle_ping (host, peer, command))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
+          if (enet_protocol_handle_send_reliable (host, peer, command, & currentData))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
+          if (enet_protocol_handle_send_unreliable (host, peer, command, & currentData))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
+          if (enet_protocol_handle_send_unsequenced (host, peer, command, & currentData))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
+          if (enet_protocol_handle_send_fragment (host, peer, command, & currentData))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT:
+          if (enet_protocol_handle_bandwidth_limit (host, peer, command))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE:
+          if (enet_protocol_handle_throttle_configure (host, peer, command))
+            goto commandError;
+          break;
+
+       case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT:
+          if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData))
+            goto commandError;
+          break;
+
+       default:
+          goto commandError;
+       }
+
+       if (peer != NULL &&
+           (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0)
+       {
+           enet_uint16 sentTime;
+
+           if (! (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME))
+             break;
+
+           sentTime = ENET_NET_TO_HOST_16 (header -> sentTime);
+
+           switch (peer -> state)
+           {
+           case ENET_PEER_STATE_DISCONNECTING:
+           case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
+           case ENET_PEER_STATE_DISCONNECTED:
+           case ENET_PEER_STATE_ZOMBIE:
+              break;
+
+           case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT:
+              if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT)
+                enet_peer_queue_acknowledgement (peer, command, sentTime);
+              break;
+
+           default:   
+              enet_peer_queue_acknowledgement (peer, command, sentTime);        
+              break;
+           }
+       }
+    }
+
+commandError:
+    if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
+      return 1;
+
+    return 0;
+}
+ 
+static int
+enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
+{
+    int packets;
+
+    for (packets = 0; packets < 256; ++ packets)
+    {
+       int receivedLength;
+       ENetBuffer buffer;
+
+       buffer.data = host -> packetData [0];
+       buffer.dataLength = sizeof (host -> packetData [0]);
+
+       receivedLength = enet_socket_receive (host -> socket,
+                                             & host -> receivedAddress,
+                                             & buffer,
+                                             1);
+
+       if (receivedLength < 0)
+         return -1;
+
+       if (receivedLength == 0)
+         return 0;
+
+       host -> receivedData = host -> packetData [0];
+       host -> receivedDataLength = receivedLength;
+      
+       host -> totalReceivedData += receivedLength;
+       host -> totalReceivedPackets ++;
+
+       if (host -> intercept != NULL)
+       {
+          switch (host -> intercept (host, event))
+          {
+          case 1:
+             if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
+               return 1;
+
+             continue;
+          
+          case -1:
+             return -1;
+        
+          default:
+             break;
+          }
+       }
+        
+       switch (enet_protocol_handle_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+       
+       case -1:
+          return -1;
+
+       default:
+          break;
+       }
+    }
+
+    return 0;
+}
+
+static void
+enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetAcknowledgement * acknowledgement;
+    ENetListIterator currentAcknowledgement;
+    enet_uint16 reliableSequenceNumber;
+ 
+    currentAcknowledgement = enet_list_begin (& peer -> acknowledgements);
+         
+    while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements))
+    {
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
+       {
+          host -> continueSending = 1;
+
+          break;
+       }
+
+       acknowledgement = (ENetAcknowledgement *) currentAcknowledgement;
+ 
+       currentAcknowledgement = enet_list_next (currentAcknowledgement);
+
+       buffer -> data = command;
+       buffer -> dataLength = sizeof (ENetProtocolAcknowledge);
+
+       host -> packetSize += buffer -> dataLength;
+
+       reliableSequenceNumber = ENET_HOST_TO_NET_16 (acknowledgement -> command.header.reliableSequenceNumber);
+  
+       command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE;
+       command -> header.channelID = acknowledgement -> command.header.channelID;
+       command -> header.reliableSequenceNumber = reliableSequenceNumber;
+       command -> acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber;
+       command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_16 (acknowledgement -> sentTime);
+  
+       if ((acknowledgement -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT)
+         enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
+
+       enet_list_remove (& acknowledgement -> acknowledgementList);
+       enet_free (acknowledgement);
+
+       ++ command;
+       ++ buffer;
+    }
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+}
+
+static int
+enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
+{
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand, insertPosition;
+
+    currentCommand = enet_list_begin (& peer -> sentReliableCommands);
+    insertPosition = enet_list_begin (& peer -> outgoingCommands);
+
+    while (currentCommand != enet_list_end (& peer -> sentReliableCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout)
+         continue;
+
+       if (peer -> earliestTimeout == 0 ||
+           ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout))
+         peer -> earliestTimeout = outgoingCommand -> sentTime;
+
+       if (peer -> earliestTimeout != 0 &&
+             (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum ||
+               (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit &&
+                 ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum)))
+       {
+          enet_protocol_notify_disconnect (host, peer, event);
+
+          return 1;
+       }
+
+       if (outgoingCommand -> packet != NULL)
+         peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
+          
+       ++ peer -> packetsLost;
+
+       outgoingCommand -> roundTripTimeout *= 2;
+
+       enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+       if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&
+           ! enet_list_empty (& peer -> sentReliableCommands))
+       {
+          outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+          peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
+       }
+    }
+    
+    return 0;
+}
+
+static int
+enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
+{
+    ENetProtocol * command = & host -> commands [host -> commandCount];
+    ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
+    ENetOutgoingCommand * outgoingCommand;
+    ENetListIterator currentCommand;
+    ENetChannel *channel;
+    enet_uint16 reliableWindow;
+    size_t commandSize;
+    int windowExceeded = 0, windowWrap = 0, canPing = 1;
+
+    currentCommand = enet_list_begin (& peer -> outgoingCommands);
+    
+    while (currentCommand != enet_list_end (& peer -> outgoingCommands))
+    {
+       outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+
+       if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+       {
+          channel = outgoingCommand -> command.header.channelID < peer -> channelCount ? & peer -> channels [outgoingCommand -> command.header.channelID] : NULL;
+          reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
+          if (channel != NULL)
+          {
+             if (! windowWrap &&      
+                  outgoingCommand -> sendAttempts < 1 && 
+                  ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) &&
+                  (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE ||
+                    channel -> usedReliableWindows & ((((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) << reliableWindow) |
+                      (((1 << (ENET_PEER_FREE_RELIABLE_WINDOWS + 2)) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow)))))
+                windowWrap = 1;
+             if (windowWrap)
+             {
+                currentCommand = enet_list_next (currentCommand);
+ 
+                continue;
+             }
+          }
+ 
+          if (outgoingCommand -> packet != NULL)
+          {
+             if (! windowExceeded)
+             {
+                enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
+             
+                if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
+                  windowExceeded = 1;
+             }
+             if (windowExceeded)
+             {
+                currentCommand = enet_list_next (currentCommand);
+
+                continue;
+             }
+          }
+
+          canPing = 0;
+       }
+
+       commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK];
+       if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
+           buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
+           peer -> mtu - host -> packetSize < commandSize ||
+           (outgoingCommand -> packet != NULL && 
+             (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength)))
+       {
+          host -> continueSending = 1;
+          
+          break;
+       }
+
+       currentCommand = enet_list_next (currentCommand);
+
+       if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
+       {
+          if (channel != NULL && outgoingCommand -> sendAttempts < 1)
+          {
+             channel -> usedReliableWindows |= 1 << reliableWindow;
+             ++ channel -> reliableWindows [reliableWindow];
+          }
+
+          ++ outgoingCommand -> sendAttempts;
+ 
+          if (outgoingCommand -> roundTripTimeout == 0)
+          {
+             outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
+             outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout;
+          }
+
+          if (enet_list_empty (& peer -> sentReliableCommands))
+            peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout;
+
+          enet_list_insert (enet_list_end (& peer -> sentReliableCommands),
+                            enet_list_remove (& outgoingCommand -> outgoingCommandList));
+
+          outgoingCommand -> sentTime = host -> serviceTime;
+
+          host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME;
+
+          peer -> reliableDataInTransit += outgoingCommand -> fragmentLength;
+       }
+       else
+       {
+          if (outgoingCommand -> packet != NULL && outgoingCommand -> fragmentOffset == 0)
+          {
+             peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER;
+             peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE;
+
+             if (peer -> packetThrottleCounter > peer -> packetThrottle)
+             {
+                enet_uint16 reliableSequenceNumber = outgoingCommand -> reliableSequenceNumber,
+                            unreliableSequenceNumber = outgoingCommand -> unreliableSequenceNumber;
+                for (;;)
+                {
+                   -- outgoingCommand -> packet -> referenceCount;
+
+                   if (outgoingCommand -> packet -> referenceCount == 0)
+                     enet_packet_destroy (outgoingCommand -> packet);
+
+                   enet_list_remove (& outgoingCommand -> outgoingCommandList);
+                   enet_free (outgoingCommand);
+
+                   if (currentCommand == enet_list_end (& peer -> outgoingCommands))
+                     break;
+
+                   outgoingCommand = (ENetOutgoingCommand *) currentCommand;
+                   if (outgoingCommand -> reliableSequenceNumber != reliableSequenceNumber ||
+                       outgoingCommand -> unreliableSequenceNumber != unreliableSequenceNumber)
+                     break;
+
+                   currentCommand = enet_list_next (currentCommand);
+                }
+
+                continue;
+             }
+          }
+
+          enet_list_remove (& outgoingCommand -> outgoingCommandList);
+
+          if (outgoingCommand -> packet != NULL)
+            enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand);
+       }
+
+       buffer -> data = command;
+       buffer -> dataLength = commandSize;
+
+       host -> packetSize += buffer -> dataLength;
+
+       * command = outgoingCommand -> command;
+
+       if (outgoingCommand -> packet != NULL)
+       {
+          ++ buffer;
+          
+          buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset;
+          buffer -> dataLength = outgoingCommand -> fragmentLength;
+
+          host -> packetSize += outgoingCommand -> fragmentLength;
+       }
+       else
+       if (! (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE))
+         enet_free (outgoingCommand);
+
+       ++ peer -> packetsSent;
+        
+       ++ command;
+       ++ buffer;
+    }
+
+    host -> commandCount = command - host -> commands;
+    host -> bufferCount = buffer - host -> buffers;
+
+    if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
+        enet_list_empty (& peer -> outgoingCommands) &&
+        enet_list_empty (& peer -> sentReliableCommands) &&
+        enet_list_empty (& peer -> sentUnreliableCommands))
+      enet_peer_disconnect (peer, peer -> eventData);
+
+    return canPing;
+}
+
+static int
+enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts)
+{
+    enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)];
+    ENetProtocolHeader * header = (ENetProtocolHeader *) headerData;
+    ENetPeer * currentPeer;
+    int sentLength;
+    size_t shouldCompress = 0;
+ 
+    host -> continueSending = 1;
+
+    while (host -> continueSending)
+    for (host -> continueSending = 0,
+           currentPeer = host -> peers;
+         currentPeer < & host -> peers [host -> peerCount];
+         ++ currentPeer)
+    {
+        if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED ||
+            currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
+          continue;
+
+        host -> headerFlags = 0;
+        host -> commandCount = 0;
+        host -> bufferCount = 1;
+        host -> packetSize = sizeof (ENetProtocolHeader);
+
+        if (! enet_list_empty (& currentPeer -> acknowledgements))
+          enet_protocol_send_acknowledgements (host, currentPeer);
+
+        if (checkForTimeouts != 0 &&
+            ! enet_list_empty (& currentPeer -> sentReliableCommands) &&
+            ENET_TIME_GREATER_EQUAL (host -> serviceTime, currentPeer -> nextTimeout) &&
+            enet_protocol_check_timeouts (host, currentPeer, event) == 1)
+        {
+            if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
+              return 1;
+            else
+              continue;
+        }
+
+        if ((enet_list_empty (& currentPeer -> outgoingCommands) ||
+              enet_protocol_check_outgoing_commands (host, currentPeer)) &&
+            enet_list_empty (& currentPeer -> sentReliableCommands) &&
+            ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval &&
+            currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
+        { 
+            enet_peer_ping (currentPeer);
+            enet_protocol_check_outgoing_commands (host, currentPeer);
+        }
+
+        if (host -> commandCount == 0)
+          continue;
+
+        if (currentPeer -> packetLossEpoch == 0)
+          currentPeer -> packetLossEpoch = host -> serviceTime;
+        else
+        if (ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL &&
+            currentPeer -> packetsSent > 0)
+        {
+           enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent;
+
+#ifdef ENET_DEBUG
+           printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0);
+#endif
+
+           currentPeer -> packetLossVariance = (currentPeer -> packetLossVariance * 3 + ENET_DIFFERENCE (packetLoss, currentPeer -> packetLoss)) / 4;
+           currentPeer -> packetLoss = (currentPeer -> packetLoss * 7 + packetLoss) / 8;
+
+           currentPeer -> packetLossEpoch = host -> serviceTime;
+           currentPeer -> packetsSent = 0;
+           currentPeer -> packetsLost = 0;
+        }
+
+        host -> buffers -> data = headerData;
+        if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)
+        {
+            header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF);
+
+            host -> buffers -> dataLength = sizeof (ENetProtocolHeader);
+        }
+        else
+          host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime;
+
+        shouldCompress = 0;
+        if (host -> compressor.context != NULL && host -> compressor.compress != NULL)
+        {
+            size_t originalSize = host -> packetSize - sizeof(ENetProtocolHeader),
+                   compressedSize = host -> compressor.compress (host -> compressor.context,
+                                        & host -> buffers [1], host -> bufferCount - 1,
+                                        originalSize,
+                                        host -> packetData [1],
+                                        originalSize);
+            if (compressedSize > 0 && compressedSize < originalSize)
+            {
+                host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED;
+                shouldCompress = compressedSize;
+#ifdef ENET_DEBUG_COMPRESS
+                printf ("peer %u: compressed %u -> %u (%u%%)\n", currentPeer -> incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize);
+#endif
+            }
+        }
+
+        if (currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID)
+          host -> headerFlags |= currentPeer -> outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT;
+        header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags);
+        if (host -> checksum != NULL)
+        {
+            enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength];
+            * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0;
+            host -> buffers -> dataLength += sizeof (enet_uint32);
+            * checksum = host -> checksum (host -> buffers, host -> bufferCount);
+        }
+
+        if (shouldCompress > 0)
+        {
+            host -> buffers [1].data = host -> packetData [1];
+            host -> buffers [1].dataLength = shouldCompress;
+            host -> bufferCount = 2;
+        }
+
+        currentPeer -> lastSendTime = host -> serviceTime;
+
+        sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount);
+
+        enet_protocol_remove_sent_unreliable_commands (currentPeer);
+
+        if (sentLength < 0)
+          return -1;
+
+        host -> totalSentData += sentLength;
+        host -> totalSentPackets ++;
+    }
+   
+    return 0;
+}
+
+/** Sends any queued packets on the host specified to its designated peers.
+
+    @param host   host to flush
+    @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service().
+    @ingroup host
+*/
+void
+enet_host_flush (ENetHost * host)
+{
+    host -> serviceTime = enet_time_get ();
+
+    enet_protocol_send_outgoing_commands (host, NULL, 0);
+}
+
+/** Checks for any queued events on the host and dispatches one if available.
+
+    @param host    host to check for events
+    @param event   an event structure where event details will be placed if available
+    @retval > 0 if an event was dispatched
+    @retval 0 if no events are available
+    @retval < 0 on failure
+    @ingroup host
+*/
+int
+enet_host_check_events (ENetHost * host, ENetEvent * event)
+{
+    if (event == NULL) return -1;
+
+    event -> type = ENET_EVENT_TYPE_NONE;
+    event -> peer = NULL;
+    event -> packet = NULL;
+
+    return enet_protocol_dispatch_incoming_commands (host, event);
+}
+
+/** Waits for events on the host specified and shuttles packets between
+    the host and its peers.
+
+    @param host    host to service
+    @param event   an event structure where event details will be placed if one occurs
+                   if event == NULL then no events will be delivered
+    @param timeout number of milliseconds that ENet should wait for events
+    @retval > 0 if an event occurred within the specified time limit
+    @retval 0 if no event occurred
+    @retval < 0 on failure
+    @remarks enet_host_service should be called fairly regularly for adequate performance
+    @ingroup host
+*/
+int
+enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout)
+{
+    enet_uint32 waitCondition;
+
+    if (event != NULL)
+    {
+        event -> type = ENET_EVENT_TYPE_NONE;
+        event -> peer = NULL;
+        event -> packet = NULL;
+
+        switch (enet_protocol_dispatch_incoming_commands (host, event))
+        {
+        case 1:
+            return 1;
+
+        case -1:
+#ifdef ENET_DEBUG
+            perror ("Error dispatching incoming packets");
+#endif
+
+            return -1;
+
+        default:
+            break;
+        }
+    }
+
+    host -> serviceTime = enet_time_get ();
+    
+    timeout += host -> serviceTime;
+
+    do
+    {
+       if (ENET_TIME_DIFFERENCE (host -> serviceTime, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
+         enet_host_bandwidth_throttle (host);
+
+       switch (enet_protocol_send_outgoing_commands (host, event, 1))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+#ifdef ENET_DEBUG
+          perror ("Error sending outgoing packets");
+#endif
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_receive_incoming_commands (host, event))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+#ifdef ENET_DEBUG
+          perror ("Error receiving incoming packets");
+#endif
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       switch (enet_protocol_send_outgoing_commands (host, event, 1))
+       {
+       case 1:
+          return 1;
+
+       case -1:
+#ifdef ENET_DEBUG
+          perror ("Error sending outgoing packets");
+#endif
+
+          return -1;
+
+       default:
+          break;
+       }
+
+       if (event != NULL)
+       {
+          switch (enet_protocol_dispatch_incoming_commands (host, event))
+          {
+          case 1:
+             return 1;
+
+          case -1:
+#ifdef ENET_DEBUG
+             perror ("Error dispatching incoming packets");
+#endif
+
+             return -1;
+
+          default:
+             break;
+          }
+       }
+
+       if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
+         return 0;
+
+       do
+       {
+          host -> serviceTime = enet_time_get ();
+
+          if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
+            return 0;
+
+          waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT;
+
+          if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0)
+            return -1;
+       }
+       while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT);
+
+       host -> serviceTime = enet_time_get ();
+    } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE);
+
+    return 0; 
+}
+
diff --git a/backends/networking/enet/source/protocol.h b/backends/networking/enet/source/protocol.h
new file mode 100644
index 00000000000..5a2970e188c
--- /dev/null
+++ b/backends/networking/enet/source/protocol.h
@@ -0,0 +1,198 @@
+/** 
+ @file  protocol.h
+ @brief ENet protocol
+*/
+#ifndef __ENET_PROTOCOL_H__
+#define __ENET_PROTOCOL_H__
+
+#include "types.h"
+
+enum
+{
+   ENET_PROTOCOL_MINIMUM_MTU             = 576,
+   ENET_PROTOCOL_MAXIMUM_MTU             = 4096,
+   ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32,
+   ENET_PROTOCOL_MINIMUM_WINDOW_SIZE     = 4096,
+   ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE     = 65536,
+   ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT   = 1,
+   ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT   = 255,
+   ENET_PROTOCOL_MAXIMUM_PEER_ID         = 0xFFF,
+   ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT  = 1024 * 1024
+};
+
+typedef enum _ENetProtocolCommand
+{
+   ENET_PROTOCOL_COMMAND_NONE               = 0,
+   ENET_PROTOCOL_COMMAND_ACKNOWLEDGE        = 1,
+   ENET_PROTOCOL_COMMAND_CONNECT            = 2,
+   ENET_PROTOCOL_COMMAND_VERIFY_CONNECT     = 3,
+   ENET_PROTOCOL_COMMAND_DISCONNECT         = 4,
+   ENET_PROTOCOL_COMMAND_PING               = 5,
+   ENET_PROTOCOL_COMMAND_SEND_RELIABLE      = 6,
+   ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE    = 7,
+   ENET_PROTOCOL_COMMAND_SEND_FRAGMENT      = 8,
+   ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED   = 9,
+   ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT    = 10,
+   ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11,
+   ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12,
+   ENET_PROTOCOL_COMMAND_COUNT              = 13,
+
+   ENET_PROTOCOL_COMMAND_MASK               = 0x0F
+} ENetProtocolCommand;
+
+typedef enum _ENetProtocolFlag
+{
+   ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7),
+   ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6),
+
+   ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14),
+   ENET_PROTOCOL_HEADER_FLAG_SENT_TIME  = (1 << 15),
+   ENET_PROTOCOL_HEADER_FLAG_MASK       = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME,
+
+   ENET_PROTOCOL_HEADER_SESSION_MASK    = (3 << 12),
+   ENET_PROTOCOL_HEADER_SESSION_SHIFT   = 12
+} ENetProtocolFlag;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#define ENET_PACKED
+#elif defined(__GNUC__) || defined(__clang__)
+#define ENET_PACKED __attribute__ ((packed))
+#else
+#define ENET_PACKED
+#endif
+
+typedef struct _ENetProtocolHeader
+{
+   enet_uint16 peerID;
+   enet_uint16 sentTime;
+} ENET_PACKED ENetProtocolHeader;
+
+typedef struct _ENetProtocolCommandHeader
+{
+   enet_uint8 command;
+   enet_uint8 channelID;
+   enet_uint16 reliableSequenceNumber;
+} ENET_PACKED ENetProtocolCommandHeader;
+
+typedef struct _ENetProtocolAcknowledge
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 receivedReliableSequenceNumber;
+   enet_uint16 receivedSentTime;
+} ENET_PACKED ENetProtocolAcknowledge;
+
+typedef struct _ENetProtocolConnect
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 outgoingPeerID;
+   enet_uint8  incomingSessionID;
+   enet_uint8  outgoingSessionID;
+   enet_uint32 mtu;
+   enet_uint32 windowSize;
+   enet_uint32 channelCount;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+   enet_uint32 connectID;
+   enet_uint32 data;
+} ENET_PACKED ENetProtocolConnect;
+
+typedef struct _ENetProtocolVerifyConnect
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 outgoingPeerID;
+   enet_uint8  incomingSessionID;
+   enet_uint8  outgoingSessionID;
+   enet_uint32 mtu;
+   enet_uint32 windowSize;
+   enet_uint32 channelCount;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+   enet_uint32 connectID;
+} ENET_PACKED ENetProtocolVerifyConnect;
+
+typedef struct _ENetProtocolBandwidthLimit
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 incomingBandwidth;
+   enet_uint32 outgoingBandwidth;
+} ENET_PACKED ENetProtocolBandwidthLimit;
+
+typedef struct _ENetProtocolThrottleConfigure
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 packetThrottleInterval;
+   enet_uint32 packetThrottleAcceleration;
+   enet_uint32 packetThrottleDeceleration;
+} ENET_PACKED ENetProtocolThrottleConfigure;
+
+typedef struct _ENetProtocolDisconnect
+{
+   ENetProtocolCommandHeader header;
+   enet_uint32 data;
+} ENET_PACKED ENetProtocolDisconnect;
+
+typedef struct _ENetProtocolPing
+{
+   ENetProtocolCommandHeader header;
+} ENET_PACKED ENetProtocolPing;
+
+typedef struct _ENetProtocolSendReliable
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 dataLength;
+} ENET_PACKED ENetProtocolSendReliable;
+
+typedef struct _ENetProtocolSendUnreliable
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 unreliableSequenceNumber;
+   enet_uint16 dataLength;
+} ENET_PACKED ENetProtocolSendUnreliable;
+
+typedef struct _ENetProtocolSendUnsequenced
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 unsequencedGroup;
+   enet_uint16 dataLength;
+} ENET_PACKED ENetProtocolSendUnsequenced;
+
+typedef struct _ENetProtocolSendFragment
+{
+   ENetProtocolCommandHeader header;
+   enet_uint16 startSequenceNumber;
+   enet_uint16 dataLength;
+   enet_uint32 fragmentCount;
+   enet_uint32 fragmentNumber;
+   enet_uint32 totalLength;
+   enet_uint32 fragmentOffset;
+} ENET_PACKED ENetProtocolSendFragment;
+
+typedef union _ENetProtocol
+{
+   ENetProtocolCommandHeader header;
+   ENetProtocolAcknowledge acknowledge;
+   ENetProtocolConnect connect;
+   ENetProtocolVerifyConnect verifyConnect;
+   ENetProtocolDisconnect disconnect;
+   ENetProtocolPing ping;
+   ENetProtocolSendReliable sendReliable;
+   ENetProtocolSendUnreliable sendUnreliable;
+   ENetProtocolSendUnsequenced sendUnsequenced;
+   ENetProtocolSendFragment sendFragment;
+   ENetProtocolBandwidthLimit bandwidthLimit;
+   ENetProtocolThrottleConfigure throttleConfigure;
+} ENET_PACKED ENetProtocol;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif
+
+#endif /* __ENET_PROTOCOL_H__ */
+
diff --git a/backends/networking/enet/source/time.h b/backends/networking/enet/source/time.h
new file mode 100644
index 00000000000..c82a5460351
--- /dev/null
+++ b/backends/networking/enet/source/time.h
@@ -0,0 +1,18 @@
+/** 
+ @file  time.h
+ @brief ENet time constants and macros
+*/
+#ifndef __ENET_TIME_H__
+#define __ENET_TIME_H__
+
+#define ENET_TIME_OVERFLOW 86400000
+
+#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW)
+#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b))
+#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b))
+
+#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b))
+
+#endif /* __ENET_TIME_H__ */
+
diff --git a/backends/networking/enet/source/types.h b/backends/networking/enet/source/types.h
new file mode 100644
index 00000000000..ab010a4b13d
--- /dev/null
+++ b/backends/networking/enet/source/types.h
@@ -0,0 +1,13 @@
+/** 
+ @file  types.h
+ @brief type definitions for ENet
+*/
+#ifndef __ENET_TYPES_H__
+#define __ENET_TYPES_H__
+
+typedef unsigned char enet_uint8;       /**< unsigned 8-bit type  */
+typedef unsigned short enet_uint16;     /**< unsigned 16-bit type */
+typedef unsigned int enet_uint32;      /**< unsigned 32-bit type */
+
+#endif /* __ENET_TYPES_H__ */
+
diff --git a/backends/networking/enet/source/unix.cpp b/backends/networking/enet/source/unix.cpp
new file mode 100644
index 00000000000..c0d74a2ddc2
--- /dev/null
+++ b/backends/networking/enet/source/unix.cpp
@@ -0,0 +1,611 @@
+/** 
+ @file  unix.c
+ @brief ENet Unix system specific functions
+*/
+#ifndef WIN32
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#define ENET_BUILDING_LIB 1
+#include "enet.h"
+
+#ifdef __APPLE__
+#ifdef HAS_POLL
+#undef HAS_POLL
+#endif
+#ifndef HAS_FCNTL
+#define HAS_FCNTL 1
+#endif
+#ifndef HAS_INET_PTON
+#define HAS_INET_PTON 1
+#endif
+#ifndef HAS_INET_NTOP
+#define HAS_INET_NTOP 1
+#endif
+#ifndef HAS_MSGHDR_FLAGS
+#define HAS_MSGHDR_FLAGS 1
+#endif
+#ifndef HAS_SOCKLEN_T
+#define HAS_SOCKLEN_T 1
+#endif
+#ifndef HAS_GETADDRINFO
+#define HAS_GETADDRINFO 1
+#endif
+#ifndef HAS_GETNAMEINFO
+#define HAS_GETNAMEINFO 1
+#endif
+#endif
+
+#ifdef HAS_FCNTL
+#include <fcntl.h>
+#endif
+
+#ifdef HAS_POLL
+#include <poll.h>
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+    return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+}
+
+enet_uint32
+enet_host_random_seed (void)
+{
+    return (enet_uint32) time (NULL);
+}
+
+enet_uint32
+enet_time_get (void)
+{
+    struct timeval timeVal;
+
+    gettimeofday (& timeVal, NULL);
+
+    return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+    struct timeval timeVal;
+
+    gettimeofday (& timeVal, NULL);
+    
+    timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase;
+}
+
+int
+enet_address_set_host_ip (ENetAddress * address, const char * name)
+{
+#ifdef HAS_INET_PTON
+    if (! inet_pton (AF_INET, name, & address -> host))
+#else
+    if (! inet_aton (name, (struct in_addr *) & address -> host))
+#endif
+        return -1;
+
+    return 0;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+#ifdef HAS_GETADDRINFO
+    struct addrinfo hints, * resultList = NULL, * result = NULL;
+
+    memset (& hints, 0, sizeof (hints));
+    hints.ai_family = AF_INET;
+
+    if (getaddrinfo (name, NULL, NULL, & resultList) != 0)
+      return -1;
+
+    for (result = resultList; result != NULL; result = result -> ai_next)
+    {
+        if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in))
+        {
+            struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr;
+
+            address -> host = sin -> sin_addr.s_addr;
+
+            freeaddrinfo (resultList);
+
+            return 0;
+        }
+    }
+
+    if (resultList != NULL)
+      freeaddrinfo (resultList);
+#else
+    struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYNAME_R
+    struct hostent hostData;
+    char buffer [2048];
+    int errnum;
+
+#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+    gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+    hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+    hostEntry = gethostbyname (name);
+#endif
+
+    if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET)
+    {
+        address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+        return 0;
+    }
+#endif
+
+    return enet_address_set_host_ip (address, name);
+}
+
+int
+enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
+{
+#ifdef HAS_INET_NTOP
+    if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL)
+#else
+    char * addr = inet_ntoa (* (struct in_addr *) const_cast<struct in_addr *>(reinterpret_cast<const struct in_addr *>(& address -> host)));
+    if (addr != NULL)
+    {
+        size_t addrLen = strlen(addr);
+        if (addrLen >= nameLength)
+          return -1;
+        memcpy (name, addr, addrLen + 1);
+    } 
+    else
+#endif
+        return -1;
+    return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+#ifdef HAS_GETNAMEINFO
+    struct sockaddr_in sin;
+    int err;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD);
+    if (! err)
+    {
+        if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength))
+          return -1;
+        return 0;
+    }
+    if (err != EAI_NONAME)
+      return -1;
+#else
+    struct in_addr in;
+    struct hostent * hostEntry = NULL;
+#ifdef HAS_GETHOSTBYADDR_R
+    struct hostent hostData;
+    char buffer [2048];
+    int errnum;
+
+    in.s_addr = address -> host;
+
+#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+    gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
+#else
+    hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum);
+#endif
+#else
+    in.s_addr = address -> host;
+
+    hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+#endif
+
+    if (hostEntry != NULL)
+    {
+       size_t hostLen = strlen (hostEntry -> h_name);
+       if (hostLen >= nameLength)
+         return -1;
+       memcpy (name, hostEntry -> h_name, hostLen + 1);
+       return 0;
+    }
+#endif
+
+    return enet_address_get_host_ip (address, name, nameLength);
+}
+
+int
+enet_socket_bind (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+
+    if (address != NULL)
+    {
+       sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+       sin.sin_addr.s_addr = address -> host;
+    }
+    else
+    {
+       sin.sin_port = 0;
+       sin.sin_addr.s_addr = INADDR_ANY;
+    }
+
+    return bind (socket,
+                 (struct sockaddr *) & sin,
+                 sizeof (struct sockaddr_in)); 
+}
+
+int
+enet_socket_get_address (ENetSocket socket, ENetAddress * address)
+{
+    struct sockaddr_in sin;
+    socklen_t sinLength = sizeof (struct sockaddr_in);
+
+    if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
+      return -1;
+
+    address -> host = (enet_uint32) sin.sin_addr.s_addr;
+    address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+
+    return 0;
+}
+
+int 
+enet_socket_listen (ENetSocket socket, int backlog)
+{
+    return listen (socket, backlog < 0 ? SOMAXCONN : backlog);
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type)
+{
+    return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+}
+
+int
+enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
+{
+    int result = -1;
+    switch (option)
+    {
+        case ENET_SOCKOPT_NONBLOCK:
+#ifdef HAS_FCNTL
+            result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK));
+#else
+            result = ioctl (socket, FIONBIO, & value);
+#endif
+            break;
+
+        case ENET_SOCKOPT_BROADCAST:
+            result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_REUSEADDR:
+            result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_RCVBUF:
+            result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_SNDBUF:
+            result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_RCVTIMEO:
+        {
+            struct timeval timeVal;
+            timeVal.tv_sec = value / 1000;
+            timeVal.tv_usec = (value % 1000) * 1000;
+            result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval));
+            break;
+        }
+
+        case ENET_SOCKOPT_SNDTIMEO:
+        {
+            struct timeval timeVal;
+            timeVal.tv_sec = value / 1000;
+            timeVal.tv_usec = (value % 1000) * 1000;
+            result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval));
+            break;
+        }
+
+        case ENET_SOCKOPT_NODELAY:
+            result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
+            break;
+
+        default:
+            break;
+    }
+    return result == -1 ? -1 : 0;
+}
+
+int
+enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
+{
+    int result = -1;
+    socklen_t len;
+    switch (option)
+    {
+        case ENET_SOCKOPT_ERROR:
+            len = sizeof (int);
+            result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len);
+            break;
+
+        default:
+            break;
+    }
+    return result == -1 ? -1 : 0;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+    int result;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+    if (result == -1 && errno == EINPROGRESS)
+      return 0;
+
+    return result;
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+    int result;
+    struct sockaddr_in sin;
+    socklen_t sinLength = sizeof (struct sockaddr_in);
+
+    result = accept (socket, 
+                     address != NULL ? (struct sockaddr *) & sin : NULL, 
+                     address != NULL ? & sinLength : NULL);
+    
+    if (result == -1)
+      return ENET_SOCKET_NULL;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return result;
+} 
+    
+int
+enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
+{
+    return shutdown (socket, (int) how);
+}
+
+void
+enet_socket_destroy (ENetSocket socket)
+{
+    if (socket != -1)
+      close (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+                  const ENetAddress * address,
+                  const ENetBuffer * buffers,
+                  size_t bufferCount)
+{
+    struct msghdr msgHdr;
+    struct sockaddr_in sin;
+    int sentLength;
+
+    memset (& msgHdr, 0, sizeof (struct msghdr));
+
+    if (address != NULL)
+    {
+        memset (& sin, 0, sizeof (struct sockaddr_in));
+
+        sin.sin_family = AF_INET;
+        sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+        sin.sin_addr.s_addr = address -> host;
+
+        msgHdr.msg_name = & sin;
+        msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+    }
+
+    msgHdr.msg_iov = const_cast<struct iovec *>(reinterpret_cast<const struct iovec *>(buffers));
+    msgHdr.msg_iovlen = bufferCount;
+
+    sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL);
+    
+    if (sentLength == -1)
+    {
+       if (errno == EWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+    return sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+                     ENetAddress * address,
+                     ENetBuffer * buffers,
+                     size_t bufferCount)
+{
+    struct msghdr msgHdr;
+    struct sockaddr_in sin;
+    int recvLength;
+
+    memset (& msgHdr, 0, sizeof (struct msghdr));
+
+    if (address != NULL)
+    {
+        msgHdr.msg_name = & sin;
+        msgHdr.msg_namelen = sizeof (struct sockaddr_in);
+    }
+
+    msgHdr.msg_iov = (struct iovec *) buffers;
+    msgHdr.msg_iovlen = bufferCount;
+
+    recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL);
+
+    if (recvLength == -1)
+    {
+       if (errno == EWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+#ifdef HAS_MSGHDR_FLAGS
+    if (msgHdr.msg_flags & MSG_TRUNC)
+      return -1;
+#endif
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return recvLength;
+}
+
+int
+enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
+{
+    struct timeval timeVal;
+
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+
+    return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+#ifdef HAS_POLL
+    struct pollfd pollSocket;
+    int pollCount;
+    
+    pollSocket.fd = socket;
+    pollSocket.events = 0;
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      pollSocket.events |= POLLOUT;
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      pollSocket.events |= POLLIN;
+
+    pollCount = poll (& pollSocket, 1, timeout);
+
+    if (pollCount < 0)
+    {
+        if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
+        {
+            * condition = ENET_SOCKET_WAIT_INTERRUPT;
+
+            return 0;
+        }
+
+        return -1;
+    }
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (pollCount == 0)
+      return 0;
+
+    if (pollSocket.revents & POLLOUT)
+      * condition |= ENET_SOCKET_WAIT_SEND;
+    
+    if (pollSocket.revents & POLLIN)
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+#else
+    fd_set readSet, writeSet;
+    struct timeval timeVal;
+    int selectCount;
+
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+
+    FD_ZERO (& readSet);
+    FD_ZERO (& writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      FD_SET (socket, & writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      FD_SET (socket, & readSet);
+
+    selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+    if (selectCount < 0)
+    {
+        if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
+        {
+            * condition = ENET_SOCKET_WAIT_INTERRUPT;
+
+            return 0;
+        }
+      
+        return -1;
+    }
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (selectCount == 0)
+      return 0;
+
+    if (FD_ISSET (socket, & writeSet))
+      * condition |= ENET_SOCKET_WAIT_SEND;
+
+    if (FD_ISSET (socket, & readSet))
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+#endif
+}
+
+#endif
+
diff --git a/backends/networking/enet/source/unix.h b/backends/networking/enet/source/unix.h
new file mode 100644
index 00000000000..b55be33103d
--- /dev/null
+++ b/backends/networking/enet/source/unix.h
@@ -0,0 +1,48 @@
+/** 
+ @file  unix.h
+ @brief ENet Unix header
+*/
+#ifndef __ENET_UNIX_H__
+#define __ENET_UNIX_H__
+
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+#ifdef MSG_MAXIOVLEN
+#define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN
+#endif
+
+typedef int ENetSocket;
+
+#define ENET_SOCKET_NULL -1
+
+#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */
+#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */
+#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */
+
+typedef struct
+{
+    void * data;
+    size_t dataLength;
+} ENetBuffer;
+
+#define ENET_CALLBACK
+
+#define ENET_API extern
+
+typedef fd_set ENetSocketSet;
+
+#define ENET_SOCKETSET_EMPTY(sockset)          FD_ZERO (& (sockset))
+#define ENET_SOCKETSET_ADD(sockset, socket)    FD_SET (socket, & (sockset))
+#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
+#define ENET_SOCKETSET_CHECK(sockset, socket)  FD_ISSET (socket, & (sockset))
+    
+#endif /* __ENET_UNIX_H__ */
+
diff --git a/backends/networking/enet/source/utility.h b/backends/networking/enet/source/utility.h
new file mode 100644
index 00000000000..b04bb7a5b35
--- /dev/null
+++ b/backends/networking/enet/source/utility.h
@@ -0,0 +1,13 @@
+/** 
+ @file  utility.h
+ @brief ENet utility header
+*/
+#ifndef __ENET_UTILITY_H__
+#define __ENET_UTILITY_H__
+
+#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y))
+#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y))
+#define ENET_DIFFERENCE(x, y) ((x) < (y) ? (y) - (x) : (x) - (y))
+
+#endif /* __ENET_UTILITY_H__ */
+
diff --git a/backends/networking/enet/source/win32.cpp b/backends/networking/enet/source/win32.cpp
new file mode 100644
index 00000000000..07d9e30ef35
--- /dev/null
+++ b/backends/networking/enet/source/win32.cpp
@@ -0,0 +1,442 @@
+/** 
+ @file  win32.c
+ @brief ENet Win32 system specific functions
+*/
+#ifdef WIN32
+
+#define ENET_BUILDING_LIB 1
+#include "enet.h"
+#include <windows.h>
+#include <mmsystem.h>
+
+static enet_uint32 timeBase = 0;
+
+int
+enet_initialize (void)
+{
+    WORD versionRequested = MAKEWORD (1, 1);
+    WSADATA wsaData;
+   
+    if (WSAStartup (versionRequested, & wsaData))
+       return -1;
+
+    if (LOBYTE (wsaData.wVersion) != 1||
+        HIBYTE (wsaData.wVersion) != 1)
+    {
+       WSACleanup ();
+       
+       return -1;
+    }
+
+    timeBeginPeriod (1);
+
+    return 0;
+}
+
+void
+enet_deinitialize (void)
+{
+    timeEndPeriod (1);
+
+    WSACleanup ();
+}
+
+enet_uint32
+enet_host_random_seed (void)
+{
+    return (enet_uint32) timeGetTime ();
+}
+
+enet_uint32
+enet_time_get (void)
+{
+    return (enet_uint32) timeGetTime () - timeBase;
+}
+
+void
+enet_time_set (enet_uint32 newTimeBase)
+{
+    timeBase = (enet_uint32) timeGetTime () - newTimeBase;
+}
+
+int
+enet_address_set_host_ip (ENetAddress * address, const char * name)
+{
+    enet_uint8 vals [4] = { 0, 0, 0, 0 };
+    int i;
+
+    for (i = 0; i < 4; ++ i)
+    {
+        const char * next = name + 1;
+        if (* name != '0')
+        {
+            long val = strtol (name, (char **) & next, 10);
+            if (val < 0 || val > 255 || next == name || next - name > 3)
+              return -1;
+            vals [i] = (enet_uint8) val;
+        }
+
+        if (* next != (i < 3 ? '.' : '\0'))
+          return -1;
+        name = next + 1;
+    }
+
+    memcpy (& address -> host, vals, sizeof (enet_uint32));
+    return 0;
+}
+
+int
+enet_address_set_host (ENetAddress * address, const char * name)
+{
+    struct hostent * hostEntry;
+
+    hostEntry = gethostbyname (name);
+    if (hostEntry == NULL ||
+        hostEntry -> h_addrtype != AF_INET)
+      return enet_address_set_host_ip (address, name);
+
+    address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
+
+    return 0;
+}
+
+int
+enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
+{
+    char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
+    if (addr == NULL)
+        return -1;
+    else
+    {
+        size_t addrLen = strlen(addr);
+        if (addrLen >= nameLength)
+          return -1;
+        memcpy (name, addr, addrLen + 1);
+    }
+    return 0;
+}
+
+int
+enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
+{
+    struct in_addr in;
+    struct hostent * hostEntry;
+ 
+    in.s_addr = address -> host;
+    
+    hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
+    if (hostEntry == NULL)
+      return enet_address_get_host_ip (address, name, nameLength);
+    else
+    {
+       size_t hostLen = strlen (hostEntry -> h_name);
+       if (hostLen >= nameLength)
+         return -1;
+       memcpy (name, hostEntry -> h_name, hostLen + 1);
+    }
+
+    return 0;
+}
+
+int
+enet_socket_bind (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+
+    if (address != NULL)
+    {
+       sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+       sin.sin_addr.s_addr = address -> host;
+    }
+    else
+    {
+       sin.sin_port = 0;
+       sin.sin_addr.s_addr = INADDR_ANY;
+    }
+
+    return bind (socket,
+                 (struct sockaddr *) & sin,
+                 sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0;
+}
+
+int
+enet_socket_get_address (ENetSocket socket, ENetAddress * address)
+{
+    struct sockaddr_in sin;
+    int sinLength = sizeof (struct sockaddr_in);
+
+    if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
+      return -1;
+
+    address -> host = (enet_uint32) sin.sin_addr.s_addr;
+    address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+
+    return 0;
+}
+
+int
+enet_socket_listen (ENetSocket socket, int backlog)
+{
+    return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0;
+}
+
+ENetSocket
+enet_socket_create (ENetSocketType type)
+{
+    return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
+}
+
+int
+enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
+{
+    int result = SOCKET_ERROR;
+    switch (option)
+    {
+        case ENET_SOCKOPT_NONBLOCK:
+        {
+            u_long nonBlocking = (u_long) value;
+            result = ioctlsocket (socket, FIONBIO, & nonBlocking);
+            break;
+        }
+
+        case ENET_SOCKOPT_BROADCAST:
+            result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_REUSEADDR:
+            result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_RCVBUF:
+            result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_SNDBUF:
+            result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_RCVTIMEO:
+            result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_SNDTIMEO:
+            result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int));
+            break;
+
+        case ENET_SOCKOPT_NODELAY:
+            result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
+            break;
+
+        default:
+            break;
+    }
+    return result == SOCKET_ERROR ? -1 : 0;
+}
+
+int
+enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
+{
+    int result = SOCKET_ERROR, len;
+    switch (option)
+    {
+        case ENET_SOCKOPT_ERROR:
+            len = sizeof(int);
+            result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len);
+            break;
+
+        default:
+            break;
+    }
+    return result == SOCKET_ERROR ? -1 : 0;
+}
+
+int
+enet_socket_connect (ENetSocket socket, const ENetAddress * address)
+{
+    struct sockaddr_in sin;
+    int result;
+
+    memset (& sin, 0, sizeof (struct sockaddr_in));
+
+    sin.sin_family = AF_INET;
+    sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+    sin.sin_addr.s_addr = address -> host;
+
+    result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
+    if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK)
+      return -1;
+
+    return 0;
+}
+
+ENetSocket
+enet_socket_accept (ENetSocket socket, ENetAddress * address)
+{
+    SOCKET result;
+    struct sockaddr_in sin;
+    int sinLength = sizeof (struct sockaddr_in);
+
+    result = accept (socket, 
+                     address != NULL ? (struct sockaddr *) & sin : NULL, 
+                     address != NULL ? & sinLength : NULL);
+
+    if (result == INVALID_SOCKET)
+      return ENET_SOCKET_NULL;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return result;
+}
+
+int
+enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
+{
+    return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0;
+}
+
+void
+enet_socket_destroy (ENetSocket socket)
+{
+    if (socket != INVALID_SOCKET)
+      closesocket (socket);
+}
+
+int
+enet_socket_send (ENetSocket socket,
+                  const ENetAddress * address,
+                  const ENetBuffer * buffers,
+                  size_t bufferCount)
+{
+    struct sockaddr_in sin;
+    DWORD sentLength = 0;
+
+    if (address != NULL)
+    {
+        memset (& sin, 0, sizeof (struct sockaddr_in));
+
+        sin.sin_family = AF_INET;
+        sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
+        sin.sin_addr.s_addr = address -> host;
+    }
+
+    if (WSASendTo (socket, 
+                   (LPWSABUF) buffers,
+                   (DWORD) bufferCount,
+                   & sentLength,
+                   0,
+                   address != NULL ? (struct sockaddr *) & sin : NULL,
+                   address != NULL ? sizeof (struct sockaddr_in) : 0,
+                   NULL,
+                   NULL) == SOCKET_ERROR)
+    {
+       if (WSAGetLastError () == WSAEWOULDBLOCK)
+         return 0;
+
+       return -1;
+    }
+
+    return (int) sentLength;
+}
+
+int
+enet_socket_receive (ENetSocket socket,
+                     ENetAddress * address,
+                     ENetBuffer * buffers,
+                     size_t bufferCount)
+{
+    INT sinLength = sizeof (struct sockaddr_in);
+    DWORD flags = 0,
+          recvLength = 0;
+    struct sockaddr_in sin;
+
+    if (WSARecvFrom (socket,
+                     (LPWSABUF) buffers,
+                     (DWORD) bufferCount,
+                     & recvLength,
+                     & flags,
+                     address != NULL ? (struct sockaddr *) & sin : NULL,
+                     address != NULL ? & sinLength : NULL,
+                     NULL,
+                     NULL) == SOCKET_ERROR)
+    {
+       switch (WSAGetLastError ())
+       {
+       case WSAEWOULDBLOCK:
+       case WSAECONNRESET:
+          return 0;
+       }
+
+       return -1;
+    }
+
+    if (flags & MSG_PARTIAL)
+      return -1;
+
+    if (address != NULL)
+    {
+        address -> host = (enet_uint32) sin.sin_addr.s_addr;
+        address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
+    }
+
+    return (int) recvLength;
+}
+
+int
+enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
+{
+    struct timeval timeVal;
+
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+
+    return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
+}
+
+int
+enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
+{
+    fd_set readSet, writeSet;
+    struct timeval timeVal;
+    int selectCount;
+    
+    timeVal.tv_sec = timeout / 1000;
+    timeVal.tv_usec = (timeout % 1000) * 1000;
+    
+    FD_ZERO (& readSet);
+    FD_ZERO (& writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_SEND)
+      FD_SET (socket, & writeSet);
+
+    if (* condition & ENET_SOCKET_WAIT_RECEIVE)
+      FD_SET (socket, & readSet);
+
+    selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
+
+    if (selectCount < 0)
+      return -1;
+
+    * condition = ENET_SOCKET_WAIT_NONE;
+
+    if (selectCount == 0)
+      return 0;
+
+    if (FD_ISSET (socket, & writeSet))
+      * condition |= ENET_SOCKET_WAIT_SEND;
+    
+    if (FD_ISSET (socket, & readSet))
+      * condition |= ENET_SOCKET_WAIT_RECEIVE;
+
+    return 0;
+} 
+
+#endif
+
diff --git a/backends/networking/enet/source/win32.h b/backends/networking/enet/source/win32.h
new file mode 100644
index 00000000000..e73ca9d052b
--- /dev/null
+++ b/backends/networking/enet/source/win32.h
@@ -0,0 +1,57 @@
+/** 
+ @file  win32.h
+ @brief ENet Win32 header
+*/
+#ifndef __ENET_WIN32_H__
+#define __ENET_WIN32_H__
+
+#ifdef _MSC_VER
+#ifdef ENET_BUILDING_LIB
+#pragma warning (disable: 4267) // size_t to int conversion
+#pragma warning (disable: 4244) // 64bit to 32bit int
+#pragma warning (disable: 4018) // signed/unsigned mismatch
+#pragma warning (disable: 4146) // unary minus operator applied to unsigned type
+#endif
+#endif
+
+#include <stdlib.h>
+#include <winsock2.h>
+
+typedef SOCKET ENetSocket;
+
+#define ENET_SOCKET_NULL INVALID_SOCKET
+
+#define ENET_HOST_TO_NET_16(value) (htons (value))
+#define ENET_HOST_TO_NET_32(value) (htonl (value))
+
+#define ENET_NET_TO_HOST_16(value) (ntohs (value))
+#define ENET_NET_TO_HOST_32(value) (ntohl (value))
+
+typedef struct
+{
+    size_t dataLength;
+    void * data;
+} ENetBuffer;
+
+#define ENET_CALLBACK __cdecl
+
+#ifdef ENET_DLL
+#ifdef ENET_BUILDING_LIB
+#define ENET_API __declspec( dllexport )
+#else
+#define ENET_API __declspec( dllimport )
+#endif /* ENET_BUILDING_LIB */
+#else /* !ENET_DLL */
+#define ENET_API extern
+#endif /* ENET_DLL */
+
+typedef fd_set ENetSocketSet;
+
+#define ENET_SOCKETSET_EMPTY(sockset)          FD_ZERO (& (sockset))
+#define ENET_SOCKETSET_ADD(sockset, socket)    FD_SET (socket, & (sockset))
+#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
+#define ENET_SOCKETSET_CHECK(sockset, socket)  FD_ISSET (socket, & (sockset))
+
+#endif /* __ENET_WIN32_H__ */
+
+
diff --git a/configure b/configure
index 21ae699d043..ad8c09dbe4f 100755
--- a/configure
+++ b/configure
@@ -138,7 +138,7 @@ _ogg=auto
 _vorbis=auto
 _sdlnet=auto
 _libcurl=auto
-_enet=auto
+_enet=yes
 _tremor=auto
 _tremolo=no
 _flac=auto
@@ -1053,8 +1053,7 @@ Optional Libraries:
   --with-libcurl-prefix=DIR    prefix where libcurl is installed (optional)
   --disable-libcurl        disable libcurl networking library [autodetect]
 
-  --with-enet-prefix=DIR prefix where ENet is installed (optional)
-  --disable-enet         disable ENet networking library [autodetect]
+  --disable-enet         don't enable ENet networking support
 
   --with-discord-prefix=DIR   prefix where discord-rpc is installed (optional)
   --disable-discord       disable Discord rich presence integration [autodetect]
@@ -1373,11 +1372,6 @@ for ac_option in $@; do
 		SDL_NET_CFLAGS="-I$arg/include"
 		SDL_NET_LIBS="-L$arg/lib"
 		;;
-	--with-enet-prefix=*)
-		arg=`echo $ac_option | cut -d '=' -f 2`
-		ENET_CFLAGS="-I$arg/include"
-		ENET_LIBS="-L$arg/lib"
-		;;
 	--with-opengl-prefix=*)
 		arg=`echo $ac_option | cut -d '=' -f 2`
 		OPENGL_CFLAGS="-I$arg/include"
@@ -4185,29 +4179,10 @@ EOF
 	echo "$_sdlnet"
 fi
 
-if test "$_enet" = auto ; then
-	if test "$_pkg_config" = "yes" && $_pkgconfig --exists libenet; then
-		append_var ENET_LIBS "`$_pkgconfig --libs libenet`"
-		append_var ENET_CFLAGS "`$_pkgconfig --cflags libenet`"
-	else
-		append_var ENET_LIBS "-lenet"
-	fi
-
-	# Check for ENet
-	echocheck "ENet"
-	_enet=no
-	cat > $TMPC << EOF
-#include <enet/enet.h>
-int main(int argc, char *argv[]) { enet_initialize(); return 0; }
-EOF
-	cc_check $ENET_LIBS $LIBS $INCLUDES $ENET_CFLAGS && _enet=yes
-	if test "$_enet" = yes ; then
-		append_var LIBS "$ENET_LIBS"
-		append_var INCLUDES "$ENET_CFLAGS"
-	fi
-	define_in_config_if_yes "$_enet" 'USE_ENET'
-	echo "$_enet"
-fi
+#
+# Check whether ENet networking support is requested
+#
+define_in_config_if_yes "$_enet" 'USE_ENET'
 
 #
 # Enable 16bit support only for backends which support it
@@ -6583,7 +6558,10 @@ if test "$_eventrec" = yes ; then
 fi
 
 if test "$_cloud" = yes ; then
-	echo ", cloud"
+	echo_n ", cloud"
+fi
+if test "$_enet" = yes ; then
+	echo ", ENet"
 else
 	echo
 fi
diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp
index f93bd51d63e..44dd5ea1a68 100644
--- a/devtools/create_project/create_project.cpp
+++ b/devtools/create_project/create_project.cpp
@@ -1078,7 +1078,6 @@ const Feature s_features[] = {
 	{ "fluidlite",   "USE_FLUIDLITE", true, false, "FluidLite support" },
 	{   "libcurl",     "USE_LIBCURL", true, true,  "libcurl support" },
 	{    "sdlnet",     "USE_SDL_NET", true, true,  "SDL_net support" },
-	{      "enet",        "USE_ENET", true, true,  "ENet support" },
 	{   "discord",     "USE_DISCORD", true, false, "Discord support" },
 	{ "retrowave",   "USE_RETROWAVE", true, false, "RetroWave OPL3 support" },
 
@@ -1099,6 +1098,7 @@ const Feature s_features[] = {
 	{   "opengl_shaders",            "USE_OPENGL_SHADERS", false, true,  "OpenGL support (shaders) in 3d games" },
 	{          "taskbar",                   "USE_TASKBAR", false, true,  "Taskbar integration support" },
 	{            "cloud",                     "USE_CLOUD", false, true,  "Cloud integration support" },
+	{            "enet",                       "USE_ENET", false, true,  "ENet networking support" },
 	{      "translation",               "USE_TRANSLATION", false, true,  "Translation support" },
 	{           "vkeybd",                 "ENABLE_VKEYBD", false, false, "Virtual keyboard support"},
 	{    "eventrecorder",          "ENABLE_EVENTRECORDER", false, false, "Event recorder support"},
diff --git a/devtools/create_project/msvc.cpp b/devtools/create_project/msvc.cpp
index bc8031d135a..29c504181a0 100644
--- a/devtools/create_project/msvc.cpp
+++ b/devtools/create_project/msvc.cpp
@@ -74,7 +74,6 @@ std::string MSVCProvider::getLibraryFromFeature(const char *feature, const Build
 		{   "libcurl", "libcurl.lib",               "libcurl-d.lib", "ws2_32.lib wldap32.lib crypt32.lib normaliz.lib", nullptr },
 		{    "sdlnet", "SDL_net.lib",               nullptr,         "iphlpapi.lib",                                    nullptr },
 		{   "sdl2net", "SDL2_net.lib",              nullptr,         "iphlpapi.lib",                                    "SDL_net.lib" },
-		{      "enet", "enet.lib",                  nullptr,         "winmm.lib ws2_32.lib",                            nullptr },
 		{   "discord", "discord-rpc.lib",           nullptr,         nullptr,                                           nullptr },
 		{ "retrowave", "retrowave.lib",             nullptr,         nullptr,                                           nullptr },
 		// Feature flags with library dependencies


Commit: cef3054212bb07e43f06d32496c079044ab7b25a
    https://github.com/scummvm/scummvm/commit/cef3054212bb07e43f06d32496c079044ab7b25a
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Check and set additional defines.

Changed paths:
    backends/networking/enet/source/unix.cpp
    configure


diff --git a/backends/networking/enet/source/unix.cpp b/backends/networking/enet/source/unix.cpp
index c0d74a2ddc2..bd5083fa817 100644
--- a/backends/networking/enet/source/unix.cpp
+++ b/backends/networking/enet/source/unix.cpp
@@ -53,6 +53,10 @@
 #include <poll.h>
 #endif
 
+#if !defined(HAS_SOCKLEN_T) && !defined(__socklen_t_defined)
+typedef int socklen_t;
+#endif
+
 #ifndef MSG_NOSIGNAL
 #define MSG_NOSIGNAL 0
 #endif
diff --git a/configure b/configure
index ad8c09dbe4f..728750341d2 100755
--- a/configure
+++ b/configure
@@ -4184,6 +4184,156 @@ fi
 #
 define_in_config_if_yes "$_enet" 'USE_ENET'
 
+
+#
+# Check and set additional defines needed for ENet.
+#
+if test "$_enet" = yes ; then
+	echo "Checks for ENet..."
+
+	echo_n "   "
+	echocheck "getaddrinfo"
+	cat > $TMPC << EOF
+#include <netdb.h>
+int main(void) { return getaddrinfo(0, 0, 0, 0); } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_GETADDRINFO"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "getnameinfo"
+	cat > $TMPC << EOF
+#include <netdb.h>
+int main(void) { return getnameinfo(0, 0, 0, 0, 0, 0, 0); } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_GETNAMEINFO"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "gethostbyaddr_r"
+	cat > $TMPC << EOF
+#include <netdb.h>
+int main(void) { return gethostbyaddr_r(0, 0, 0, 0, 0, 0, 0, 0); } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_GETHOSTBYADDR_R"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "gethostbyname_r"
+	cat > $TMPC << EOF
+#include <netdb.h>
+int main(void) { return gethostbyname_r(0, 0, 0, 0, 0, 0); } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_GETHOSTBYNAME_R"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "poll"
+	cat > $TMPC << EOF
+#include <poll.h>
+int main(void) { return poll(0, 0, 0); } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_POLL"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "fcntl"
+	cat > $TMPC << EOF
+#include <fcntl.h>
+int main(void) { return fcntl(0, 0); } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_FCNTL"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "inet_pton"
+	cat > $TMPC << EOF
+#include <arpa/inet.h>
+int main(void) { return inet_pton(0, 0, 0); } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_INET_PTON"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "inet_ntop"
+	cat > $TMPC << EOF
+#include <arpa/inet.h>
+int main(void) { inet_ntop(0, 0, 0, 0); return 0; } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_INET_NTOP"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "msghdr.msg_flags"
+	cat > $TMPC << EOF
+#include <sys/socket.h>
+struct msghdr msg;
+int main(void) { return msg.msg_flags; } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_MSGHDR_FLAGS"
+		echo "yes"
+	else
+		echo "no"
+	fi
+
+	echo_n "   "
+	echocheck "socklen_t"
+	cat > $TMPC << EOF
+#include <sys/socket.h>
+int main(void) { socklen_t len = 0; return 0; } 
+EOF
+	cc_check
+	if test "$TMPR" -eq 0; then
+		append_var DEFINES "-DHAS_SOCKLEN_T"
+		echo "yes"
+	else
+		echo "no"
+	fi
+fi
+
+
 #
 # Enable 16bit support only for backends which support it
 #


Commit: bd1872c2abe91083514ea604a9a7dd93b7fb8255
    https://github.com/scummvm/scummvm/commit/bd1872c2abe91083514ea604a9a7dd93b7fb8255
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Now compiles on Windows.

Changed paths:
    backends/networking/enet/source/protocol.cpp
    backends/networking/enet/source/win32.cpp
    configure
    devtools/create_project/msvc.cpp


diff --git a/backends/networking/enet/source/protocol.cpp b/backends/networking/enet/source/protocol.cpp
index de55a083535..592a662407f 100644
--- a/backends/networking/enet/source/protocol.cpp
+++ b/backends/networking/enet/source/protocol.cpp
@@ -290,7 +290,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet
     ENetChannel * channel;
     size_t channelCount, duplicatePeers = 0;
     ENetPeer * currentPeer, * peer = NULL;
-    ENetProtocol verifyCommand;
+    ENetProtocol verifyCommand {};
 
     channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount);
 
@@ -1062,7 +1062,7 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event)
     {
         enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)],
                     desiredChecksum = * checksum;
-        ENetBuffer buffer;
+        ENetBuffer buffer {};
 
         * checksum = peer != NULL ? peer -> connectID : 0;
 
@@ -1221,7 +1221,7 @@ enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
     for (packets = 0; packets < 256; ++ packets)
     {
        int receivedLength;
-       ENetBuffer buffer;
+       ENetBuffer buffer {};
 
        buffer.data = host -> packetData [0];
        buffer.dataLength = sizeof (host -> packetData [0]);
@@ -1390,8 +1390,8 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
     ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
     ENetOutgoingCommand * outgoingCommand;
     ENetListIterator currentCommand;
-    ENetChannel *channel;
-    enet_uint16 reliableWindow;
+    ENetChannel *channel = 0;
+    enet_uint16 reliableWindow = 0;
     size_t commandSize;
     int windowExceeded = 0, windowWrap = 0, canPing = 1;
 
@@ -1567,7 +1567,7 @@ enet_protocol_check_outgoing_commands (ENetHost * host, ENetPeer * peer)
 static int
 enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts)
 {
-    enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)];
+	  enet_uint8 headerData [sizeof(ENetProtocolHeader) + sizeof(enet_uint32)] {};
     ENetProtocolHeader * header = (ENetProtocolHeader *) headerData;
     ENetPeer * currentPeer;
     int sentLength;
diff --git a/backends/networking/enet/source/win32.cpp b/backends/networking/enet/source/win32.cpp
index 07d9e30ef35..c19bb1aee59 100644
--- a/backends/networking/enet/source/win32.cpp
+++ b/backends/networking/enet/source/win32.cpp
@@ -70,7 +70,7 @@ enet_address_set_host_ip (ENetAddress * address, const char * name)
         const char * next = name + 1;
         if (* name != '0')
         {
-            long val = strtol (name, (char **) & next, 10);
+            long val = strtol(name, const_cast<char **>(&next), 10);
             if (val < 0 || val > 255 || next == name || next - name > 3)
               return -1;
             vals [i] = (enet_uint8) val;
@@ -103,7 +103,7 @@ enet_address_set_host (ENetAddress * address, const char * name)
 int
 enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
 {
-    char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
+    char *addr = inet_ntoa(*(struct in_addr *)const_cast<struct in_addr *>(reinterpret_cast<const struct in_addr *>(&address->host)));
     if (addr == NULL)
         return -1;
     else
@@ -119,7 +119,7 @@ enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameL
 int
 enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
 {
-    struct in_addr in;
+    struct in_addr in {};
     struct hostent * hostEntry;
  
     in.s_addr = address -> host;
@@ -166,7 +166,7 @@ enet_socket_bind (ENetSocket socket, const ENetAddress * address)
 int
 enet_socket_get_address (ENetSocket socket, ENetAddress * address)
 {
-    struct sockaddr_in sin;
+    struct sockaddr_in sin {};
     int sinLength = sizeof (struct sockaddr_in);
 
     if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
@@ -277,7 +277,7 @@ ENetSocket
 enet_socket_accept (ENetSocket socket, ENetAddress * address)
 {
     SOCKET result;
-    struct sockaddr_in sin;
+    struct sockaddr_in sin {};
     int sinLength = sizeof (struct sockaddr_in);
 
     result = accept (socket, 
@@ -328,7 +328,7 @@ enet_socket_send (ENetSocket socket,
     }
 
     if (WSASendTo (socket, 
-                   (LPWSABUF) buffers,
+                   reinterpret_cast<LPWSABUF>(const_cast<ENetBuffer *>(buffers)),
                    (DWORD) bufferCount,
                    & sentLength,
                    0,
@@ -355,7 +355,7 @@ enet_socket_receive (ENetSocket socket,
     INT sinLength = sizeof (struct sockaddr_in);
     DWORD flags = 0,
           recvLength = 0;
-    struct sockaddr_in sin;
+    struct sockaddr_in sin {};
 
     if (WSARecvFrom (socket,
                      (LPWSABUF) buffers,
@@ -392,7 +392,7 @@ enet_socket_receive (ENetSocket socket,
 int
 enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
 {
-    struct timeval timeVal;
+    struct timeval timeVal {};
 
     timeVal.tv_sec = timeout / 1000;
     timeVal.tv_usec = (timeout % 1000) * 1000;
@@ -403,8 +403,8 @@ enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocket
 int
 enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
 {
-    fd_set readSet, writeSet;
-    struct timeval timeVal;
+    fd_set readSet {}, writeSet{};
+    struct timeval timeVal {};
     int selectCount;
     
     timeVal.tv_sec = timeout / 1000;
diff --git a/configure b/configure
index 728750341d2..34c8a3fe697 100755
--- a/configure
+++ b/configure
@@ -4184,156 +4184,164 @@ fi
 #
 define_in_config_if_yes "$_enet" 'USE_ENET'
 
-
 #
-# Check and set additional defines needed for ENet.
+# Check and set additional stuff needed for ENet.
 #
 if test "$_enet" = yes ; then
-	echo "Checks for ENet..."
+	case "$_host_os" in
+		mingw*)
+			# Additional libraries.
+			append_var LIBS "-lwinmm -lws2_32"
+			;;
 
-	echo_n "   "
-	echocheck "getaddrinfo"
-	cat > $TMPC << EOF
+		*)
+			# Check for features and define.
+			echo "Checks for ENet..."
+
+			echo_n "   "
+			echocheck "getaddrinfo"
+			cat > $TMPC << EOF
 #include <netdb.h>
 int main(void) { return getaddrinfo(0, 0, 0, 0); } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_GETADDRINFO"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_GETADDRINFO"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "getnameinfo"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "getnameinfo"
+			cat > $TMPC << EOF
 #include <netdb.h>
 int main(void) { return getnameinfo(0, 0, 0, 0, 0, 0, 0); } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_GETNAMEINFO"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_GETNAMEINFO"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "gethostbyaddr_r"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "gethostbyaddr_r"
+			cat > $TMPC << EOF
 #include <netdb.h>
 int main(void) { return gethostbyaddr_r(0, 0, 0, 0, 0, 0, 0, 0); } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_GETHOSTBYADDR_R"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_GETHOSTBYADDR_R"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "gethostbyname_r"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "gethostbyname_r"
+			cat > $TMPC << EOF
 #include <netdb.h>
 int main(void) { return gethostbyname_r(0, 0, 0, 0, 0, 0); } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_GETHOSTBYNAME_R"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_GETHOSTBYNAME_R"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "poll"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "poll"
+			cat > $TMPC << EOF
 #include <poll.h>
 int main(void) { return poll(0, 0, 0); } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_POLL"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_POLL"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "fcntl"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "fcntl"
+			cat > $TMPC << EOF
 #include <fcntl.h>
 int main(void) { return fcntl(0, 0); } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_FCNTL"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_FCNTL"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "inet_pton"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "inet_pton"
+			cat > $TMPC << EOF
 #include <arpa/inet.h>
 int main(void) { return inet_pton(0, 0, 0); } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_INET_PTON"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_INET_PTON"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "inet_ntop"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "inet_ntop"
+			cat > $TMPC << EOF
 #include <arpa/inet.h>
 int main(void) { inet_ntop(0, 0, 0, 0); return 0; } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_INET_NTOP"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_INET_NTOP"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "msghdr.msg_flags"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "msghdr.msg_flags"
+			cat > $TMPC << EOF
 #include <sys/socket.h>
 struct msghdr msg;
 int main(void) { return msg.msg_flags; } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_MSGHDR_FLAGS"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_MSGHDR_FLAGS"
+				echo "yes"
+			else
+				echo "no"
+			fi
 
-	echo_n "   "
-	echocheck "socklen_t"
-	cat > $TMPC << EOF
+			echo_n "   "
+			echocheck "socklen_t"
+			cat > $TMPC << EOF
 #include <sys/socket.h>
 int main(void) { socklen_t len = 0; return 0; } 
 EOF
-	cc_check
-	if test "$TMPR" -eq 0; then
-		append_var DEFINES "-DHAS_SOCKLEN_T"
-		echo "yes"
-	else
-		echo "no"
-	fi
+			cc_check
+			if test "$TMPR" -eq 0; then
+				append_var DEFINES "-DHAS_SOCKLEN_T"
+				echo "yes"
+			else
+				echo "no"
+			fi
+			;;
+	esac
 fi
 
-
 #
 # Enable 16bit support only for backends which support it
 #
diff --git a/devtools/create_project/msvc.cpp b/devtools/create_project/msvc.cpp
index 29c504181a0..0a755d45b01 100644
--- a/devtools/create_project/msvc.cpp
+++ b/devtools/create_project/msvc.cpp
@@ -79,7 +79,8 @@ std::string MSVCProvider::getLibraryFromFeature(const char *feature, const Build
 		// Feature flags with library dependencies
 		{   "updates", "winsparkle.lib",            nullptr,         nullptr,                                           nullptr },
 		{       "tts", nullptr,                     nullptr,         "sapi.lib",                                        nullptr },
-		{    "opengl", nullptr,                     nullptr,         "opengl32.lib",                                    nullptr }
+		{    "opengl", nullptr,                     nullptr,         "opengl32.lib",                                    nullptr },
+		{      "enet", nullptr,                     nullptr,         "winmm.lib ws2_32.lib",                            nullptr }
 	};
 
 	// HACK for switching SDL_net to SDL2_net


Commit: cec2134a2d81ffdef1e814b30d5ce1666b48c903
    https://github.com/scummvm/scummvm/commit/cec2134a2d81ffdef1e814b30d5ce1666b48c903
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Network play options.

Changed paths:
    engines/scumm/dialogs.cpp
    engines/scumm/dialogs.h
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h
    engines/scumm/metaengine.cpp


diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 551cd13daee..d7250031c7c 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -31,6 +31,7 @@
 
 #include "gui/gui-manager.h"
 #include "gui/widget.h"
+#include "gui/widgets/edittext.h"
 #include "gui/ThemeEval.h"
 
 #include "scumm/dialogs.h"
@@ -1347,4 +1348,96 @@ void MI1CdGameOptionsWidget::updateOutlookAdjustmentValue() {
 	updateAdjustmentSlider(_outlookAdjustmentSlider, _outlookAdjustmentValue);
 }
 
+#ifdef USE_ENET
+// HE Network Play Adjustment settings
+
+HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
+		ScummOptionsContainerWidget(boss, name, "HENetworkGameOptionsDialog", domain) {
+	Common::String extra = ConfMan.get("extra", domain);
+
+	GUI::StaticTextWidget *text = new GUI::StaticTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.SessionServerLabel", _("Multiplayer Server:"));
+
+	text->setAlign(Graphics::TextAlign::kTextAlignEnd);
+
+	_enableSessionServer = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.EnableSessionServer", _("Enable connection to Multiplayer Server"), _("Toggles the connection to the server that allows hosting and joining online multiplayer games over the Internet."), kEnableSessionCmd);
+	_enableLANBroadcast = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.EnableLANBroadcast", _("Host games over LAN"), _("Allows the game sessions to be discovered over your local area network."));
+
+	_sessionServerAddr = new GUI::EditTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.SessionServerAddress", Common::U32String(""), _("Address of the server to connect to for hosting and joining online game sessions."));
+
+	_serverResetButton = new GUI::ButtonWidget(widgetsBoss(), "HENetworkGameOptionsDialog.ServerReset", _("Default Server Settings"), _("Reset server settings to their defaults."), kResetServersCmd);
+	_serverResetDevButton = new GUI::ButtonWidget(widgetsBoss(), "HENetworkGameOptionsDialog.ServerResetDev", _("Development Server Settings"), _("Reset server settings to their development defaults."), kResetServerDevCmd);
+
+}
+
+void HENetworkGameOptionsWidget::load() {
+	bool enableSessionServer = true;
+	bool enableLANBroadcast = true;
+	Common::String sessionServerAddr = "multiplayer.scummvm.org";
+
+	if (ConfMan.hasKey("enable_session_server", _domain))
+		enableSessionServer = ConfMan.getBool("enable_session_server", _domain);
+	_enableSessionServer->setState(enableSessionServer);
+
+	if (ConfMan.hasKey("enable_lan_broadcast", _domain))
+		enableLANBroadcast = ConfMan.getBool("enable_lan_broadcast", _domain);
+	_enableLANBroadcast->setState(enableLANBroadcast);
+
+	if (ConfMan.hasKey("session_server", _domain))
+		sessionServerAddr = ConfMan.get("session_server", _domain);
+	_sessionServerAddr->setEditString(sessionServerAddr);
+	_sessionServerAddr->setEnabled(enableSessionServer);
+
+}
+
+bool HENetworkGameOptionsWidget::save() {
+	ConfMan.setBool("enable_session_server", _enableSessionServer->getState(), _domain);
+	ConfMan.setBool("enable_lan_broadcast", _enableLANBroadcast->getState(), _domain);
+	ConfMan.set("session_server", _sessionServerAddr->getEditString(), _domain);
+	return true;
+}
+
+void HENetworkGameOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
+	layouts.addDialog(layoutName, overlayedLayout)
+		.addLayout(GUI::ThemeLayout::kLayoutVertical, 5)
+			.addPadding(0, 0, 12, 0)
+			.addWidget("EnableSessionServer", "Checkbox")
+			.addWidget("EnableLANBroadcast", "Checkbox")
+			.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 12)
+				.addPadding(0, 0, 12, 0)
+				.addWidget("SessionServerLabel", "OptionsLabel")
+				.addWidget("SessionServerAddress", "EditTextWidget")
+			.closeLayout()
+			.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 5)
+				.addPadding(0, 0, 12, 0)
+				.addWidget("ServerReset", "Button")
+				.addWidget("ServerResetDev", "Button")
+			.closeLayout()
+		.closeLayout()
+	.closeDialog();
+}
+
+void HENetworkGameOptionsWidget::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
+
+	switch (cmd) {
+	case kEnableSessionCmd:
+		_sessionServerAddr->setEnabled(_enableSessionServer->getState());
+		g_gui.scheduleTopDialogRedraw();
+		break;
+	case kResetServersCmd:
+		_enableSessionServer->setState(true);
+		_sessionServerAddr->setEditString(Common::U32String("multiplayer.scummvm.org"));
+		g_gui.scheduleTopDialogRedraw();
+		break;
+	case kResetServerDevCmd:
+		_enableSessionServer->setState(false);
+		_sessionServerAddr->setEditString(Common::U32String("127.0.0.1"));
+		g_gui.scheduleTopDialogRedraw();
+		break;
+	default:
+		GUI::OptionsContainerWidget::handleCommand(sender, cmd, data);
+		break;
+	}
+}
+#endif
+
 } // End of namespace Scumm
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index a561f9a5353..860bb493576 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -318,6 +318,40 @@ private:
 	void updateOutlookAdjustmentValue();
 };
 
+#ifdef USE_ENET
+/**
+ * Options widget for network supported HE games
+ * (Football 1999/2002, Baseball 2001 and
+ * Moonbase Commander).
+ */
+class HENetworkGameOptionsWidget : public ScummOptionsContainerWidget {
+public:
+	HENetworkGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
+	~HENetworkGameOptionsWidget() override {};
+
+	void load() override;
+	bool save() override;
+
+private:
+	enum {
+		kEnableSessionCmd = 'ENBS',
+		kResetServersCmd = 'CLRS',
+		kResetServerDevCmd = 'CLRD'
+	};
+
+	void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
+	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
+
+	GUI::CheckboxWidget *_enableSessionServer;
+	GUI::EditTextWidget *_sessionServerAddr;
+
+	GUI::CheckboxWidget *_enableLANBroadcast;
+
+	GUI::ButtonWidget *_serverResetButton;
+	GUI::ButtonWidget *_serverResetDevButton;
+};
+#endif
+
 } // End of namespace Scumm
 
 #endif
diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index b4c834423bd..10a411d9c39 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -19,6 +19,8 @@
  *
  */
 
+#include "common/config-manager.h"
+
 #include "scumm/he/intern_he.h"
 #include "scumm/he/net/net_main.h"
 #include "scumm/he/net/net_defines.h"
@@ -40,6 +42,14 @@ Net::Net(ScummEngine_v90he *vm) : _latencyTime(1), _fakeLatency(false), _vm(vm)
 	_sessionHost = nullptr;
 	_broadcastSocket = nullptr;
 
+	_sessionServerAddress = Address {"multiplayer.scummvm.org", 9120};
+	if (ConfMan.hasKey("session_server")) {
+		_sessionServerAddress = getAddressFromString(ConfMan.get("session_server"));
+		// Set port to default if not defined.
+		if (!_sessionServerAddress.port)
+			_sessionServerAddress.port = 9120;
+	}
+
 	_sessionServerPeer = -1;
 	_sessionServerHost = nullptr;
 	_isRelayingGame = false;
@@ -257,22 +267,24 @@ int Net::createSession(char *name) {
 	}
 
 	_isHost = true;
-	// TODO: Config to enable/disable Internet sessions.
-	if (_sessionHost->connectPeer("127.0.0.1", 9120)) {
-		_sessionServerPeer = _sessionHost->getPeerIndexFromHost("127.0.0.1", 9120);
-		// Create session to the session server.
-		Common::String req = Common::String::format(
-			"{\"cmd\":\"host_session\",\"game\":\"%s\",\"version\":\"%s\",\"name\":\"%s\"}",
-			_gameName.c_str(), _gameVersion.c_str(), name);
-		_sessionHost->send(req.c_str(), _sessionServerPeer);
-	} else {
-		warning("Failed to connect to session server!  This game will not be listed on the Internet");
+	if (ConfMan.getBool("enable_session_server")) {
+		if (_sessionHost->connectPeer(_sessionServerAddress.host, _sessionServerAddress.port)) {
+			_sessionServerPeer = _sessionHost->getPeerIndexFromHost(_sessionServerAddress.host, _sessionServerAddress.port);
+			// Create session to the session server.
+			Common::String req = Common::String::format(
+				"{\"cmd\":\"host_session\",\"game\":\"%s\",\"version\":\"%s\",\"name\":\"%s\"}",
+				_gameName.c_str(), _gameVersion.c_str(), name);
+			_sessionHost->send(req.c_str(), _sessionServerPeer);
+		} else {
+			warning("Failed to connect to session server!  This game will not be listed on the Internet");
+		}
 	}
 
-	// TODO: Config to enable/disable LAN discovery.
-	_broadcastSocket = _enet->createSocket("0.0.0.0", 9130);
-	if (!_broadcastSocket) {
-		warning("NETWORK: Unable to create broadcast socket, your game will not be broadcast over LAN");
+	if (ConfMan.getBool("enable_lan_broadcast")) {
+		_broadcastSocket = _enet->createSocket("0.0.0.0", 9130);
+		if (!_broadcastSocket) {
+			warning("NETWORK: Unable to create broadcast socket, your game will not be broadcast over LAN");
+		}
 	}
 
 	return 1;
@@ -496,16 +508,15 @@ bool Net::destroyPlayer(int32 userId) {
 int32 Net::startQuerySessions(bool connectToSessionServer) {
 	debug(1, "Net::startQuerySessions()");
 
-	// TODO: Config to enable/disable Internet sessions.
-	if (!_sessionServerHost && connectToSessionServer) {
-		// TODO: Configurable session server address and port
-		_sessionServerHost = _enet->connectToHost("127.0.0.1", 9120);
-		if (!_sessionServerHost)
-			warning("Failed to connect to session server!  You'll won't be able to join internet sessions");
+	if (connectToSessionServer && ConfMan.getBool("enable_session_server")) {
+		if (!_sessionServerHost) {
+			_sessionServerHost = _enet->connectToHost(_sessionServerAddress.host, _sessionServerAddress.port);
+			if (!_sessionServerHost)
+				warning("Failed to connect to session server!  You'll won't be able to join internet sessions");
+		}
 	}
 
-	// TODO: Config to enable/disable LAN discovery.
-	if (!_broadcastSocket) {
+	if (ConfMan.getBool("enable_lan_broadcast") && !_broadcastSocket) {
 		_broadcastSocket = _enet->createSocket("0.0.0.0", 0);
 	}
 	return 0;
@@ -881,8 +892,8 @@ void Net::handleSessionServerData(Common::String data) {
 				Common::String address = root["address"]->asString();
 
 				if (addUser(const_cast<char *>(address.c_str()), const_cast<char *>(address.c_str()))) {
-					_userIdToAddress[_userIdCounter] = "127.0.0.1:9120";
-					_addressToUserId["127.0.0.1:9120"] = _userIdCounter;
+					_userIdToAddress[_userIdCounter] = getStringFromAddress(_sessionServerAddress);
+					_addressToUserId[getStringFromAddress(_sessionServerAddress)] = _userIdCounter;
 					_userIdToPeerIndex[_userIdCounter] = _sessionServerPeer;
 
 					_isRelayingGame = true;
@@ -959,8 +970,8 @@ void Net::handleBroadcastData(Common::String data, Common::String host, int port
 			// Session query.
 			if (_sessionHost) {
 				Common::String resp = Common::String::format(
-					"{\"cmd\":\"session_resp\",\"version\":\"%s\",\"id\":%d,\"name\":\"%s\",\"players\":%d}",
-					_gameVersion.c_str(), _sessionId, _sessionName.c_str(), getTotalPlayers());
+					"{\"cmd\":\"session_resp\",\"game\":\"%s\",\"version\":\"%s\",\"id\":%d,\"name\":\"%s\",\"players\":%d}",
+					_gameName.c_str(), _gameVersion.c_str(), _sessionId, _sessionName.c_str(), getTotalPlayers());
 
 				// Send this through the session host instead of the broadcast socket
 				// because that will send the correct port to connect to.
@@ -968,16 +979,15 @@ void Net::handleBroadcastData(Common::String data, Common::String host, int port
 				_sessionHost->sendRawData(host, port, resp.c_str());
 			}
 		} else if (command == "session_resp") {
-			if (!_sessionHost && root.contains("version") && root.contains("id") && root.contains("name") && root.contains("players")) {
+			if (!_sessionHost && root.contains("game") && root.contains("version") && root.contains("id") && root.contains("name") && root.contains("players")) {
+				Common::String game = root["game"]->asString();
 				Common::String version = root["version"]->asString();
 				int sessionId = root["id"]->asIntegerNumber();
 				Common::String name = root["name"]->asString();
 				int players = root["players"]->asIntegerNumber();
 
-				// TODO: Check and match game name
-
-				if (version != _gameVersion)
-					// Version mismatch.
+				if (game != _gameName || version != _gameVersion)
+					// Game/Version mismatch.
 					return;
 
 				if (players < 1 || players > _maxPlayers)
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index 25da3850e87..eb9efef3bcd 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -155,6 +155,7 @@ public:
 
 	// For creating/joining sessions over the Internet.
 	Networking::Host *_sessionServerHost;
+	Address _sessionServerAddress;
 	int _sessionServerPeer;
 	bool _isRelayingGame; // If we're relaying in-game data over the session server or not.
 };
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index b445b912a2f..031e6515959 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -590,6 +590,11 @@ GUI::OptionsContainerWidget *ScummMetaEngine::buildEngineOptionsWidget(GUI::GuiO
 
 		return new Scumm::MI1CdGameOptionsWidget(boss, name, target);
 	}
+#ifdef USE_ENET
+	else if (gameid == "football" || gameid == "baseball2001" || gameid == "football2002" ||
+		gameid == "moonbase")
+		return new Scumm::HENetworkGameOptionsWidget(boss, name, target);
+#endif
 
 	return MetaEngine::buildEngineOptionsWidget(boss, name, target);
 }


Commit: f42930ba66b0d92e793d7f2a919a618342f440da
    https://github.com/scummvm/scummvm/commit/f42930ba66b0d92e793d7f2a919a618342f440da
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: CURL: Ability to create raw sockets.

This can be used to create custom protocols with an SDL_net like
interface while being able to connect to SSL/TLS servers.

Changed paths:
  A backends/networking/curl/socket.cpp
  A backends/networking/curl/socket.h
    backends/module.mk


diff --git a/backends/module.mk b/backends/module.mk
index 4a3aebff3e3..a0ccfeebc7c 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -77,7 +77,8 @@ MODULE_OBJS += \
 	networking/curl/postrequest.o \
 	networking/curl/request.o \
 	networking/curl/session.o \
-	networking/curl/sessionrequest.o
+	networking/curl/sessionrequest.o \
+	networking/curl/socket.o
 endif
 
 ifdef USE_SDL_NET
diff --git a/backends/networking/curl/socket.cpp b/backends/networking/curl/socket.cpp
new file mode 100644
index 00000000000..00688e1978b
--- /dev/null
+++ b/backends/networking/curl/socket.cpp
@@ -0,0 +1,142 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "backends/networking/curl/socket.h"
+#include "common/debug.h"
+#include <curl/curl.h>
+
+// Auxiliary function that waits on the socket.
+// From https://github.com/curl/curl/blob/master/docs/examples/sendrecv.c
+static int waitOnSocket(curl_socket_t sockfd, int for_recv, long timeout_ms) {
+  struct timeval tv;
+  fd_set infd, outfd, errfd;
+  int res;
+
+  tv.tv_sec = timeout_ms / 1000;
+  tv.tv_usec = (timeout_ms % 1000) * 1000;
+
+  FD_ZERO(&infd);
+  FD_ZERO(&outfd);
+  FD_ZERO(&errfd);
+
+  FD_SET(sockfd, &errfd); /* always check for error */
+
+  if(for_recv) {
+    FD_SET(sockfd, &infd);
+  }
+  else {
+    FD_SET(sockfd, &outfd);
+  }
+
+  /* select() returns the number of signalled sockets or -1 */
+  res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv);
+  return res;
+}
+
+namespace Networking {
+
+CurlSocket::CurlSocket() {
+	_easy = nullptr;
+}
+
+CurlSocket::~CurlSocket() {
+	// Always clean up.
+	curl_easy_cleanup(_easy);
+}
+
+int CurlSocket::ready() {
+	return waitOnSocket(_socket, 1, 0);
+}
+
+bool CurlSocket::connect(Common::String url) {
+	_easy = curl_easy_init();
+	if (_easy) {
+		curl_easy_setopt(_easy, CURLOPT_URL, url.c_str());
+		// Just connect to the host, do not do any transfers.
+		curl_easy_setopt(_easy, CURLOPT_CONNECT_ONLY, 1L);
+		
+		// Uncomment this to disable SSL certificate verification
+		// (e.g. self-signed certs).
+		// curl_easy_setopt(_easy, CURLOPT_SSL_VERIFYPEER, 0L);
+
+		CURLcode res = curl_easy_perform(_easy);
+		if (res != CURLE_OK) {
+			warning("libcurl: Failed to connect: %s", curl_easy_strerror(res));
+			return false;
+		}
+
+		// Get the socket, we'll need it for waiting.
+		res = curl_easy_getinfo(_easy, CURLINFO_ACTIVESOCKET, &_socket);
+		if (res != CURLE_OK) {
+			warning("libcurl: Failed to extract socket: %s", curl_easy_strerror(res));
+			return false;
+		}
+
+		return true;
+	}
+	return false;
+}
+
+size_t CurlSocket::send(const char *data, int len) {
+	if (!_socket)
+		return -1;
+    size_t nsent_total = 0, left = len;
+    CURLcode res = CURLE_AGAIN;
+
+	// Keep looping until the whole thing is sent, errors,
+	// or times out.
+	while (((left > 0) && (len > 0))) {
+		size_t nsent;
+		while (res == CURLE_AGAIN) {
+			// TODO: Time out if it takes too long.
+			res = curl_easy_send(_easy, data + nsent_total, left - nsent_total, &nsent);
+		}
+		if (res == CURLE_OK) {
+			nsent_total += nsent;
+			left -= nsent;
+		} else if (res != CURLE_AGAIN) {
+			warning("libcurl: Error when sending to socket: %s", curl_easy_strerror(res));
+			return nsent_total;
+		}
+	}
+
+	return nsent_total;
+}
+
+size_t CurlSocket::recv(void *data, int maxLen) {
+	size_t nread;
+	CURLcode res = CURLE_AGAIN;
+	while (res == CURLE_AGAIN) {
+		// TODO: Time out if it takes too long.
+		res = curl_easy_recv(_easy, data, maxLen, &nread);
+	}
+	if(res != CURLE_OK) {
+		warning("libcurl Error on receiving data: %s\n", curl_easy_strerror(res));
+		return -1;
+	}
+
+	debug(1, "libcurl: Received %lu bytes", (uint64)nread);
+	return nread;
+}
+
+} // End of namespace Networking
+
diff --git a/backends/networking/curl/socket.h b/backends/networking/curl/socket.h
new file mode 100644
index 00000000000..bf670d957bc
--- /dev/null
+++ b/backends/networking/curl/socket.h
@@ -0,0 +1,60 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef BACKENDS_NETWORKING_CURL_SOCKET_H
+#define BACKENDS_NETWORKING_CURL_SOCKET_H
+
+typedef void CURL;
+#ifdef WIN32
+// Including winsock2.h will result in errors, we have to define
+// SOCKET ourselves.
+#include <basetsd.h>
+typedef UINT_PTR SOCKET;
+
+typedef SOCKET curl_socket_t;
+#else
+typedef int curl_socket_t;
+#endif
+
+#include "common/str.h"
+
+namespace Networking {
+
+class CurlSocket {
+public:
+	CurlSocket();
+	~CurlSocket();
+
+	bool connect(Common::String url);
+
+	int ready();
+
+	size_t send(const char *data, int len);
+	size_t recv(void *data, int maxLen);
+private:
+	CURL *_easy;
+	curl_socket_t _socket;
+};
+
+} // End of namespace Networking
+
+
+
+#endif
\ No newline at end of file


Commit: d06bac869c8534568045eef3443b9770b90177e3
    https://github.com/scummvm/scummvm/commit/d06bac869c8534568045eef3443b9770b90177e3
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Initial Lobby (Boneyards) implementation

Changed paths:
  A engines/scumm/he/net/net_lobby.cpp
  A engines/scumm/he/net/net_lobby.h
    engines/scumm/he/intern_he.h
    engines/scumm/he/logic/football.cpp
    engines/scumm/module.mk
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index eab49d3b9b8..cec294d808a 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -252,6 +252,9 @@ protected:
 
 #ifdef USE_ENET
 class Net;
+#ifdef USE_LIBCURL
+class Lobby;
+#endif
 #endif
 class Moonbase;
 
@@ -542,6 +545,9 @@ class ScummEngine_v90he : public ScummEngine_v80he {
 	friend class LogicHE;
 #ifdef USE_ENET
 	friend class Net;
+#ifdef USE_LIBCURL
+	friend class Lobby;
+#endif
 #endif
 	friend class Moonbase;
 	friend class MoviePlayer;
@@ -623,6 +629,9 @@ protected:
 #ifdef USE_ENET
 public:
 	Net *_net;
+#ifdef USE_LIBCURL
+	Lobby *_lobby;
+#endif
 #endif
 
 public:
diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index 20d7d698962..496f251ed29 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -25,6 +25,9 @@
 
 #ifdef USE_ENET
 #include "scumm/he/net/net_main.h"
+#ifdef USE_LIBCURL
+#include "scumm/he/net/net_lobby.h"
+#endif
 #include "scumm/he/net/net_defines.h"
 #endif
 
@@ -44,6 +47,41 @@
 #define OP_NET_INIT_LAN_GAME		1515
 #define OP_NET_SET_PROVIDER_BY_NAME	1516
 
+// Boneyards (Lobby) opcodes.
+#define OP_NET_OPEN_WEB_URL						2121
+#define OP_NET_DOWNLOAD_PLAYBOOK				2122
+#define OP_NET_CONNECT 							2200
+#define OP_NET_DISCONNECT 						2201
+#define OP_NET_LOGIN							2202
+#define OP_NET_ENTER_AREA						2204
+#define OP_NET_GET_NUM_PLAYERS_IN_AREA			2205
+#define OP_NET_FETCH_PLAYERS_INFO_IN_AREA		2206
+#define OP_NET_GET_PLAYERS_INFO					2207
+#define OP_NET_START_HOSTING_GAME				2208
+#define OP_NET_CALL_PLAYER						2209
+#define OP_NET_RECEIVER_BUSY					2212
+#define OP_NET_COUNTER_CHALLENGE				2213
+#define OP_NET_GET_PROFILE						2214
+#define OP_NET_DECLINE_CHALLENGE				2215
+#define OP_NET_ACCEPT_CHALLENGE					2216
+#define OP_NET_STOP_CALLING						2217
+#define OP_NET_CHANGE_ICON						2218
+#define OP_NET_SET_PHONE_STATUS					2220
+#define OP_NET_ANSWER_PHONE						2221
+#define OP_NET_LEAVE_AREA						2222
+#define OP_NET_GAME_FINISHED					2223
+#define OP_NET_GAME_STARTED						2224
+#define OP_NET_UPDATE_PROFILE_ARRAY				2225
+#define OP_NET_LOCATE_PLAYER					2226
+#define OP_NET_GET_POPULATION					2227
+// Used in baseball to get news, poll and banner.
+#define OP_NET_DOWNLOAD_FILE					2238
+
+// MAIA (Updater) opcodes.
+#define OP_NET_UPDATE_INIT						3000
+#define OP_NET_CHECK_INTERNET_STATUS			3001
+#define OP_NET_FETCH_UPDATES					3002
+
 namespace Scumm {
 
 /**
@@ -63,6 +101,9 @@ protected:
 #ifdef USE_ENET
 	void netRemoteStartScript(int numArgs, int32 *args);
 	void netRemoteSendArray(int32 *args);
+#ifdef USE_LIBCURL
+	void netLogin(int32 *args);
+#endif
 #endif
 
 	int lineEquation3D(int32 *args);
@@ -80,6 +121,9 @@ int LogicHEfootball::versionID() {
 
 int LogicHEfootball::startOfFrame() {
 #ifdef USE_ENET
+#ifdef USE_LIBCURL
+	_vm->_lobby->doNetworkOnceAFrame();
+#endif
 	_vm->_net->doNetworkOnceAFrame(0);
 #endif
 	return 0;
@@ -153,7 +197,50 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 	case OP_NET_WHO_AM_I:
 		res = _vm->_net->whoAmI();
 		break;
+
+#ifdef USE_LIBCURL
+	// Lobby opcodes goes here:
+	case OP_NET_DOWNLOAD_PLAYBOOK:
+		// TODO
+		break;
+	case OP_NET_CONNECT:
+		_vm->_lobby->connect();
+		break;
+
+	case OP_NET_DISCONNECT:
+		_vm->_lobby->disconnect();
+		break;
+
+	case OP_NET_LOGIN:
+		netLogin(args);
+		break;
+
+	case OP_NET_GET_PROFILE:
+		_vm->_lobby->getUserProfile(args[0]);
+		break;
+
+#endif // USE_LIBCURL
+#endif // USE_ENET
+
+	case OP_NET_CHECK_INTERNET_STATUS:
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+		// We can only use the lobby system if both
+		// libcurl (for lobby communication) and
+		// ENet (for gameplay communication) is enabled.
+
+		// TODO: Actually check if we're connected to the
+		// Internet.
+		res = 1;
 #endif
+		break;
+
+	// TODO: Should we actually implement update checks here
+	// this at some point?
+	case OP_NET_UPDATE_INIT:
+		break;
+	case OP_NET_FETCH_UPDATES:
+		writeScummVar(111, 2);
+		break;
 
 	case 1493: case 1494: case 1495: case 1496:
 	case 1498: case 1499: case 1501:
@@ -165,16 +252,16 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 		// 1555: set fake lag
 		break;
 
-	case 2200: case 2201: case 2202: case 2203: case 2204:
+	case 2203: case 2204:
 	case 2205: case 2206: case 2207: case 2208: case 2209:
-	case 2210: case 2211: case 2212: case 2213: case 2214:
+	case 2210: case 2211: case 2212: case 2213:
 	case 2215: case 2216: case 2217: case 2218: case 2219:
 	case 2220: case 2221: case 2222: case 2223: case 2224:
 	case 2225: case 2226: case 2227: case 2228:
 		// Boneyards-related
 		break;
 
-	case 3000: case 3001: case 3002: case 3003: case 3004:
+	case 3003: case 3004:
 		// Internet-related
 		// 3000: check for updates
 		// 3001: check network status
@@ -377,7 +464,20 @@ void LogicHEfootball::netRemoteSendArray(int32 *args) {
 
 	_vm->_net->remoteSendArray(PN_SENDTYPE_INDIVIDUAL, targetUserId, priority, args[3]);
 }
-#endif
+
+#ifdef USE_LIBCURL
+void LogicHEfootball::netLogin(int32 *args) {
+	char userName[16];
+	char password[16];
+
+	_vm->getStringFromArray(args[0], userName, sizeof(userName));
+	_vm->getStringFromArray(args[1], password, sizeof(password));
+
+	_vm->_lobby->login(userName, password);
+}
+
+#endif // USE_LIBCURL
+#endif // USE_ENET
 
 class LogicHEfootball2002 : public LogicHEfootball {
 public:
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
new file mode 100644
index 00000000000..73c0fb0307b
--- /dev/null
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -0,0 +1,209 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/config-manager.h"
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/net/net_lobby.h"
+
+namespace Scumm {
+
+Lobby::Lobby(ScummEngine_v90he *vm) : _vm(vm) {
+	_gameName = _vm->_game.gameid;
+	_socket = nullptr;
+
+	_userId = 0;
+}
+
+Lobby::~Lobby() {
+	if (_socket)
+		delete _socket;
+}
+
+void Lobby::writeStringArray(int array, Common::String string) {
+	int newArray = 0;
+	byte *data = _vm->defineArray(array, ScummEngine_v90he::kStringArray, 0, 0, 0, strlen(string.c_str()), true, &newArray);
+	memcpy(data, string.c_str(), strlen(string.c_str()));
+	_vm->writeVar(array, newArray);
+}
+
+void Lobby::doNetworkOnceAFrame() {
+	if (!_socket)
+		return;
+
+	int ready = _socket->ready();
+	if (ready) {
+		receiveData();
+	}
+}
+
+void Lobby::send(Common::JSONObject data) {
+	if (!_socket) {
+		warning("LOBBY: Attempted to send data while not connected to server");
+		return;
+	}
+	Common::JSONValue value(data);
+	Common::String valueString = Common::JSON::stringify(&value);
+	// Add new line.
+	valueString += "\n";
+
+	debug(1, "LOBBY: Sending data: %s", valueString.c_str());
+	_socket->send(valueString.c_str(), strlen(valueString.c_str()));
+}
+
+void Lobby::receiveData() {
+	if (!_socket)
+		return;
+
+	char data[1024];
+	size_t len = _socket->recv(data, 1024);
+	if (!len) {
+		// Assume disconnection.
+	}
+
+	Common::String data_str(data, len);
+	_buffer += data_str;
+
+	while (_buffer.contains("\n")) {
+		int pos = _buffer.findFirstOf('\n');
+		processLine(_buffer.substr(0, pos));
+		_buffer = _buffer.substr(pos + 1);
+	}
+}
+
+void Lobby::processLine(Common::String line) {
+	debug(1, "LOBBY: Received Data: %s", line.c_str());
+	Common::JSONValue *json = Common::JSON::parse(line.c_str());
+	if (!json) {
+		warning("LOBBY: Received trunciated data from server! %s", line.c_str());
+		return;
+	}
+	if (!json->isObject()){
+		warning("LOBBY: Received non JSON object from server! %s", line.c_str());
+		return;
+	}
+
+	Common::JSONObject root = json->asObject();
+	if (root.find("cmd") != root.end() && root["cmd"]->isString()) {
+		Common::String command = root["cmd"]->asString();
+		if (command == "heartbeat") {
+			handleHeartbeat();
+		} else if (command == "login_resp") {
+			int errorCode = root["error_code"]->asIntegerNumber();
+			int userId = root["id"]->asIntegerNumber();
+			Common::String response = root["response"]->asString();
+			handleLoginResp(errorCode, userId, response);
+		} else if (command == "profile_info") {
+			Common::JSONArray profile = root["profile"]->asArray();
+			handleProfileInfo(profile);
+		}
+	}
+}
+
+void Lobby::handleHeartbeat() {
+	Common::JSONObject heartbeat;
+	heartbeat.setVal("cmd", new Common::JSONValue("heartbeat"));
+	send(heartbeat);
+}
+
+bool Lobby::connect() {
+	if (_socket)
+		return true;
+
+	_socket = new Networking::CurlSocket();
+
+	// NOTE: Even though the protocol starts with http(s), this is an entirely
+	// different protocol.  This is done so we can achieve communicating over
+	// TLS/SSL sockets.
+	// TODO: custom url
+	Common::String url = "http://localhost:9130";
+
+	debug(1, "LOBBY: Connecting to %s", url.c_str());
+
+	if (_socket->connect(url)) {
+		debugC(1, "LOBBY: Successfully connected to %s", url.c_str());
+		return true;
+	} else {
+		writeStringArray(109, "Unable to contact server");
+		_vm->writeVar(108, -99);
+	}
+	return false;
+}
+
+void Lobby::disconnect() {
+	if (!_socket)
+		return;
+
+	debug(1, "LOBBY: Disconnecting connection to server.");
+	delete _socket;
+	_socket = nullptr;
+}
+
+void Lobby::login(const char *userName, const char *password) {
+	Common::JSONObject loginRequestParameters;
+	loginRequestParameters.setVal("cmd", new Common::JSONValue("login"));
+	loginRequestParameters.setVal("user", new Common::JSONValue((Common::String)userName));
+	loginRequestParameters.setVal("pass", new Common::JSONValue((Common::String)password));
+	loginRequestParameters.setVal("game", new Common::JSONValue((Common::String)_gameName));
+
+	send(loginRequestParameters);
+}
+
+void Lobby::handleLoginResp(int errorCode, int userId, Common::String response) {
+	if (errorCode > 0) {
+		writeStringArray(109, response);
+		_vm->writeVar(108, -99);
+		return;
+	}
+	_userId = userId;
+	_vm->writeVar(108, 99);
+}
+
+void Lobby::getUserProfile(int userId) {
+	if (!_socket)
+		return;
+
+	Common::JSONObject getProfileRequest;
+	getProfileRequest.setVal("cmd", new Common::JSONValue("get_profile"));
+	if (userId) {
+		getProfileRequest.setVal("user_id", new Common::JSONValue((long long int)userId));
+	}
+	send(getProfileRequest);
+}
+
+void Lobby::handleProfileInfo(Common::JSONArray profile) {
+	int newArray = 0;
+	_vm->defineArray(108, ScummEngine_v90he::kDwordArray, 0, 0, 0, profile.size(), true, &newArray);
+	_vm->writeVar(108, newArray);
+
+	for (uint i = 0; i < profile.size(); i++) {
+		if (profile[i]->isIntegerNumber()) {
+			_vm->writeArray(108, 0, i, profile[i]->asIntegerNumber());
+		} else {
+			warning("BYOnline: Value for profile index %d is not an integer!", i);
+		}
+	}
+	_vm->writeVar(111, 1);
+}
+
+} // End of namespace Scumm
+
+
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
new file mode 100644
index 00000000000..5715d624c0a
--- /dev/null
+++ b/engines/scumm/he/net/net_lobby.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SCUMM_HE_NET_LOBBY_H
+#define SCUMM_HE_NET_LOBBY_H
+
+#include "common/formats/json.h"
+#include "backends/networking/curl/socket.h"
+
+// Commands for VAR_REMOTE_START_SCRIPT. (55 in football; 324 in baseball)
+#define OP_REMOTE_SYSTEM_ALERT			9911
+#define OP_REMOTE_START_CONNECTION		9919
+#define OP_REMOTE_RECEIVE_CHALLENGE		9920
+#define OP_REMOTE_OPPONENT_ANSWERS		9921
+#define OP_REMOTE_OPPONENT_BUSY			9922
+#define OP_REMOTE_COUNTER_CHALLENGE		9923
+#define OP_REMOTE_OPPONENT_DECLINES		9924
+#define OP_REMOTE_OPPONENT_ACCEPTS		9925
+#define OP_REMOTE_PING_TEST_RESULT		9927
+
+namespace Scumm {
+
+class ScummEngine_v90he;
+
+class Lobby {
+public:
+	Lobby(ScummEngine_v90he *vm);
+	~Lobby();
+
+	void doNetworkOnceAFrame();
+	void send(Common::JSONObject data);
+
+	bool connect();
+	void disconnect();
+	void login(const char *userName, const char *password);
+	void getUserProfile(int userId);
+protected:
+	ScummEngine_v90he *_vm;
+	Common::String _gameName;
+	Networking::CurlSocket *_socket;
+
+	Common::String _buffer;
+
+	void writeStringArray(int array, Common::String string);
+
+	void receiveData();
+	void processLine(Common::String line);
+
+	void handleHeartbeat();
+
+	void handleLoginResp(int errorCode, int userId, Common::String response);
+	void handleProfileInfo(Common::JSONArray profile);
+
+	int _userId;
+};
+
+} // End of namespace Scumm
+
+
+#endif
\ No newline at end of file
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 24b82623e21..218132cfbcf 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -169,6 +169,12 @@ MODULE_OBJS += \
 ifdef USE_ENET
 MODULE_OBJS += \
 	he/net/net_main.o
+
+ifdef USE_LIBCURL
+MODULE_OBJS += \
+	he/net/net_lobby.o
+endif
+
 endif
 endif
 
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 831b264b01c..da3b7094544 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -89,6 +89,9 @@
 
 #ifdef USE_ENET
 #include "scumm/he/net/net_main.h"
+#ifdef USE_LIBCURL
+#include "scumm/he/net/net_lobby.h"
+#endif
 #endif
 
 #include "backends/audiocd/audiocd.h"
@@ -718,12 +721,17 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr)
 	_videoParams.wizResNum = 0;
 
 #ifdef USE_ENET
-	/* Online stuff for Backyard Football and Backyard Baseball 2001 */
+	/* Online stuff for compatable HE games */
 	_net = 0;
 	if (_game.id == GID_FOOTBALL || _game.id == GID_BASEBALL2001 || _game.id == GID_FOOTBALL2002 ||
 		_game.id == GID_MOONBASE) {
 		_net = new Net(this);
 	}
+#ifdef USE_LIBCURL
+	_lobby = 0;
+	if (_game.id == GID_FOOTBALL || _game.id == GID_BASEBALL2001)
+		_lobby = new Lobby(this);
+#endif
 #endif
 
 	VAR_NUM_SPRITE_GROUPS = 0xFF;
@@ -741,6 +749,9 @@ ScummEngine_v90he::~ScummEngine_v90he() {
 
 #ifdef USE_ENET
 	delete _net;
+#ifdef USE_LIBCURL
+	delete _lobby;
+#endif
 #endif
 
 	if (_game.heversion >= 98) {


Commit: 3e29d0bea4229b50453b04c15c4747fe721168b6
    https://github.com/scummvm/scummvm/commit/3e29d0bea4229b50453b04c15c4747fe721168b6
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: CURL: Fix compiling on Windows.

Changed paths:
    backends/networking/curl/socket.cpp


diff --git a/backends/networking/curl/socket.cpp b/backends/networking/curl/socket.cpp
index 00688e1978b..9b96e8c192f 100644
--- a/backends/networking/curl/socket.cpp
+++ b/backends/networking/curl/socket.cpp
@@ -27,35 +27,35 @@
 // Auxiliary function that waits on the socket.
 // From https://github.com/curl/curl/blob/master/docs/examples/sendrecv.c
 static int waitOnSocket(curl_socket_t sockfd, int for_recv, long timeout_ms) {
-  struct timeval tv;
-  fd_set infd, outfd, errfd;
-  int res;
+	struct timeval tv {};
+	fd_set infd {}, outfd {}, errfd {};
+	int res;
 
-  tv.tv_sec = timeout_ms / 1000;
-  tv.tv_usec = (timeout_ms % 1000) * 1000;
+	tv.tv_sec = timeout_ms / 1000;
+	tv.tv_usec = (timeout_ms % 1000) * 1000;
 
-  FD_ZERO(&infd);
-  FD_ZERO(&outfd);
-  FD_ZERO(&errfd);
+	FD_ZERO(&infd);
+	FD_ZERO(&outfd);
+	FD_ZERO(&errfd);
 
-  FD_SET(sockfd, &errfd); /* always check for error */
+	FD_SET(sockfd, &errfd); /* always check for error */
 
-  if(for_recv) {
-    FD_SET(sockfd, &infd);
-  }
-  else {
-    FD_SET(sockfd, &outfd);
-  }
+	if(for_recv) {
+		FD_SET(sockfd, &infd);
+	} else {
+		FD_SET(sockfd, &outfd);
+	}
 
-  /* select() returns the number of signalled sockets or -1 */
-  res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv);
-  return res;
+	/* select() returns the number of signalled sockets or -1 */
+	res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv);
+	return res;
 }
 
 namespace Networking {
 
 CurlSocket::CurlSocket() {
 	_easy = nullptr;
+	_socket = 0;
 }
 
 CurlSocket::~CurlSocket() {
@@ -105,7 +105,7 @@ size_t CurlSocket::send(const char *data, int len) {
 	// Keep looping until the whole thing is sent, errors,
 	// or times out.
 	while (((left > 0) && (len > 0))) {
-		size_t nsent;
+		size_t nsent = 0;
 		while (res == CURLE_AGAIN) {
 			// TODO: Time out if it takes too long.
 			res = curl_easy_send(_easy, data + nsent_total, left - nsent_total, &nsent);
@@ -123,7 +123,7 @@ size_t CurlSocket::send(const char *data, int len) {
 }
 
 size_t CurlSocket::recv(void *data, int maxLen) {
-	size_t nread;
+	size_t nread = 0;
 	CURLcode res = CURLE_AGAIN;
 	while (res == CURLE_AGAIN) {
 		// TODO: Time out if it takes too long.


Commit: 484a72bc4c41c05b7ee3c6474c7ad36040a7b32c
    https://github.com/scummvm/scummvm/commit/484a72bc4c41c05b7ee3c6474c7ad36040a7b32c
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Clean up socket on connection failure.

Changed paths:
    engines/scumm/he/net/net_lobby.cpp


diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 73c0fb0307b..1b6c5440cf8 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -139,9 +139,10 @@ bool Lobby::connect() {
 	debug(1, "LOBBY: Connecting to %s", url.c_str());
 
 	if (_socket->connect(url)) {
-		debugC(1, "LOBBY: Successfully connected to %s", url.c_str());
+		debug(1, "LOBBY: Successfully connected to %s", url.c_str());
 		return true;
 	} else {
+		disconnect();
 		writeStringArray(109, "Unable to contact server");
 		_vm->writeVar(108, -99);
 	}


Commit: 46828bdef5dccbdcbe93af427140e1aa667de820
    https://github.com/scummvm/scummvm/commit/46828bdef5dccbdcbe93af427140e1aa667de820
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Fix crash when starting Football 2002.

Changed paths:
    engines/scumm/he/logic/football.cpp


diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index 496f251ed29..d10116d0575 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -122,7 +122,10 @@ int LogicHEfootball::versionID() {
 int LogicHEfootball::startOfFrame() {
 #ifdef USE_ENET
 #ifdef USE_LIBCURL
-	_vm->_lobby->doNetworkOnceAFrame();
+	// Football 2002 does not have lobby support, so
+	// _lobby is not defined.
+	if (_vm->_lobby)
+		_vm->_lobby->doNetworkOnceAFrame();
 #endif
 	_vm->_net->doNetworkOnceAFrame(0);
 #endif


Commit: 200402852ee6521742d8665f505777154799ab34
    https://github.com/scummvm/scummvm/commit/200402852ee6521742d8665f505777154799ab34
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Replace get_host_ip to get_host.

This resolves issues when connecting to peers via domain names.

Changed paths:
    backends/networking/enet/host.cpp
    backends/networking/enet/socket.cpp


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index df77ed9426b..7b65df62d10 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -54,7 +54,7 @@ uint8 Host::service(int timeout) {
 
 	if (event.type > ENET_EVENT_TYPE_NONE) {
 		char hostName[50];
-		if (enet_address_get_host_ip(&event.peer->address, hostName, 50) == 0)
+		if (enet_address_get_host(&event.peer->address, hostName, 50) == 0)
 			_recentHost = Common::String(hostName);
 		_recentPort = event.peer->address.port;
 	}
@@ -124,7 +124,7 @@ int Host::getPort() {
 int Host::getPeerIndexFromHost(Common::String host, int port) {
 	for (int i = 0; i < (int)_host->peerCount; i++) {
 		char hostName[50];
-		if (enet_address_get_host_ip(&_host->peers[i].address, hostName, 50) == 0) {
+		if (enet_address_get_host(&_host->peers[i].address, hostName, 50) == 0) {
 			if (host == hostName && port == _host->peers[i].address.port) {
 				return i;
 			}
diff --git a/backends/networking/enet/socket.cpp b/backends/networking/enet/socket.cpp
index cb8395b162d..36a6958b396 100644
--- a/backends/networking/enet/socket.cpp
+++ b/backends/networking/enet/socket.cpp
@@ -78,8 +78,8 @@ bool Socket::receive() {
 
 	_recentData = Common::String((const char*)data, receivedLength);
 
-	char hostName[15];
-	if (enet_address_get_host_ip(&_address, hostName, sizeof(hostName)) == 0)
+	char hostName[50];
+	if (enet_address_get_host(&_address, hostName, sizeof(hostName)) == 0)
 		_recentHost = hostName;
 	else
 		_recentHost = "";


Commit: 2697d213b06c4574d11f692cd3a127f9a27c84b9
    https://github.com/scummvm/scummvm/commit/2697d213b06c4574d11f692cd3a127f9a27c84b9
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Properly wait until "get_sessions_resp".

Changed paths:
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h


diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 10a411d9c39..0d24e1159e1 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -274,6 +274,7 @@ int Net::createSession(char *name) {
 			Common::String req = Common::String::format(
 				"{\"cmd\":\"host_session\",\"game\":\"%s\",\"version\":\"%s\",\"name\":\"%s\"}",
 				_gameName.c_str(), _gameVersion.c_str(), name);
+			debug(1, "NETWORK: Sending to session server: %s", req.c_str());
 			_sessionHost->send(req.c_str(), _sessionServerPeer);
 		} else {
 			warning("Failed to connect to session server!  This game will not be listed on the Internet");
@@ -532,9 +533,12 @@ int32 Net::updateQuerySessions() {
 			_gameName.c_str(), _gameVersion.c_str());
 		_sessionServerHost->send(getSessions.c_str(), 0);
 
-		uint32 tickCount = g_system->getMillis() + 100;
+		_gotSessions = false;
+		uint32 tickCount = g_system->getMillis() + 1000;
 		while(g_system->getMillis() < tickCount) {
 			serviceSessionServer();
+			if (_gotSessions)
+				break;
 		}
 	}
 	if (_broadcastSocket) {
@@ -820,7 +824,7 @@ void Net::handleSessionServerData(Common::String data) {
 
 	Common::JSONValue *json = Common::JSON::parse(data.c_str());
 	if (!json) {
-		warning("NETWORK: Received non-JSON string from session server ignoring.");
+		warning("NETWORK: Received non-JSON string from session server, \"%s\", ignoring", data.c_str());
 		return;
 	}
 	if (!json->isObject()){
@@ -871,6 +875,7 @@ void Net::handleSessionServerData(Common::String data) {
 					session.timestamp = g_system->getMillis();
 					_sessions.push_back(session);
 				}
+				_gotSessions = true;
 			}
 		} else if (_isHost && command == "joining_session") {
 			// Someone is gonna attempt to join our session.  Get their address and hole-punch:
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index eb9efef3bcd..0647782fba5 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -156,6 +156,7 @@ public:
 	// For creating/joining sessions over the Internet.
 	Networking::Host *_sessionServerHost;
 	Address _sessionServerAddress;
+	bool _gotSessions;
 	int _sessionServerPeer;
 	bool _isRelayingGame; // If we're relaying in-game data over the session server or not.
 };


Commit: 13616c37ec51d8295aea6feb4207e6c1af5679c7
    https://github.com/scummvm/scummvm/commit/13616c37ec51d8295aea6feb4207e6c1af5679c7
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Fix casting.

Changed paths:
    backends/networking/enet/source/protocol.cpp


diff --git a/backends/networking/enet/source/protocol.cpp b/backends/networking/enet/source/protocol.cpp
index 592a662407f..9e079e7be4c 100644
--- a/backends/networking/enet/source/protocol.cpp
+++ b/backends/networking/enet/source/protocol.cpp
@@ -621,7 +621,7 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet
          fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
 
        memcpy (startCommand -> packet -> data + fragmentOffset,
-               static_cast<const ENetProtocol *>(command) + sizeof (ENetProtocolSendFragment),
+               (const enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
                fragmentLength);
 
         if (startCommand -> fragmentsRemaining <= 0)
@@ -739,7 +739,7 @@ enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer,
          fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
 
        memcpy (startCommand -> packet -> data + fragmentOffset,
-               reinterpret_cast<const enet_uint8 *>(command) + sizeof (ENetProtocolSendFragment),
+               (const enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
                fragmentLength);
 
         if (startCommand -> fragmentsRemaining <= 0)


Commit: 239f1f6760f2ef526044d9b1507f7e5fbb011143
    https://github.com/scummvm/scummvm/commit/239f1f6760f2ef526044d9b1507f7e5fbb011143
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: call both get_host and get_host_ip

Changed paths:
    backends/networking/enet/host.cpp


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 7b65df62d10..45f72a12b62 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -129,6 +129,11 @@ int Host::getPeerIndexFromHost(Common::String host, int port) {
 				return i;
 			}
 		}
+		if (enet_address_get_host_ip(&_host->peers[i].address, hostName, 50) == 0) {
+			if (host == hostName && port == _host->peers[i].address.port) {
+				return i;
+			}
+		}
 	}
 	return -1;
 }


Commit: 71106595910f5d41bff323bc0e12ed118ca8fbdf
    https://github.com/scummvm/scummvm/commit/71106595910f5d41bff323bc0e12ed118ca8fbdf
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Handle registration URL.

Changed paths:
    engines/scumm/he/logic/football.cpp
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h
    engines/scumm/he/net/net_main.cpp


diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index d10116d0575..328c3874f9c 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -203,6 +203,14 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 
 #ifdef USE_LIBCURL
 	// Lobby opcodes goes here:
+	case OP_NET_OPEN_WEB_URL:
+		{
+			char url[50];
+			_vm->getStringFromArray(args[0], url, sizeof(url));
+
+			_vm->_lobby->openUrl(url);
+			break;
+		}
 	case OP_NET_DOWNLOAD_PLAYBOOK:
 		// TODO
 		break;
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 1b6c5440cf8..cb1026bc597 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -124,6 +124,20 @@ void Lobby::handleHeartbeat() {
 	send(heartbeat);
 }
 
+void Lobby::openUrl(const char *url) {
+	debug(1, "LOBBY: openURL: %s", url);
+	Common::String urlString = Common::String(url);
+
+	if (urlString == "http://www.jrsn.com/c_corner/cc_regframe.asp") {
+		_vm->displayMessage(0, "Online Play for this game is provided by Backyard Sports Online, which is a\nservice provided by the ScummVM project.\nYou will now be taken to their registration page.");
+		if (!g_system->openUrl("https://backyardsports.online/register")) {
+			_vm->displayMessage(0, "Failed to open registration URL.  Please navigate to this page manually.\n\n\"https://backyardsports.online/register\"");
+		}
+	} else {
+		warning("LOBBY: URL not handled: %s", url);
+	}
+}
+
 bool Lobby::connect() {
 	if (_socket)
 		return true;
@@ -134,7 +148,7 @@ bool Lobby::connect() {
 	// different protocol.  This is done so we can achieve communicating over
 	// TLS/SSL sockets.
 	// TODO: custom url
-	Common::String url = "http://localhost:9130";
+	Common::String url = "http://127.0.0.1:9130";
 
 	debug(1, "LOBBY: Connecting to %s", url.c_str());
 
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index 5715d624c0a..e5b35b60b04 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -48,6 +48,8 @@ public:
 	void doNetworkOnceAFrame();
 	void send(Common::JSONObject data);
 
+	void openUrl(const char *url);
+
 	bool connect();
 	void disconnect();
 	void login(const char *userName, const char *password);
diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 0d24e1159e1..3b4ebb74809 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -1221,8 +1221,6 @@ void Net::handleGameData(Common::JSONValue *json, int peerIndex) {
 				return;
 			}
 
-			debug("%d", _vm->VAR(_vm->VAR_REMOTE_START_SCRIPT));
-
 			_vm->runScript(_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT), 1, 0, (int *)_tmpbuffer);
 			_vm->pop();
 		}


Commit: 4f23fe5c1f545b63e36891c340c4af4e01dfb2a2
    https://github.com/scummvm/scummvm/commit/4f23fe5c1f545b63e36891c340c4af4e01dfb2a2
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Proper server reset button

Changed paths:
    engines/scumm/dialogs.cpp
    engines/scumm/dialogs.h


diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index d7250031c7c..18876754f52 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -1364,9 +1364,7 @@ HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Co
 
 	_sessionServerAddr = new GUI::EditTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.SessionServerAddress", Common::U32String(""), _("Address of the server to connect to for hosting and joining online game sessions."));
 
-	_serverResetButton = new GUI::ButtonWidget(widgetsBoss(), "HENetworkGameOptionsDialog.ServerReset", _("Default Server Settings"), _("Reset server settings to their defaults."), kResetServersCmd);
-	_serverResetDevButton = new GUI::ButtonWidget(widgetsBoss(), "HENetworkGameOptionsDialog.ServerResetDev", _("Development Server Settings"), _("Reset server settings to their development defaults."), kResetServerDevCmd);
-
+	_serverResetButton = addClearButton(widgetsBoss(), "HENetworkGameOptionsDialog.ServerReset", kResetServersCmd);
 }
 
 void HENetworkGameOptionsWidget::load() {
@@ -1406,11 +1404,7 @@ void HENetworkGameOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Com
 				.addPadding(0, 0, 12, 0)
 				.addWidget("SessionServerLabel", "OptionsLabel")
 				.addWidget("SessionServerAddress", "EditTextWidget")
-			.closeLayout()
-			.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 5)
-				.addPadding(0, 0, 12, 0)
-				.addWidget("ServerReset", "Button")
-				.addWidget("ServerResetDev", "Button")
+				.addWidget("ServerReset", "", 15, 15)
 			.closeLayout()
 		.closeLayout()
 	.closeDialog();
@@ -1428,11 +1422,6 @@ void HENetworkGameOptionsWidget::handleCommand(GUI::CommandSender *sender, uint3
 		_sessionServerAddr->setEditString(Common::U32String("multiplayer.scummvm.org"));
 		g_gui.scheduleTopDialogRedraw();
 		break;
-	case kResetServerDevCmd:
-		_enableSessionServer->setState(false);
-		_sessionServerAddr->setEditString(Common::U32String("127.0.0.1"));
-		g_gui.scheduleTopDialogRedraw();
-		break;
 	default:
 		GUI::OptionsContainerWidget::handleCommand(sender, cmd, data);
 		break;
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index 860bb493576..a2b634f3a8a 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -336,19 +336,17 @@ private:
 	enum {
 		kEnableSessionCmd = 'ENBS',
 		kResetServersCmd = 'CLRS',
-		kResetServerDevCmd = 'CLRD'
 	};
 
 	void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
 	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
 
 	GUI::CheckboxWidget *_enableSessionServer;
+
 	GUI::EditTextWidget *_sessionServerAddr;
+	GUI::ButtonWidget *_serverResetButton;
 
 	GUI::CheckboxWidget *_enableLANBroadcast;
-
-	GUI::ButtonWidget *_serverResetButton;
-	GUI::ButtonWidget *_serverResetDevButton;
 };
 #endif
 


Commit: 8a17fc4ebf3ff68f949de9c6900ecc6e1790a1b9
    https://github.com/scummvm/scummvm/commit/8a17fc4ebf3ff68f949de9c6900ecc6e1790a1b9
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Add Yes/No buttons to registration box.

Changed paths:
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/scumm.cpp
    engines/scumm/scumm.h


diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index cb1026bc597..1cba0b4ceff 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -129,9 +129,10 @@ void Lobby::openUrl(const char *url) {
 	Common::String urlString = Common::String(url);
 
 	if (urlString == "http://www.jrsn.com/c_corner/cc_regframe.asp") {
-		_vm->displayMessage(0, "Online Play for this game is provided by Backyard Sports Online, which is a\nservice provided by the ScummVM project.\nYou will now be taken to their registration page.");
-		if (!g_system->openUrl("https://backyardsports.online/register")) {
-			_vm->displayMessage(0, "Failed to open registration URL.  Please navigate to this page manually.\n\n\"https://backyardsports.online/register\"");
+		if (_vm->displayMessageYesNo("Online Play for this game is provided by Backyard Sports Online, which is a\nservice provided by the ScummVM project.\nWould you like to go to their registration page?")) {
+			if (!g_system->openUrl("https://backyardsports.online/register")) {
+				_vm->displayMessage(0, "Failed to open registration URL.  Please navigate to this page manually.\n\n\"https://backyardsports.online/register\"");
+			}
 		}
 	} else {
 		warning("LOBBY: URL not handled: %s", url);
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index da3b7094544..779450a5bfa 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -3442,6 +3442,18 @@ char ScummEngine::displayMessage(const char *altButton, const char *message, ...
 	return runDialog(dialog);
 }
 
+bool ScummEngine::displayMessageYesNo(const char *message, ...) {
+	char buf[STRINGBUFLEN];
+	va_list va;
+
+	va_start(va, message);
+	vsnprintf(buf, STRINGBUFLEN, message, va);
+	va_end(va);
+
+	GUI::MessageDialog dialog(buf, _("Yes"), _("No"));
+	return runDialog(dialog) == GUI::kMessageOK;
+}
+
 
 #pragma mark -
 #pragma mark --- Miscellaneous ---
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 2ec3af78f0c..b5a0aa88724 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -783,6 +783,7 @@ protected:
 
 public:
 	char displayMessage(const char *altButton, MSVC_PRINTF const char *message, ...) GCC_PRINTF(3, 4);
+	bool displayMessageYesNo(MSVC_PRINTF const char *message, ...) GCC_PRINTF(2, 3);
 
 protected:
 	byte _fastMode = 0;


Commit: 446f95d26cc1fdd614df24f75366bc7baaf97578
    https://github.com/scummvm/scummvm/commit/446f95d26cc1fdd614df24f75366bc7baaf97578
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Send version to lobby server.

Changed paths:
    base/version.cpp
    base/version.h
    engines/scumm/he/logic/football.cpp
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h


diff --git a/base/version.cpp b/base/version.cpp
index f4cf7e8bb31..970bc95a15d 100644
--- a/base/version.cpp
+++ b/base/version.cpp
@@ -55,6 +55,7 @@
  * to properly work in exports (i.e. release tar balls etc.).
  */
 const char gScummVMVersion[] = SCUMMVM_VERSION SCUMMVM_REVISION;
+const char gScummVMVersionLite[] = SCUMMVM_VERSION;
 #if defined(__amigaos4__) || defined(__MORPHOS__)
 static const char *version_cookie __attribute__((used)) = "$VER: ScummVM " SCUMMVM_VERSION SCUMMVM_REVISION " (" AMIGA_DATE ")";
 #endif
diff --git a/base/version.h b/base/version.h
index 657a703f41d..a8dccb40661 100644
--- a/base/version.h
+++ b/base/version.h
@@ -23,6 +23,7 @@
 #define BASE_VERSION_H
 
 extern const char gScummVMVersion[];     // e.g. "0.4.1"
+extern const char gScummVMVersionLite[]; // e.g. "0.4.1" (without version control revisions)
 extern const char gScummVMBuildDate[];   // e.g. "2003-06-24"
 extern const char gScummVMVersionDate[]; // e.g. "0.4.1 (2003-06-24)"
 extern const char gScummVMCompiler[];    // e.g. "GCC 11.2.0"
diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index 328c3874f9c..77c5977a679 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -229,6 +229,10 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 	case OP_NET_GET_PROFILE:
 		_vm->_lobby->getUserProfile(args[0]);
 		break;
+	
+	case OP_NET_CHANGE_ICON:
+		_vm->_lobby->setIcon(args[0]);
+		break;
 
 #endif // USE_LIBCURL
 #endif // USE_ENET
@@ -266,7 +270,7 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 	case 2203: case 2204:
 	case 2205: case 2206: case 2207: case 2208: case 2209:
 	case 2210: case 2211: case 2212: case 2213:
-	case 2215: case 2216: case 2217: case 2218: case 2219:
+	case 2215: case 2216: case 2217: case 2219:
 	case 2220: case 2221: case 2222: case 2223: case 2224:
 	case 2225: case 2226: case 2227: case 2228:
 		// Boneyards-related
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 1cba0b4ceff..779ab88973a 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "base/version.h"
 #include "common/config-manager.h"
 
 #include "scumm/he/intern_he.h"
@@ -28,6 +29,8 @@ namespace Scumm {
 
 Lobby::Lobby(ScummEngine_v90he *vm) : _vm(vm) {
 	_gameName = _vm->_game.gameid;
+	if (_gameName == "baseball2001")
+		_gameName == "baseball";
 	_socket = nullptr;
 
 	_userId = 0;
@@ -76,7 +79,8 @@ void Lobby::receiveData() {
 	char data[1024];
 	size_t len = _socket->recv(data, 1024);
 	if (!len) {
-		// Assume disconnection.
+		// We have been disconnected.
+		disconnect(true);
 	}
 
 	Common::String data_str(data, len);
@@ -157,28 +161,64 @@ bool Lobby::connect() {
 		debug(1, "LOBBY: Successfully connected to %s", url.c_str());
 		return true;
 	} else {
-		disconnect();
+		delete _socket;
+		_socket = nullptr;
 		writeStringArray(109, "Unable to contact server");
 		_vm->writeVar(108, -99);
 	}
 	return false;
 }
 
-void Lobby::disconnect() {
+void Lobby::disconnect(bool lost) {
 	if (!_socket)
 		return;
+	
+	if (!lost) {
+		debug(1, "LOBBY: Disconnecting connection to server.");
+		Common::JSONObject disconnectObject;
+		disconnectObject.setVal("cmd", new Common::JSONValue("disconnect"));
+		send(disconnectObject);
+	} else {
+		systemAlert(901, "You have been disconnected from our server. Returning to login screen.");
+	}
 
-	debug(1, "LOBBY: Disconnecting connection to server.");
 	delete _socket;
 	_socket = nullptr;
 }
 
+void Lobby::runRemoteStartScript(int *args) {
+	if (!_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT)) {
+		warning("LOBBY: VAR_REMOTE_START_SCRIPT not defined!");
+		return;
+	}
+	_vm->runScript(_vm->VAR(_vm->VAR_REMOTE_START_SCRIPT), 1, 0, args);
+	// These scripts always returns a 1 into the stack.  Let's pop it out.
+	_vm->pop();
+}
+
+void Lobby::systemAlert(int type, Common::String message) {
+	int args[25];
+	memset(args, 0, sizeof(args));
+
+	// Write the message as a string array.
+	writeStringArray(0, message);
+
+	// Setup the arguments
+	args[0] = OP_REMOTE_SYSTEM_ALERT;
+	args[1] = type;
+	args[2] = _vm->VAR(0);
+
+	// Run the script
+	runRemoteStartScript(args);
+}
+
 void Lobby::login(const char *userName, const char *password) {
 	Common::JSONObject loginRequestParameters;
 	loginRequestParameters.setVal("cmd", new Common::JSONValue("login"));
 	loginRequestParameters.setVal("user", new Common::JSONValue((Common::String)userName));
 	loginRequestParameters.setVal("pass", new Common::JSONValue((Common::String)password));
 	loginRequestParameters.setVal("game", new Common::JSONValue((Common::String)_gameName));
+	loginRequestParameters.setVal("version", new Common::JSONValue(gScummVMVersionLite));
 
 	send(loginRequestParameters);
 }
@@ -214,12 +254,22 @@ void Lobby::handleProfileInfo(Common::JSONArray profile) {
 		if (profile[i]->isIntegerNumber()) {
 			_vm->writeArray(108, 0, i, profile[i]->asIntegerNumber());
 		} else {
-			warning("BYOnline: Value for profile index %d is not an integer!", i);
+			warning("LOBBY: Value for profile index %d is not an integer!", i);
 		}
 	}
 	_vm->writeVar(111, 1);
 }
 
+void Lobby::setIcon(int icon) {
+	if (!_socket)
+		return;
+
+	Common::JSONObject setIconRequest;
+	setIconRequest.setVal("cmd", new Common::JSONValue("set_icon"));
+	setIconRequest.setVal("icon", new Common::JSONValue((long long int)icon));
+	send(setIconRequest);
+}
+
 } // End of namespace Scumm
 
 
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index e5b35b60b04..168f5bf261a 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -51,9 +51,11 @@ public:
 	void openUrl(const char *url);
 
 	bool connect();
-	void disconnect();
+	void disconnect(bool lost = false);
 	void login(const char *userName, const char *password);
+	
 	void getUserProfile(int userId);
+	void setIcon(int icon);
 protected:
 	ScummEngine_v90he *_vm;
 	Common::String _gameName;
@@ -62,6 +64,8 @@ protected:
 	Common::String _buffer;
 
 	void writeStringArray(int array, Common::String string);
+	void runRemoteStartScript(int *args);
+	void systemAlert(int type, Common::String message);
 
 	void receiveData();
 	void processLine(Common::String line);


Commit: 91c6ddacbaafad7028161a2b5e3b0c9aa1f836a3
    https://github.com/scummvm/scummvm/commit/91c6ddacbaafad7028161a2b5e3b0c9aa1f836a3
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
GUI: Online session selector for Football 2002.

Changed paths:
  A gui/sessionselector.cpp
  A gui/sessionselector.h
    gui/module.mk
    gui/themes/common/highres_layout.stx
    gui/themes/common/lowres_layout.stx
    gui/themes/default.inc
    gui/themes/residualvm.zip
    gui/themes/scummclassic.zip
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx
    gui/themes/scummmodern.zip
    gui/themes/scummremastered.zip


diff --git a/gui/module.mk b/gui/module.mk
index e7db62dd677..3303ddc0acd 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -69,5 +69,12 @@ MODULE_OBJS += \
 	updates-dialog.o
 endif
 
+ifdef ENABLE_HE
+ifdef USE_ENET
+MODULE_OBJS += \
+	sessionselector.o
+endif
+endif
+
 # Include common rules
 include $(srcdir)/rules.mk
diff --git a/gui/sessionselector.cpp b/gui/sessionselector.cpp
new file mode 100644
index 00000000000..70e461aed74
--- /dev/null
+++ b/gui/sessionselector.cpp
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "engines/metaengine.h"
+#include "common/algorithm.h"
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/system.h"
+#include "common/taskbar.h"
+#include "common/translation.h"
+
+#include "engines/advancedDetector.h"
+
+#include "gui/sessionselector.h"
+
+namespace GUI {
+
+enum {
+	kOkCmd = 'OK  ',
+	kCancelCmd = 'CNCL'
+};
+
+SessionSelectorDialog::SessionSelectorDialog(Scumm::ScummEngine_v90he *vm)
+	: Dialog("SessionSelector"),
+	_vm(vm),
+	_joinButton(nullptr),
+	_queryProgressText(nullptr) {
+
+	_timestamp = 0;
+
+	_queryProgressText = new StaticTextWidget(this, "SessionSelector.QueryProgressText",
+						_("... progress ..."));
+
+	_queryProgressText->setAlign(Graphics::kTextAlignCenter);
+
+	_list = new ListWidget(this, "SessionSelector.SessionList");
+	_list->setEditable(false);
+	_list->setNumberingMode(kListNumberingOff);
+
+	_joinButton = new ButtonWidget(this, "SessionSelector.Join", _("Join"), Common::U32String(), kOkCmd, Common::ASCII_RETURN);
+	_joinButton->setEnabled(false);
+
+	new ButtonWidget(this, "SessionSelector.Cancel", _("Cancel"), Common::U32String(), kCancelCmd, Common::ASCII_ESCAPE);
+}
+
+void SessionSelectorDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch (cmd) {
+	case GUI::kListSelectionChangedCmd:
+		_joinButton->setEnabled(true);
+		break;
+	case GUI::kListItemDoubleClickedCmd:
+	case kOkCmd:
+		if (_list->getSelected() > -1) {
+			setResult(_list->getSelected());
+			close();
+		}
+		break;
+	case kCancelCmd:
+		// User cancelled, so we don't do anything and just leave.
+		setResult(-2);
+		close();
+		break;
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+	}
+}
+
+void SessionSelectorDialog::handleTickle() {
+	// Keep the connection active.
+	_vm->_net->doNetworkOnceAFrame(12);
+
+	// Query for new sessions every 5 seconds.
+	if (g_system->getMillis() - _timestamp > 5000) {
+		int numSessions = _vm->_net->querySessions();
+
+		// Clear list
+		Common::U32StringArray l;
+		_list->setList(l);
+
+		for (int i = 0; i < numSessions; i++) {
+			char name[MAX_SESSION_NAME];
+			_vm->_net->getSessionName(i, name, MAX_SESSION_NAME);
+			_list->append(name);
+		}
+
+		_timestamp = g_system->getMillis();
+	}
+
+	drawDialog(kDrawLayerForeground);
+}
+
+
+} // End of namespace GUI
diff --git a/gui/sessionselector.h b/gui/sessionselector.h
new file mode 100644
index 00000000000..51435b425be
--- /dev/null
+++ b/gui/sessionselector.h
@@ -0,0 +1,61 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SESSION_SELECTOR_DIALOG_H
+#define SESSION_SELECTOR_DIALOG_H
+
+#include "gui/dialog.h"
+#include "gui/widgets/list.h"
+#include "common/fs.h"
+#include "common/hashmap.h"
+#include "common/stack.h"
+#include "common/str.h"
+
+#include "scumm/he/intern_he.h"
+#include "scumm/he/net/net_main.h"
+#include "scumm/he/net/net_defines.h"
+
+namespace GUI {
+
+class StaticTextWidget;
+
+class SessionSelectorDialog : public Dialog {
+public:
+	SessionSelectorDialog(Scumm::ScummEngine_v90he *vm);
+
+	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) override;
+	void handleTickle() override;
+
+private:
+	Scumm::ScummEngine_v90he *_vm;
+
+	uint32 _timestamp;
+
+	Widget *_joinButton;
+	StaticTextWidget *_queryProgressText;
+
+	ListWidget *_list;
+};
+
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/themes/common/highres_layout.stx b/gui/themes/common/highres_layout.stx
index 29bb5301638..9df99895bfd 100644
--- a/gui/themes/common/highres_layout.stx
+++ b/gui/themes/common/highres_layout.stx
@@ -2233,4 +2233,25 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SessionSelector' overlays = 'screen_center' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 32, 8' align = 'center'>
+			<widget name = 'QueryProgressText'
+					width = '480'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'SessionList'
+					width = '480'
+					height = '250'
+			/>
+			<layout type = 'horizontal' padding = '8, 8, 8, 8'>
+				<widget name = 'Join'
+						type = 'Button'
+				/>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>
diff --git a/gui/themes/common/lowres_layout.stx b/gui/themes/common/lowres_layout.stx
index d77bd40234c..3090f6c1809 100644
--- a/gui/themes/common/lowres_layout.stx
+++ b/gui/themes/common/lowres_layout.stx
@@ -2153,4 +2153,25 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SessionSelector' overlays = 'screen_center' shading = 'dim'>
+		<layout type = 'vertical' padding = '4, 4, 16, 4' align = 'center'>
+			<widget name = 'QueryProgressText'
+					width = '480'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'SessionList'
+					width = '480'
+					height = '250'
+			/>
+			<layout type = 'horizontal' padding = '8, 8, 8, 8'>
+				<widget name = 'Join'
+						type = 'Button'
+				/>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index df6b58ef7e0..6839208f939 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -3409,6 +3409,26 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='SessionSelector' overlays='screen_center' shading='dim'>"
+"<layout type='vertical' padding='8,8,32,8' align='center'>"
+"<widget name='QueryProgressText' "
+"width='480' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='SessionList' "
+"width='480' "
+"height='250' "
+"/>"
+"<layout type='horizontal' padding='8,8,8,8'>"
+"<widget name='Join' "
+"type='Button' "
+"/>"
+"<widget name='Cancel' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "</layout_info>"
 ;
  const char *defaultXML4 = "<layout_info resolution='y<400'>"
@@ -5364,6 +5384,26 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='SessionSelector' overlays='screen_center' shading='dim'>"
+"<layout type='vertical' padding='4,4,16,4' align='center'>"
+"<widget name='QueryProgressText' "
+"width='480' "
+"height='Globals.Line.Height' "
+"/>"
+"<widget name='SessionList' "
+"width='480' "
+"height='250' "
+"/>"
+"<layout type='horizontal' padding='8,8,8,8'>"
+"<widget name='Join' "
+"type='Button' "
+"/>"
+"<widget name='Cancel' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "</layout_info>"
 ;
 const char *defaultXML[] = { defaultXML1, defaultXML2, defaultXML3, defaultXML4 };
diff --git a/gui/themes/residualvm.zip b/gui/themes/residualvm.zip
index c93386c9a9b..d31953d0150 100644
Binary files a/gui/themes/residualvm.zip and b/gui/themes/residualvm.zip differ
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index 99bae0a9616..71513d1d777 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 4ce5c778ae8..3d2bc5aea2a 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -2068,4 +2068,25 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SessionSelector' overlays = 'screen_center' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 32, 8' align = 'center'>
+			<widget name = 'QueryProgressText'
+					width = '480'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'SessionList'
+					width = '480'
+					height = '250'
+			/>
+			<layout type = 'horizontal' padding = '8, 8, 8, 8'>
+				<widget name = 'Join'
+						type = 'Button'
+				/>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 2162d15832e..e63fe9f9ad5 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -2052,4 +2052,25 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'SessionSelector' overlays = 'screen_center' shading = 'dim'>
+		<layout type = 'vertical' padding = '4, 4, 16, 4' align = 'center'>
+			<widget name = 'QueryProgressText'
+					width = '480'
+					height = 'Globals.Line.Height'
+			/>
+			<widget name = 'SessionList'
+					width = '480'
+					height = '250'
+			/>
+			<layout type = 'horizontal' padding = '8, 8, 8, 8'>
+				<widget name = 'Join'
+						type = 'Button'
+				/>
+				<widget name = 'Cancel'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index f8bef2ef97a..942ae123b55 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummremastered.zip b/gui/themes/scummremastered.zip
index 5a8d5d773db..61f3b23b148 100644
Binary files a/gui/themes/scummremastered.zip and b/gui/themes/scummremastered.zip differ


Commit: 893eb485f8f53615835d13454db350343be628e6
    https://github.com/scummvm/scummvm/commit/893eb485f8f53615835d13454db350343be628e6
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Use new online session dialog.

Changed paths:
    engines/scumm/he/intern_he.h
    engines/scumm/he/logic/football.cpp
    engines/scumm/script_v6.cpp
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index cec294d808a..837514f43bb 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -737,6 +737,9 @@ protected:
 	byte VAR_REMOTE_START_SCRIPT;
 	byte VAR_NETWORK_AVAILABLE;
 	byte VAR_NETWORK_RECEIVE_ARRAY_SCRIPT;
+
+public:
+	int networkSessionDialog();
 #endif
 };
 
diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index 77c5977a679..e9e89449eaa 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -526,6 +526,8 @@ private:
 	float _angle;
 	int32 _maxX;
 	int32 _minX;
+
+	int _requestedSessionId = -2;
 };
 
 int32 LogicHEfootball2002::dispatch(int op, int numArgs, int32 *args) {
@@ -569,6 +571,13 @@ int32 LogicHEfootball2002::dispatch(int op, int numArgs, int32 *args) {
 	case OP_NET_SET_PROVIDER_BY_NAME:
 #ifdef USE_ENET
 		res = _vm->_net->setProviderByName(args[0], args[1]);
+		if (res)
+			_requestedSessionId = _vm->networkSessionDialog();
+		if (_requestedSessionId == -2) {
+			// Cancelled out the join dialog.
+			_vm->_net->closeProvider();
+			res = 0;
+		}
 #endif
 		break;
 
@@ -578,9 +587,9 @@ int32 LogicHEfootball2002::dispatch(int op, int numArgs, int32 *args) {
 		break;
 
 	case OP_NET_QUERY_SESSIONS:
-		// TODO: Replace the in-game session querying with
-		// our own GUI.
-		res = _vm->_net->querySessions();
+		if (_requestedSessionId > -1)
+			// Emulate that we've found a session.
+			res = 1;
 		break;
 
 	case OP_NET_GET_SESSION_NAME:
@@ -731,8 +740,8 @@ int LogicHEfootball2002::largestFreeBlock() {
 
 #ifdef USE_ENET
 int LogicHEfootball2002::netGetSessionName(int index) {
-	char name[MAX_PROVIDER_NAME];
-	_vm->_net->getSessionName(index - 1, name, sizeof(name));
+	char name[MAX_SESSION_NAME];
+	_vm->_net->getSessionName(_requestedSessionId, name, sizeof(name));
 	return _vm->setupStringArrayFromString(name);
 }
 
@@ -753,11 +762,9 @@ int LogicHEfootball2002::netInitLanGame(int32 *args) {
 		// Stop querying sessions if we haven't already
 		_vm->_net->stopQuerySessions();
 		// And host our new game.
-
 		res = _vm->_net->hostGame(sessionName, userName);
 	} else {
-		// TODO: Join via session name
-		res = _vm->_net->joinSession(0);
+		res = _vm->_net->joinSession(_requestedSessionId);
 		if (res)
 			_vm->_net->addUser(userName, userName);
 		_vm->_net->stopQuerySessions();
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index c95afeb08c5..3e49c3dbe5b 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -551,7 +551,31 @@ void ScummEngine_v6::o6_neq() {
 
 void ScummEngine_v6::o6_gt() {
 	int a = pop();
-	push(pop() > a);
+	int b = pop();
+
+	// WORKAROUND: In Football 2002, when hosting an Network game, it would eventually timeout,
+	// which causes the game to stop hosting and query for sessions again.
+	//
+	// [016C] (39)     localvar8++
+	// [016F] (36)     if (localvar8 > localvar12) {
+	// [0179] (54)       printDebug.begin()
+	// [017B] (54)       printDebug.msg("Host Timeout")
+	// [018A] (7C)       startScript(90,220,[])
+	// [0190] (7C)       startScript(90,2051,[])
+	// [0197] (7C)       startScript(90,2054,[])
+	// [019E] (80)       stopScript(0)
+	// [01A1] (**)     }
+	//
+	// We have our own session selection dialog which allows the user to host or join a session as
+	// they please; we do not want them to go through the whole setup again after the timeout
+	// so let's just make unreachable, allowing the session to be hosted indefinitely until
+	// they cancel it out.
+	if (_game.id == GID_FOOTBALL2002 && _currentRoom == 3 && vm.slot[_currentScript].number == 2052) {
+		push(0);
+		return;
+	}
+
+	push(b > a);
 }
 
 void ScummEngine_v6::o6_lt() {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 779450a5bfa..6d0645df23d 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -87,12 +87,15 @@
 #include "scumm/imuse/drivers/midi.h"
 #include "scumm/detection_steam.h"
 
+#ifdef ENABLE_HE
 #ifdef USE_ENET
 #include "scumm/he/net/net_main.h"
+#include "gui/sessionselector.h"
 #ifdef USE_LIBCURL
 #include "scumm/he/net/net_lobby.h"
 #endif
 #endif
+#endif
 
 #include "backends/audiocd/audiocd.h"
 
@@ -3454,6 +3457,19 @@ bool ScummEngine::displayMessageYesNo(const char *message, ...) {
 	return runDialog(dialog) == GUI::kMessageOK;
 }
 
+#ifdef ENABLE_HE
+int ScummEngine_v90he::networkSessionDialog() {
+	GUI::MessageDialog dialog(_("Would you like to host or join a network play session?"), _("Host"), _("Join"));
+	int res = runDialog(dialog);
+	if (res == GUI::kMessageOK)
+		// Hosting session.
+		return -1;
+	
+	// Joining a session
+	GUI::SessionSelectorDialog sessionDialog(this);
+	return runDialog(sessionDialog);
+}
+#endif
 
 #pragma mark -
 #pragma mark --- Miscellaneous ---


Commit: b8ac9ed46ec6260ce6e593fc4a06caff64e41f61
    https://github.com/scummvm/scummvm/commit/b8ac9ed46ec6260ce6e593fc4a06caff64e41f61
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Revert back to using "get_host_ip"

Changed paths:
    backends/networking/enet/host.cpp
    backends/networking/enet/socket.cpp


diff --git a/backends/networking/enet/host.cpp b/backends/networking/enet/host.cpp
index 45f72a12b62..df77ed9426b 100644
--- a/backends/networking/enet/host.cpp
+++ b/backends/networking/enet/host.cpp
@@ -54,7 +54,7 @@ uint8 Host::service(int timeout) {
 
 	if (event.type > ENET_EVENT_TYPE_NONE) {
 		char hostName[50];
-		if (enet_address_get_host(&event.peer->address, hostName, 50) == 0)
+		if (enet_address_get_host_ip(&event.peer->address, hostName, 50) == 0)
 			_recentHost = Common::String(hostName);
 		_recentPort = event.peer->address.port;
 	}
@@ -124,11 +124,6 @@ int Host::getPort() {
 int Host::getPeerIndexFromHost(Common::String host, int port) {
 	for (int i = 0; i < (int)_host->peerCount; i++) {
 		char hostName[50];
-		if (enet_address_get_host(&_host->peers[i].address, hostName, 50) == 0) {
-			if (host == hostName && port == _host->peers[i].address.port) {
-				return i;
-			}
-		}
 		if (enet_address_get_host_ip(&_host->peers[i].address, hostName, 50) == 0) {
 			if (host == hostName && port == _host->peers[i].address.port) {
 				return i;
diff --git a/backends/networking/enet/socket.cpp b/backends/networking/enet/socket.cpp
index 36a6958b396..c46a2da0772 100644
--- a/backends/networking/enet/socket.cpp
+++ b/backends/networking/enet/socket.cpp
@@ -79,7 +79,7 @@ bool Socket::receive() {
 	_recentData = Common::String((const char*)data, receivedLength);
 
 	char hostName[50];
-	if (enet_address_get_host(&_address, hostName, sizeof(hostName)) == 0)
+	if (enet_address_get_host_ip(&_address, hostName, sizeof(hostName)) == 0)
 		_recentHost = hostName;
 	else
 		_recentHost = "";


Commit: c14481e534616b369a80f9be817fb7523b4dd0a2
    https://github.com/scummvm/scummvm/commit/c14481e534616b369a80f9be817fb7523b4dd0a2
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Hack fix when connecting via domain.

Changed paths:
    engines/scumm/he/net/net_main.cpp


diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 3b4ebb74809..5d64eab721a 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -269,7 +269,10 @@ int Net::createSession(char *name) {
 	_isHost = true;
 	if (ConfMan.getBool("enable_session_server")) {
 		if (_sessionHost->connectPeer(_sessionServerAddress.host, _sessionServerAddress.port)) {
-			_sessionServerPeer = _sessionHost->getPeerIndexFromHost(_sessionServerAddress.host, _sessionServerAddress.port);
+			// FIXME: Get the IP address of the session server when a domain address is used. 
+
+			// _sessionServerPeer = _sessionHost->getPeerIndexFromHost(_sessionServerAddress.host, _sessionServerAddress.port);
+			_sessionServerPeer = 0;
 			// Create session to the session server.
 			Common::String req = Common::String::format(
 				"{\"cmd\":\"host_session\",\"game\":\"%s\",\"version\":\"%s\",\"name\":\"%s\"}",


Commit: cfc08b5d97ebc5c74e3e33412bcf111575245c8f
    https://github.com/scummvm/scummvm/commit/cfc08b5d97ebc5c74e3e33412bcf111575245c8f
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Always send football messages reliably.

After testing with a friend, the game seems to be a lot stable if
priority is always set to high, then so be it.

Changed paths:
    engines/scumm/he/logic/football.cpp


diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index e9e89449eaa..b4a261ece5d 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -453,9 +453,7 @@ int LogicHEfootball::computeTwoCircleIntercepts(int32 *args) {
 
 #ifdef USE_ENET
 void LogicHEfootball::netRemoteStartScript(int numArgs, int32 *args) {
-	int priority = 0;
-	if (args[0] >= 15)
-		priority = PN_PRIORITY_HIGH;
+	int priority = PN_PRIORITY_HIGH;
 
 	int targetUserId;
 	if (_vm->_net->_isHost)
@@ -467,9 +465,7 @@ void LogicHEfootball::netRemoteStartScript(int numArgs, int32 *args) {
 }
 
 void LogicHEfootball::netRemoteSendArray(int32 *args) {
-	int priority = 0;
-	if (args[0] >= 10)
-		priority = PN_PRIORITY_HIGH;
+	int priority = PN_PRIORITY_HIGH;
 
 	int targetUserId;
 	if (_vm->_net->_isHost)


Commit: 5d79f212d226a98b3b4b5d82b5418c43d8fd017c
    https://github.com/scummvm/scummvm/commit/5d79f212d226a98b3b4b5d82b5418c43d8fd017c
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Fix network not running on missing config.

Changed paths:
    engines/scumm/he/net/net_main.cpp
    gui/sessionselector.cpp


diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 5d64eab721a..06773aced61 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -267,9 +267,17 @@ int Net::createSession(char *name) {
 	}
 
 	_isHost = true;
-	if (ConfMan.getBool("enable_session_server")) {
+
+	bool enableSessionServer = true;
+	bool enableLanBroadcast = true;
+	if (ConfMan.hasKey("enable_session_server"))
+		enableSessionServer = ConfMan.getBool("enable_session_server");
+	if (ConfMan.hasKey("enable_lan_broadcast"))
+		enableLanBroadcast = ConfMan.getBool("enable_lan_broadcast");
+
+	if (enableSessionServer) {
 		if (_sessionHost->connectPeer(_sessionServerAddress.host, _sessionServerAddress.port)) {
-			// FIXME: Get the IP address of the session server when a domain address is used. 
+			// FIXME: Get the IP address of the session server when a domain address is used.
 
 			// _sessionServerPeer = _sessionHost->getPeerIndexFromHost(_sessionServerAddress.host, _sessionServerAddress.port);
 			_sessionServerPeer = 0;
@@ -284,7 +292,7 @@ int Net::createSession(char *name) {
 		}
 	}
 
-	if (ConfMan.getBool("enable_lan_broadcast")) {
+	if (enableLanBroadcast) {
 		_broadcastSocket = _enet->createSocket("0.0.0.0", 9130);
 		if (!_broadcastSocket) {
 			warning("NETWORK: Unable to create broadcast socket, your game will not be broadcast over LAN");
@@ -512,7 +520,14 @@ bool Net::destroyPlayer(int32 userId) {
 int32 Net::startQuerySessions(bool connectToSessionServer) {
 	debug(1, "Net::startQuerySessions()");
 
-	if (connectToSessionServer && ConfMan.getBool("enable_session_server")) {
+	bool enableSessionServer = true;
+	bool enableLanBroadcast = true;
+	if (ConfMan.hasKey("enable_session_server"))
+		enableSessionServer = ConfMan.getBool("enable_session_server");
+	if (ConfMan.hasKey("enable_lan_broadcast"))
+		enableLanBroadcast = ConfMan.getBool("enable_lan_broadcast");
+
+	if (connectToSessionServer && enableSessionServer) {
 		if (!_sessionServerHost) {
 			_sessionServerHost = _enet->connectToHost(_sessionServerAddress.host, _sessionServerAddress.port);
 			if (!_sessionServerHost)
@@ -520,7 +535,7 @@ int32 Net::startQuerySessions(bool connectToSessionServer) {
 		}
 	}
 
-	if (ConfMan.getBool("enable_lan_broadcast") && !_broadcastSocket) {
+	if (enableLanBroadcast && !_broadcastSocket) {
 		_broadcastSocket = _enet->createSocket("0.0.0.0", 0);
 	}
 	return 0;
diff --git a/gui/sessionselector.cpp b/gui/sessionselector.cpp
index 70e461aed74..dfa9ab5dae0 100644
--- a/gui/sessionselector.cpp
+++ b/gui/sessionselector.cpp
@@ -47,7 +47,7 @@ SessionSelectorDialog::SessionSelectorDialog(Scumm::ScummEngine_v90he *vm)
 	_timestamp = 0;
 
 	_queryProgressText = new StaticTextWidget(this, "SessionSelector.QueryProgressText",
-						_("... progress ..."));
+						_("Querying games..."));
 
 	_queryProgressText->setAlign(Graphics::kTextAlignCenter);
 
@@ -64,6 +64,7 @@ SessionSelectorDialog::SessionSelectorDialog(Scumm::ScummEngine_v90he *vm)
 void SessionSelectorDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case GUI::kListSelectionChangedCmd:
+		_timestamp = g_system->getMillis();
 		_joinButton->setEnabled(true);
 		break;
 	case GUI::kListItemDoubleClickedCmd:
@@ -88,7 +89,7 @@ void SessionSelectorDialog::handleTickle() {
 	_vm->_net->doNetworkOnceAFrame(12);
 
 	// Query for new sessions every 5 seconds.
-	if (g_system->getMillis() - _timestamp > 5000) {
+	if (!_timestamp || g_system->getMillis() - _timestamp > 5000) {
 		int numSessions = _vm->_net->querySessions();
 
 		// Clear list
@@ -101,6 +102,10 @@ void SessionSelectorDialog::handleTickle() {
 			_list->append(name);
 		}
 
+		_joinButton->setEnabled(false);
+		// Update the dialog
+		_queryProgressText->setLabel(Common::U32String::format(_("Found %d available games."), l.size()));
+
 		_timestamp = g_system->getMillis();
 	}
 


Commit: 1cf1f302add0f702317466de04ab9e058216d845
    https://github.com/scummvm/scummvm/commit/1cf1f302add0f702317466de04ab9e058216d845
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: LIBCURL: URL Parsing.

Changed paths:
  A backends/networking/curl/url.cpp
  A backends/networking/curl/url.h
    backends/module.mk
    backends/networking/curl/socket.h


diff --git a/backends/module.mk b/backends/module.mk
index a0ccfeebc7c..14a052db777 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -78,7 +78,8 @@ MODULE_OBJS += \
 	networking/curl/request.o \
 	networking/curl/session.o \
 	networking/curl/sessionrequest.o \
-	networking/curl/socket.o
+	networking/curl/socket.o \
+	networking/curl/url.o
 endif
 
 ifdef USE_SDL_NET
diff --git a/backends/networking/curl/socket.h b/backends/networking/curl/socket.h
index bf670d957bc..5d95129a26a 100644
--- a/backends/networking/curl/socket.h
+++ b/backends/networking/curl/socket.h
@@ -55,6 +55,4 @@ private:
 
 } // End of namespace Networking
 
-
-
 #endif
\ No newline at end of file
diff --git a/backends/networking/curl/url.cpp b/backends/networking/curl/url.cpp
new file mode 100644
index 00000000000..2e577a8480d
--- /dev/null
+++ b/backends/networking/curl/url.cpp
@@ -0,0 +1,128 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+#include "backends/networking/curl/url.h"
+#include "common/debug.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+CurlURL::CurlURL() {
+	_url = nullptr;
+}
+
+// This requires libcurl version 7.62.0, If we're using
+// a lower version, stub all of this.
+#if LIBCURL_VERSION_NUM < 0x073E00
+CurlURL::~CurlURL() {
+}
+
+Common::String CurlURL::getScheme() {
+	return "";
+}
+
+Common::String CurlURL::getHost() {
+	return "";
+}
+
+int CurlURL::getPort(bool defaultPort) {
+	return -1;
+}
+
+bool CurlURL::parseURL(Common::String url) {
+	warning("libcurl: curl_url requires curl 7.62.0 or later");
+	return false;
+}
+
+#else
+
+CurlURL::~CurlURL() {
+	if (_url) {
+		curl_url_cleanup(_url);
+		_url = nullptr;
+	}
+}
+
+bool CurlURL::parseURL(Common::String url) {
+	if (_url)
+		curl_url_cleanup(_url);
+
+	_url = curl_url();
+	if (!_url) {
+		warning("libcurl: Could not create curl_url handle");
+		return false;
+	}
+	CURLUcode rc = curl_url_set(_url, CURLUPART_URL, url.c_str(), 0);
+	if (rc) {
+		warning("libcurl: Unable to parse URL: \"%s\"", url.c_str());
+		return false;
+	}
+	return true;
+}
+
+Common::String CurlURL::getScheme() {
+	if (!_url)
+		return "";
+	char *scheme;
+	CURLUcode rc = curl_url_get(_url, CURLUPART_SCHEME, &scheme, 0);
+	if (rc) {
+		warning("libcurl: Unable to get scheme");
+		return "";
+	}
+	Common::String schemeString(scheme);
+	curl_free(scheme);
+	return schemeString;
+}
+
+Common::String CurlURL::getHost() {
+	if (!_url)
+		return "";
+	char *host;
+	CURLUcode rc = curl_url_get(_url, CURLUPART_HOST, &host, 0);
+	if (rc) {
+		warning("libcurl: Unable to get host");
+		return "";
+	}
+	Common::String hostString(host);
+	curl_free(host);
+	return hostString;
+}
+
+int CurlURL::getPort(bool defaultPort) {
+	if (!_url)
+		return -1;
+
+	char *portChr;
+	CURLUcode rc = curl_url_get(_url, CURLUPART_PORT, &portChr, (defaultPort) ? CURLU_DEFAULT_PORT : CURLU_NO_DEFAULT_PORT);
+	if (rc) {
+		if (rc == CURLUE_NO_PORT)
+			return 0;
+		warning("libcurl: Unable to get port");
+		return -1;
+	}
+	int port = atoi(portChr);
+	curl_free(portChr);
+	return port;
+}
+#endif
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/url.h b/backends/networking/curl/url.h
new file mode 100644
index 00000000000..7255221b72c
--- /dev/null
+++ b/backends/networking/curl/url.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef BACKENDS_NETWORKING_CURL_URL_H
+#define BACKENDS_NETWORKING_CURL_URL_H
+
+typedef struct Curl_URL CURLU;
+
+#include "common/str.h"
+
+namespace Networking {
+
+class CurlURL {
+public:
+	CurlURL();
+	~CurlURL();
+
+	bool parseURL(Common::String url);
+
+	Common::String getScheme();
+	Common::String getHost();
+	int getPort(bool returnDefault = false);
+	Common::String getPath();
+private:
+	CURLU *_url;
+};
+
+} // End of Namespace Networking
+
+#endif
\ No newline at end of file


Commit: 5b92f8fe97ae12b4838e1f0a0886fb96005d26cd
    https://github.com/scummvm/scummvm/commit/5b92f8fe97ae12b4838e1f0a0886fb96005d26cd
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Configurable lobby server and checking.

Changed paths:
    engines/scumm/detection.h
    engines/scumm/detection_tables.h
    engines/scumm/dialogs.cpp
    engines/scumm/dialogs.h
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h
    engines/scumm/metaengine.cpp


diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h
index 51927c822ef..5ccde31d359 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -34,6 +34,7 @@ namespace Scumm {
 #define GUIO_AUDIO_OVERRIDE                            GUIO_GAMEOPTIONS3
 #define GUIO_ORIGINALGUI                               GUIO_GAMEOPTIONS4
 #define GUIO_LOWLATENCYAUDIO                           GUIO_GAMEOPTIONS5
+#define GUIO_NETWORK                                   GUIO_GAMEOPTIONS6
 
 /**
  * Descriptor of a specific SCUMM game. Used internally to store
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index 4092f8b5ef7..2e7fa664e12 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -320,7 +320,7 @@ static const GameSettings gameVariantsTable[] = {
 	{"freddi4", "unenc",  0, GID_FREDDI4, 6, 98, MDT_NONE,              GF_HE_985, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
 
 	// Humongous Entertainment Scumm Version 9.9 ?  Scummsys.99
-	{"football", 0, 0, GID_FOOTBALL, 6, 99, MDT_NONE, GF_USE_KEY, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
+	{"football", 0, 0, GID_FOOTBALL, 6, 99, MDT_NONE, GF_USE_KEY, UNK, GUIO5(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE, GUIO_NETWORK)},
 	{"pajama3", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
 	{"puttcircus", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_HE_NO_BIDI, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
 	{"spyfox2", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
@@ -336,7 +336,7 @@ static const GameSettings gameVariantsTable[] = {
 
 	// Added 16bit color
 	{"arttime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
-	{"baseball2001", 0, 0, GID_BASEBALL2001, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
+	{"baseball2001", 0, 0, GID_BASEBALL2001, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO5(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE, GUIO_NETWORK)},
 	{"readtime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
 	{"SoccerMLS", 0, 0, GID_SOCCERMLS, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
 	{"spyozon", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_AUDIO_OVERRIDE)},
@@ -352,14 +352,14 @@ static const GameSettings gameVariantsTable[] = {
 	{"Soccer2004", 0, 0, GID_SOCCER2004, 6, 100, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
 
 	// U32 code required, for testing only
-	{"moonbase", "1.0", 0, GID_MOONBASE, 6, 100, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
-	{"moonbase", "1.1", 0, GID_MOONBASE, 6, 100, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
-	{"moonbase", "Demo", 0, GID_MOONBASE, 6, 100, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR | GF_DEMO, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
+	{"moonbase", "1.0", 0, GID_MOONBASE, 6, 100, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_NETWORK)},
+	{"moonbase", "1.1", 0, GID_MOONBASE, 6, 100, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_NETWORK)},
+	{"moonbase", "Demo", 0, GID_MOONBASE, 6, 100, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR | GF_DEMO, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_NETWORK)},
 
 	// HE100 games, which use older o72_debugInput code
 	{"Baseball2003", 0, 0, GID_BASEBALL2003, 6, 101, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
 	{"basketball", 0, 0, GID_BASKETBALL, 6, 101, MDT_NONE, GF_USE_KEY| GF_16BIT_COLOR, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
-	{"football2002", 0, 0, GID_FOOTBALL2002, 6, 101, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO3(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT)},
+	{"football2002", 0, 0, GID_FOOTBALL2002, 6, 101, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO4(GUIO_NOLAUNCHLOAD, GUIO_NOMIDI, GUIO_NOASPECT, GUIO_NETWORK)},
 
 	// The following are meant to be generic HE game variants and as such do
 	// not specify a game ID. Make sure that these are last in the table, else
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 18876754f52..b55cb30393b 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -1351,63 +1351,105 @@ void MI1CdGameOptionsWidget::updateOutlookAdjustmentValue() {
 #ifdef USE_ENET
 // HE Network Play Adjustment settings
 
-HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
-		ScummOptionsContainerWidget(boss, name, "HENetworkGameOptionsDialog", domain) {
+HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain, Common::String gameid) :
+		ScummOptionsContainerWidget(boss, name, "HENetworkGameOptionsDialog", domain), _gameid(gameid) {
 	Common::String extra = ConfMan.get("extra", domain);
 
+	// TODO: Re-add "Load modded audio" option.
+
 	GUI::StaticTextWidget *text = new GUI::StaticTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.SessionServerLabel", _("Multiplayer Server:"));
 
 	text->setAlign(Graphics::TextAlign::kTextAlignEnd);
 
-	_enableSessionServer = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.EnableSessionServer", _("Enable connection to Multiplayer Server"), _("Toggles the connection to the server that allows hosting and joining online multiplayer games over the Internet."), kEnableSessionCmd);
-	_enableLANBroadcast = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.EnableLANBroadcast", _("Host games over LAN"), _("Allows the game sessions to be discovered over your local area network."));
+	if (_gameid == "football" || _gameid == "baseball2001") {
+		// Lobby configuration (Do not include LAN settings)
+#ifdef USE_LIBCURL
+		text->setLabel(_("Online Server:"));
+		_lobbyServerAddr = new GUI::EditTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.LobbyServerAddress", Common::U32String(""), _("Address of the server to connect to for online play."));
+		_serverResetButton = addClearButton(widgetsBoss(), "HENetworkGameOptionsDialog.ServerReset", kResetServersCmd);
+#endif
+	} else {
+		// Network configuration (Include LAN settings)
+		_enableSessionServer = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.EnableSessionServer", _("Enable connection to Multiplayer Server"), _("Toggles the connection to the server that allows hosting and joining online multiplayer games over the Internet."), kEnableSessionCmd);
+		_enableLANBroadcast = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.EnableLANBroadcast", _("Host games over LAN"), _("Allows the game sessions to be discovered over your local area network."));
 
-	_sessionServerAddr = new GUI::EditTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.SessionServerAddress", Common::U32String(""), _("Address of the server to connect to for hosting and joining online game sessions."));
+		_sessionServerAddr = new GUI::EditTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.SessionServerAddress", Common::U32String(""), _("Address of the server to connect to for hosting and joining online game sessions."));
 
-	_serverResetButton = addClearButton(widgetsBoss(), "HENetworkGameOptionsDialog.ServerReset", kResetServersCmd);
+		_serverResetButton = addClearButton(widgetsBoss(), "HENetworkGameOptionsDialog.ServerReset", kResetServersCmd);
+	}
 }
 
 void HENetworkGameOptionsWidget::load() {
-	bool enableSessionServer = true;
-	bool enableLANBroadcast = true;
-	Common::String sessionServerAddr = "multiplayer.scummvm.org";
-
-	if (ConfMan.hasKey("enable_session_server", _domain))
-		enableSessionServer = ConfMan.getBool("enable_session_server", _domain);
-	_enableSessionServer->setState(enableSessionServer);
-
-	if (ConfMan.hasKey("enable_lan_broadcast", _domain))
-		enableLANBroadcast = ConfMan.getBool("enable_lan_broadcast", _domain);
-	_enableLANBroadcast->setState(enableLANBroadcast);
-
-	if (ConfMan.hasKey("session_server", _domain))
-		sessionServerAddr = ConfMan.get("session_server", _domain);
-	_sessionServerAddr->setEditString(sessionServerAddr);
-	_sessionServerAddr->setEnabled(enableSessionServer);
-
+	if (_gameid == "football" || _gameid == "baseball2001") {
+#ifdef USE_LIBCURL
+		Common::String lobbyServerAddr = "https://multiplayer.scummvm.org:9130";
+		if (ConfMan.hasKey("lobby_server", _domain))
+			lobbyServerAddr = ConfMan.get("lobby_server", _domain);
+		_lobbyServerAddr->setEditString(lobbyServerAddr);
+#endif
+	} else {
+		bool enableSessionServer = true;
+		bool enableLANBroadcast = true;
+		Common::String sessionServerAddr = "multiplayer.scummvm.org";
+
+		if (ConfMan.hasKey("enable_session_server", _domain))
+			enableSessionServer = ConfMan.getBool("enable_session_server", _domain);
+		_enableSessionServer->setState(enableSessionServer);
+
+		if (ConfMan.hasKey("enable_lan_broadcast", _domain))
+			enableLANBroadcast = ConfMan.getBool("enable_lan_broadcast", _domain);
+		_enableLANBroadcast->setState(enableLANBroadcast);
+
+		if (ConfMan.hasKey("session_server", _domain))
+			sessionServerAddr = ConfMan.get("session_server", _domain);
+		_sessionServerAddr->setEditString(sessionServerAddr);
+		_sessionServerAddr->setEnabled(enableSessionServer);
+	}
 }
 
 bool HENetworkGameOptionsWidget::save() {
-	ConfMan.setBool("enable_session_server", _enableSessionServer->getState(), _domain);
-	ConfMan.setBool("enable_lan_broadcast", _enableLANBroadcast->getState(), _domain);
-	ConfMan.set("session_server", _sessionServerAddr->getEditString(), _domain);
+	if (_gameid == "football" || _gameid == "baseball2001") {
+#ifdef USE_LIBCURL
+		ConfMan.set("lobby_server", _lobbyServerAddr->getEditString(), _domain);
+#endif
+	} else {
+		ConfMan.setBool("enable_session_server", _enableSessionServer->getState(), _domain);
+		ConfMan.setBool("enable_lan_broadcast", _enableLANBroadcast->getState(), _domain);
+		ConfMan.set("session_server", _sessionServerAddr->getEditString(), _domain);
+	}
 	return true;
 }
 
 void HENetworkGameOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
-	layouts.addDialog(layoutName, overlayedLayout)
-		.addLayout(GUI::ThemeLayout::kLayoutVertical, 5)
-			.addPadding(0, 0, 12, 0)
-			.addWidget("EnableSessionServer", "Checkbox")
-			.addWidget("EnableLANBroadcast", "Checkbox")
-			.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 12)
+	if (_gameid == "football" || _gameid == "baseball2001") {
+#ifdef USE_LIBCURL
+		layouts.addDialog(layoutName, overlayedLayout)
+			.addLayout(GUI::ThemeLayout::kLayoutVertical, 5)
 				.addPadding(0, 0, 12, 0)
-				.addWidget("SessionServerLabel", "OptionsLabel")
-				.addWidget("SessionServerAddress", "EditTextWidget")
-				.addWidget("ServerReset", "", 15, 15)
+				.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 12)
+					.addPadding(0, 0, 12, 0)
+					.addWidget("SessionServerLabel", "OptionsLabel")
+					.addWidget("LobbyServerAddress", "EditTextWidget")
+					.addWidget("ServerReset", "", 15, 15)
+				.closeLayout()
 			.closeLayout()
-		.closeLayout()
-	.closeDialog();
+		.closeDialog();
+#endif
+	} else {
+		layouts.addDialog(layoutName, overlayedLayout)
+			.addLayout(GUI::ThemeLayout::kLayoutVertical, 5)
+				.addPadding(0, 0, 12, 0)
+				.addWidget("EnableSessionServer", "Checkbox")
+				.addWidget("EnableLANBroadcast", "Checkbox")
+				.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 12)
+					.addPadding(0, 0, 12, 0)
+					.addWidget("SessionServerLabel", "OptionsLabel")
+					.addWidget("SessionServerAddress", "EditTextWidget")
+					.addWidget("ServerReset", "", 15, 15)
+				.closeLayout()
+			.closeLayout()
+		.closeDialog();
+	}
 }
 
 void HENetworkGameOptionsWidget::handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) {
@@ -1418,8 +1460,12 @@ void HENetworkGameOptionsWidget::handleCommand(GUI::CommandSender *sender, uint3
 		g_gui.scheduleTopDialogRedraw();
 		break;
 	case kResetServersCmd:
-		_enableSessionServer->setState(true);
-		_sessionServerAddr->setEditString(Common::U32String("multiplayer.scummvm.org"));
+		if (_gameid == "football" || _gameid == "baseball2001") {
+			_lobbyServerAddr->setEditString(Common::U32String("https://multiplayer.scummvm.org:9130"));
+		} else {
+			_enableSessionServer->setState(true);
+			_sessionServerAddr->setEditString(Common::U32String("multiplayer.scummvm.org"));
+		}
 		g_gui.scheduleTopDialogRedraw();
 		break;
 	default:
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index a2b634f3a8a..0e8fb922be9 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -326,7 +326,7 @@ private:
  */
 class HENetworkGameOptionsWidget : public ScummOptionsContainerWidget {
 public:
-	HENetworkGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
+	HENetworkGameOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain, const Common::String gameid);
 	~HENetworkGameOptionsWidget() override {};
 
 	void load() override;
@@ -338,6 +338,8 @@ private:
 		kResetServersCmd = 'CLRS',
 	};
 
+	Common::String _gameid;
+
 	void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
 	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
 
@@ -347,6 +349,9 @@ private:
 	GUI::ButtonWidget *_serverResetButton;
 
 	GUI::CheckboxWidget *_enableLANBroadcast;
+
+	GUI::EditTextWidget *_lobbyServerAddr;
+	GUI::ButtonWidget *_lobbyResetButton;
 };
 #endif
 
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 779ab88973a..85a175b7adc 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -152,13 +152,48 @@ bool Lobby::connect() {
 	// NOTE: Even though the protocol starts with http(s), this is an entirely
 	// different protocol.  This is done so we can achieve communicating over
 	// TLS/SSL sockets.
-	// TODO: custom url
-	Common::String url = "http://127.0.0.1:9130";
+	Common::String lobbyUrl = "https://multiplayer.scummvm.org:9130";
+	if (ConfMan.hasKey("lobby_server")) {
+		lobbyUrl = ConfMan.get("lobby_server");
+	}
+
+	// Parse the URL for checks:
+	Networking::CurlURL url;
+	if (url.parseURL(lobbyUrl)) {
+		Common::String scheme = url.getScheme();
+		if (!scheme.contains("http")) {
+			warning("LOBBY: Unsupported scheme in URL: \"%s\"", scheme.c_str());
+			writeStringArray(109, "Unsupported scheme in server address");
+			_vm->writeVar(108, -99);
+			return false;
+		}
+
+		int port = url.getPort();
+		switch (port) {
+		case -1:
+			warning("LOBBY: Unable to get port.");
+			writeStringArray(109, "Unable to get port in address");
+			_vm->writeVar(108, -99);
+			return false;
+		case 0:
+			// Add default port:
+			lobbyUrl += ":9130";
+			break;
+		}
+	} else
+		warning("LOBBY: Could not parse URL, attempting to connect as is");
+
 
-	debug(1, "LOBBY: Connecting to %s", url.c_str());
+	// // Maybe this check could be done better...
+	// int pos = lobbyUrl.findLastOf(":");
+	// if (pos)
+	// 	// If the URL missing a port at the end, add the default one in.
+	// 	lobbyUrl += ":9130";
 
-	if (_socket->connect(url)) {
-		debug(1, "LOBBY: Successfully connected to %s", url.c_str());
+	debug(1, "LOBBY: Connecting to %s", lobbyUrl.c_str());
+
+	if (_socket->connect(lobbyUrl)) {
+		debug(1, "LOBBY: Successfully connected to %s", lobbyUrl.c_str());
 		return true;
 	} else {
 		delete _socket;
@@ -172,7 +207,7 @@ bool Lobby::connect() {
 void Lobby::disconnect(bool lost) {
 	if (!_socket)
 		return;
-	
+
 	if (!lost) {
 		debug(1, "LOBBY: Disconnecting connection to server.");
 		Common::JSONObject disconnectObject;
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index 168f5bf261a..ce8629c3e89 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -24,6 +24,7 @@
 
 #include "common/formats/json.h"
 #include "backends/networking/curl/socket.h"
+#include "backends/networking/curl/url.h"
 
 // Commands for VAR_REMOTE_START_SCRIPT. (55 in football; 324 in baseball)
 #define OP_REMOTE_SYSTEM_ALERT			9911
@@ -53,7 +54,7 @@ public:
 	bool connect();
 	void disconnect(bool lost = false);
 	void login(const char *userName, const char *password);
-	
+
 	void getUserProfile(int userId);
 	void setIcon(int icon);
 protected:
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 031e6515959..4dfe7e71945 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -244,7 +244,8 @@ bool ScummEngine::hasFeature(EngineFeature f) const {
 		(f == kSupportsHelp) ||
 		(
 			f == kSupportsChangingOptionsDuringRuntime &&
-			Common::String(_game.guioptions).contains(GUIO_AUDIO_OVERRIDE)
+			(Common::String(_game.guioptions).contains(GUIO_AUDIO_OVERRIDE) ||
+			 Common::String(_game.guioptions).contains(GUIO_NETWORK))
 		) ||
 		(f == kSupportsQuitDialogOverride && (_useOriginalGUI || !ChainedGamesMan.empty()));
 }
@@ -593,7 +594,7 @@ GUI::OptionsContainerWidget *ScummMetaEngine::buildEngineOptionsWidget(GUI::GuiO
 #ifdef USE_ENET
 	else if (gameid == "football" || gameid == "baseball2001" || gameid == "football2002" ||
 		gameid == "moonbase")
-		return new Scumm::HENetworkGameOptionsWidget(boss, name, target);
+		return new Scumm::HENetworkGameOptionsWidget(boss, name, target, gameid);
 #endif
 
 	return MetaEngine::buildEngineOptionsWidget(boss, name, target);


Commit: 4b544d3b5fe7ac431fec74be6d36617a47a35ac0
    https://github.com/scummvm/scummvm/commit/4b544d3b5fe7ac431fec74be6d36617a47a35ac0
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Dispatch lobby opcodes to lobby code.

Changed paths:
    engines/scumm/he/logic/football.cpp
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h


diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index b4a261ece5d..872d5b1df71 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -47,41 +47,6 @@
 #define OP_NET_INIT_LAN_GAME		1515
 #define OP_NET_SET_PROVIDER_BY_NAME	1516
 
-// Boneyards (Lobby) opcodes.
-#define OP_NET_OPEN_WEB_URL						2121
-#define OP_NET_DOWNLOAD_PLAYBOOK				2122
-#define OP_NET_CONNECT 							2200
-#define OP_NET_DISCONNECT 						2201
-#define OP_NET_LOGIN							2202
-#define OP_NET_ENTER_AREA						2204
-#define OP_NET_GET_NUM_PLAYERS_IN_AREA			2205
-#define OP_NET_FETCH_PLAYERS_INFO_IN_AREA		2206
-#define OP_NET_GET_PLAYERS_INFO					2207
-#define OP_NET_START_HOSTING_GAME				2208
-#define OP_NET_CALL_PLAYER						2209
-#define OP_NET_RECEIVER_BUSY					2212
-#define OP_NET_COUNTER_CHALLENGE				2213
-#define OP_NET_GET_PROFILE						2214
-#define OP_NET_DECLINE_CHALLENGE				2215
-#define OP_NET_ACCEPT_CHALLENGE					2216
-#define OP_NET_STOP_CALLING						2217
-#define OP_NET_CHANGE_ICON						2218
-#define OP_NET_SET_PHONE_STATUS					2220
-#define OP_NET_ANSWER_PHONE						2221
-#define OP_NET_LEAVE_AREA						2222
-#define OP_NET_GAME_FINISHED					2223
-#define OP_NET_GAME_STARTED						2224
-#define OP_NET_UPDATE_PROFILE_ARRAY				2225
-#define OP_NET_LOCATE_PLAYER					2226
-#define OP_NET_GET_POPULATION					2227
-// Used in baseball to get news, poll and banner.
-#define OP_NET_DOWNLOAD_FILE					2238
-
-// MAIA (Updater) opcodes.
-#define OP_NET_UPDATE_INIT						3000
-#define OP_NET_CHECK_INTERNET_STATUS			3001
-#define OP_NET_FETCH_UPDATES					3002
-
 namespace Scumm {
 
 /**
@@ -101,9 +66,6 @@ protected:
 #ifdef USE_ENET
 	void netRemoteStartScript(int numArgs, int32 *args);
 	void netRemoteSendArray(int32 *args);
-#ifdef USE_LIBCURL
-	void netLogin(int32 *args);
-#endif
 #endif
 
 	int lineEquation3D(int32 *args);
@@ -134,6 +96,12 @@ int LogicHEfootball::startOfFrame() {
 
 
 int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+	if (op > 2120 && op < 3003 && op != OP_NET_CHECK_INTERNET_STATUS &&
+		_vm->_lobby) 
+		return _vm->_lobby->dispatch(op, numArgs, args);
+#endif
+
 	int res = 0;
 
 	switch (op) {
@@ -200,41 +168,6 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 	case OP_NET_WHO_AM_I:
 		res = _vm->_net->whoAmI();
 		break;
-
-#ifdef USE_LIBCURL
-	// Lobby opcodes goes here:
-	case OP_NET_OPEN_WEB_URL:
-		{
-			char url[50];
-			_vm->getStringFromArray(args[0], url, sizeof(url));
-
-			_vm->_lobby->openUrl(url);
-			break;
-		}
-	case OP_NET_DOWNLOAD_PLAYBOOK:
-		// TODO
-		break;
-	case OP_NET_CONNECT:
-		_vm->_lobby->connect();
-		break;
-
-	case OP_NET_DISCONNECT:
-		_vm->_lobby->disconnect();
-		break;
-
-	case OP_NET_LOGIN:
-		netLogin(args);
-		break;
-
-	case OP_NET_GET_PROFILE:
-		_vm->_lobby->getUserProfile(args[0]);
-		break;
-	
-	case OP_NET_CHANGE_ICON:
-		_vm->_lobby->setIcon(args[0]);
-		break;
-
-#endif // USE_LIBCURL
 #endif // USE_ENET
 
 	case OP_NET_CHECK_INTERNET_STATUS:
@@ -249,14 +182,6 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 #endif
 		break;
 
-	// TODO: Should we actually implement update checks here
-	// this at some point?
-	case OP_NET_UPDATE_INIT:
-		break;
-	case OP_NET_FETCH_UPDATES:
-		writeScummVar(111, 2);
-		break;
-
 	case 1493: case 1494: case 1495: case 1496:
 	case 1498: case 1499: case 1501:
 	case 1502: case 1503: case 1504: case 1506:
@@ -267,23 +192,6 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 		// 1555: set fake lag
 		break;
 
-	case 2203: case 2204:
-	case 2205: case 2206: case 2207: case 2208: case 2209:
-	case 2210: case 2211: case 2212: case 2213:
-	case 2215: case 2216: case 2217: case 2219:
-	case 2220: case 2221: case 2222: case 2223: case 2224:
-	case 2225: case 2226: case 2227: case 2228:
-		// Boneyards-related
-		break;
-
-	case 3003: case 3004:
-		// Internet-related
-		// 3000: check for updates
-		// 3001: check network status
-		// 3002: autoupdate
-		// 3003: close connection
-		break;
-
 	default:
 		LogicHE::dispatch(op, numArgs, args);
 		warning("Tell sev how to reproduce it (%d)", op);
@@ -475,19 +383,6 @@ void LogicHEfootball::netRemoteSendArray(int32 *args) {
 
 	_vm->_net->remoteSendArray(PN_SENDTYPE_INDIVIDUAL, targetUserId, priority, args[3]);
 }
-
-#ifdef USE_LIBCURL
-void LogicHEfootball::netLogin(int32 *args) {
-	char userName[16];
-	char password[16];
-
-	_vm->getStringFromArray(args[0], userName, sizeof(userName));
-	_vm->getStringFromArray(args[1], password, sizeof(password));
-
-	_vm->_lobby->login(userName, password);
-}
-
-#endif // USE_LIBCURL
 #endif // USE_ENET
 
 class LogicHEfootball2002 : public LogicHEfootball {
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 85a175b7adc..f9c60bcdb80 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -30,7 +30,7 @@ namespace Scumm {
 Lobby::Lobby(ScummEngine_v90he *vm) : _vm(vm) {
 	_gameName = _vm->_game.gameid;
 	if (_gameName == "baseball2001")
-		_gameName == "baseball";
+		_gameName = "baseball";
 	_socket = nullptr;
 
 	_userId = 0;
@@ -122,6 +122,67 @@ void Lobby::processLine(Common::String line) {
 	}
 }
 
+int32 Lobby::dispatch(int op, int numArgs, int32 *args) {
+	int res = 0;
+
+	switch(op) {
+	case OP_NET_OPEN_WEB_URL:
+		char url[128];
+		_vm->getStringFromArray(args[0], url, sizeof(url));
+
+		openUrl(url);
+		break;
+	case OP_NET_DOWNLOAD_PLAYBOOK:
+		// TODO
+		break;
+	case OP_NET_CONNECT:
+		connect();
+		break;
+	case OP_NET_DISCONNECT:
+		disconnect();
+		break;
+	case OP_NET_LOGIN:
+		char userName[16];
+		char password[16];
+
+		_vm->getStringFromArray(args[0], userName, sizeof(userName));
+		_vm->getStringFromArray(args[1], password, sizeof(password));
+
+		login(userName, password);
+		break;
+	case OP_NET_GET_PROFILE:
+		getUserProfile(args[0]);
+		break;
+	case OP_NET_CHANGE_ICON:
+		setIcon(args[0]);
+		break;
+	
+	case OP_NET_DOWNLOAD_FILE:
+		// TODO: News, Poll, and Banner downloads.
+		_vm->writeVar(135, 1);
+		break;
+	// TODO: Should we actually implement update checks here
+	// this at some point?
+	case OP_NET_UPDATE_INIT:
+		break;
+	case OP_NET_FETCH_UPDATES:
+		_vm->writeVar(111, 2);
+		break;
+
+	default:
+		Common::String str = Common::String::format("LOBBY: unknown op: (%d, %d, [", op, numArgs);
+		if (numArgs > 0)
+			str += Common::String::format("%d", args[0]);
+		for (int i = 1; i < numArgs; i++) {
+			str += Common::String::format(", %d", args[i]);
+		}
+		str += "])";
+		warning("%s", str.c_str());
+	}
+
+	return res;
+}
+
 void Lobby::handleHeartbeat() {
 	Common::JSONObject heartbeat;
 	heartbeat.setVal("cmd", new Common::JSONValue("heartbeat"));
@@ -132,7 +193,8 @@ void Lobby::openUrl(const char *url) {
 	debug(1, "LOBBY: openURL: %s", url);
 	Common::String urlString = Common::String(url);
 
-	if (urlString == "http://www.jrsn.com/c_corner/cc_regframe.asp") {
+	if (urlString == "http://www.jrsn.com/c_corner/cc_regframe.asp" ||
+	    urlString == "http://www.humongoussports.com/backyard/registration/register.asp") {
 		if (_vm->displayMessageYesNo("Online Play for this game is provided by Backyard Sports Online, which is a\nservice provided by the ScummVM project.\nWould you like to go to their registration page?")) {
 			if (!g_system->openUrl("https://backyardsports.online/register")) {
 				_vm->displayMessage(0, "Failed to open registration URL.  Please navigate to this page manually.\n\n\"https://backyardsports.online/register\"");
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index ce8629c3e89..ddee923f8a0 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -26,6 +26,41 @@
 #include "backends/networking/curl/socket.h"
 #include "backends/networking/curl/url.h"
 
+// Boneyards (Lobby) opcodes.
+#define OP_NET_OPEN_WEB_URL						2121
+#define OP_NET_DOWNLOAD_PLAYBOOK				2122
+#define OP_NET_CONNECT 							2200
+#define OP_NET_DISCONNECT 						2201
+#define OP_NET_LOGIN							2202
+#define OP_NET_ENTER_AREA						2204
+#define OP_NET_GET_NUM_PLAYERS_IN_AREA			2205
+#define OP_NET_FETCH_PLAYERS_INFO_IN_AREA		2206
+#define OP_NET_GET_PLAYERS_INFO					2207
+#define OP_NET_START_HOSTING_GAME				2208
+#define OP_NET_CALL_PLAYER						2209
+#define OP_NET_RECEIVER_BUSY					2212
+#define OP_NET_COUNTER_CHALLENGE				2213
+#define OP_NET_GET_PROFILE						2214
+#define OP_NET_DECLINE_CHALLENGE				2215
+#define OP_NET_ACCEPT_CHALLENGE					2216
+#define OP_NET_STOP_CALLING						2217
+#define OP_NET_CHANGE_ICON						2218
+#define OP_NET_SET_PHONE_STATUS					2220
+#define OP_NET_ANSWER_PHONE						2221
+#define OP_NET_LEAVE_AREA						2222
+#define OP_NET_GAME_FINISHED					2223
+#define OP_NET_GAME_STARTED						2224
+#define OP_NET_UPDATE_PROFILE_ARRAY				2225
+#define OP_NET_LOCATE_PLAYER					2226
+#define OP_NET_GET_POPULATION					2227
+// Used in baseball to get news, poll and banner.
+#define OP_NET_DOWNLOAD_FILE					2238
+
+// MAIA (Updater) opcodes.
+#define OP_NET_UPDATE_INIT						3000
+#define OP_NET_CHECK_INTERNET_STATUS			3001
+#define OP_NET_FETCH_UPDATES					3002
+
 // Commands for VAR_REMOTE_START_SCRIPT. (55 in football; 324 in baseball)
 #define OP_REMOTE_SYSTEM_ALERT			9911
 #define OP_REMOTE_START_CONNECTION		9919
@@ -49,6 +84,8 @@ public:
 	void doNetworkOnceAFrame();
 	void send(Common::JSONObject data);
 
+	int32 dispatch(int op, int numArgs, int32 *args);
+
 	void openUrl(const char *url);
 
 	bool connect();


Commit: ad96e502836a212952e5e2cc947069690c6ac1e0
    https://github.com/scummvm/scummvm/commit/ad96e502836a212952e5e2cc947069690c6ac1e0
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Migrate more lobby code.

Changed paths:
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h


diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index f9c60bcdb80..6a58cc4c0f7 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -118,6 +118,50 @@ void Lobby::processLine(Common::String line) {
 		} else if (command == "profile_info") {
 			Common::JSONArray profile = root["profile"]->asArray();
 			handleProfileInfo(profile);
+		} else if (command == "population_resp") {
+			int areaId = root["area"]->asIntegerNumber();
+			int population = root["population"]->asIntegerNumber();
+			handlePopulation((int)areaId, (int)population);
+		} else if (command == "locate_resp") {
+			int code = root["code"]->asIntegerNumber();
+			int areaId = root["areaId"]->asIntegerNumber();
+			Common::String area = root["area"]->asString();
+			handleLocateResp(code, areaId, area);
+		} else if (command == "players_list") {
+			Common::JSONArray playersList = root["players"]->asArray();
+			handlePlayersList(playersList);
+		} else if (command == "games_playing") {
+			int games = root["games"]->asIntegerNumber();
+			handleGamesPlaying(games);
+		} else if (command == "receive_challenge") {
+			int user = root["user"]->asIntegerNumber();
+			int stadium = root["stadium"]->asIntegerNumber();
+			Common::String name = root["name"]->asString();
+			handleReceiveChallenge(user, stadium, name);
+		} else if (command == "receiver_busy") {
+			handleReceiverBusy();
+		} else if (command == "considering_challenge") {
+			handleConsideringChallenge();
+		} else if (command == "counter_challenge") {
+			int stadium = root["stadium"]->asIntegerNumber();
+			handleCounterChallenge((int)stadium);
+		} else if (command == "decline_challenge") {
+			int notResponding = root["not_responding"]->asIntegerNumber();
+			handleDeclineChallenge((int)notResponding);
+		} else if (command == "accept_challenge") {
+			handleAcceptChallenge();
+		} else if (command == "game_session") {
+			// int session = root["session"]->asIntegerNumber();
+			// handleGameSession((int)session);
+		} else if (command == "game_relay") {
+			// int relay = root["relay"]->asIntegerNumber();
+			// handleGameRelay(relay);
+		} else if (command == "teams") {
+			// int error = root["error"]->asIntegerNumber();
+			// Common::String message = root["message"]->asString();
+			// Common::JSONArray userTeam = root["user"]->asArray();
+			// Common::JSONArray opponentTeam = root["opponent"]->asArray();
+			// handleTeams(userTeam, opponentTeam, (int)error, message);
 		}
 	}
 }
@@ -150,19 +194,74 @@ int32 Lobby::dispatch(int op, int numArgs, int32 *args) {
 
 		login(userName, password);
 		break;
+	case OP_NET_ENTER_AREA:
+		enterArea(args[0]);
+		break;
+	case OP_NET_GET_NUM_PLAYERS_IN_AREA:
+		// Refreshed with var115 = 2
+		res = _playersList.size();
+		break;
+	case OP_NET_FETCH_PLAYERS_INFO_IN_AREA:
+		getPlayersList(args[0], args[1]);
+		break;
+	case OP_NET_GET_PLAYERS_INFO:
+		getPlayerInfo(args[0]);
+		break;
+	// case OP_NET_START_HOSTING_GAME:
+		// startHostingGame(args[0]);
+		// break;
+	case OP_NET_CALL_PLAYER:
+		challengePlayer(args[0], args[1]);
+		break;
+	case OP_NET_RECEIVER_BUSY:
+		sendBusy(args[0]);
+		break;
+	case OP_NET_COUNTER_CHALLENGE:
+		counterChallenge(args[0]);
+		break;
 	case OP_NET_GET_PROFILE:
 		getUserProfile(args[0]);
 		break;
+	case OP_NET_DECLINE_CHALLENGE:
+		declineChallenge(args[0]);
+		break;
+	case OP_NET_ACCEPT_CHALLENGE:
+		acceptChallenge(args[0]);
+		break;
+	case OP_NET_STOP_CALLING:
+		challengeTimeout(args[0]);
+		break;
+	case OP_NET_LEAVE_AREA:
+		leaveArea();
+		break;
+	// case OP_NET_GAME_FINISHED:
+		// gameFinished();
+		// break;
+	// case OP_NET_GAME_STARTED:
+		// gameStarted(args[0], args[1], args[2]);
+		// break;
+	// case OP_NET_UPDATE_PROFILE_ARRAY:
+	// 	sendGameResults(args[0], args[1], args[2]);
+	// 	break;
+	case OP_NET_LOCATE_PLAYER:
+		locatePlayer(args[0]);
+		break;
+	case OP_NET_GET_POPULATION:
+		getPopulation(args[0], args[1]);
+		break;
 	case OP_NET_CHANGE_ICON:
 		setIcon(args[0]);
 		break;
-	
+	case OP_NET_SET_PHONE_STATUS:
+		setPhoneStatus(args[0]);
+		break;
+	case OP_NET_ANSWER_PHONE:
+		res = answerPhone(args[0]);
+		break;
 	case OP_NET_DOWNLOAD_FILE:
-		// TODO: News, Poll, and Banner downloads.
+		// TODO
 		_vm->writeVar(135, 1);
 		break;
-	// TODO: Should we actually implement update checks here
-	// this at some point?
 	case OP_NET_UPDATE_INIT:
 		break;
 	case OP_NET_FETCH_UPDATES:
@@ -367,6 +466,354 @@ void Lobby::setIcon(int icon) {
 	send(setIconRequest);
 }
 
+void Lobby::getPopulation(int areaId, int unknown) {
+	_areaIdForPopulation = areaId;
+
+	Common::JSONObject getPopulationRequest;
+	getPopulationRequest.setVal("cmd", new Common::JSONValue("get_population"));
+	getPopulationRequest.setVal("area", new Common::JSONValue((long long int)areaId));
+	send(getPopulationRequest);
+}
+
+void Lobby::handlePopulation(int areaId, int population) {
+		if (areaId == _areaIdForPopulation) {
+			_vm->writeVar(((_vm->_game.id == GID_FOOTBALL) ? 108 : 136), population + 1); // Game deducts the one
+			_areaIdForPopulation = 0;
+		}
+}
+
+void Lobby::locatePlayer(int usernameArray) {
+	if (!_socket) {
+		return;
+	}
+
+	char userName[16];
+	_vm->getStringFromArray(usernameArray, userName, sizeof(userName));
+
+	Common::JSONObject locatePlayerRequest;
+	locatePlayerRequest.setVal("cmd", new Common::JSONValue("locate_player"));
+	locatePlayerRequest.setVal("user", new Common::JSONValue((Common::String)userName));
+	send(locatePlayerRequest);
+}
+
+void Lobby::handleLocateResp(int code, int areaId, Common::String area) {
+	_vm->writeVar(108, 1);
+	_vm->writeVar(110, code);
+	if (code == 1) {
+		writeStringArray(109, area);
+		_vm->writeVar(111, areaId);
+	}
+}
+
+void Lobby::enterArea(int32 areaId) {
+	if (!areaId) {
+		warning("Backyard Online (enterArea): Missing area id!");
+		return;
+	}
+	if (!_socket) {
+		warning("BYOnline: Tried to enter area %d without connecting to server first!", (int)areaId);
+		return;
+	}
+
+	// HACK: After you log in on Baseball, you would join this "lobby area".
+	// My best guess is so you could potationally chat with other players, but
+	// unsure if that's actually implemented in-game, so let's just ignore
+	// that for now.
+	if (_vm->_game.id == GID_BASEBALL2001 && areaId == 33) {
+		return;
+	}
+
+	// Bugfix: If a single-player game is played with pitch locator on, it
+	// remains on for a subsequently played online game even though the areas'
+	// stated rules do not allow it. Here we fix this bug/exploit by writing to
+	// the variable that determines whether to use pitch locator
+	if (_vm->_game.id == GID_BASEBALL2001) {
+		_vm->writeVar(440, 0);
+	}
+
+	debug(1, "LOBBY: Entering area %d", int(areaId));
+
+	Common::JSONObject enterAreaRequest;
+	enterAreaRequest.setVal("cmd", new Common::JSONValue("enter_area"));
+	enterAreaRequest.setVal("area", new Common::JSONValue((long long int)areaId));
+	send(enterAreaRequest);
+
+	_inArea = true;
+}
+
+void Lobby::leaveArea() {
+	debug(1, "LOBBY: Leaving area.");
+	_playersList.clear();
+
+	if (_socket) {
+		Common::JSONObject leaveAreaRequest;
+		leaveAreaRequest.setVal("cmd", new Common::JSONValue("leave_area"));
+		send(leaveAreaRequest);
+
+		_inArea = false;
+	}
+}
+
+void Lobby::getPlayersList(int start, int end) {
+	if (!_socket) {
+		warning("BYOnline: Tried to fetch players list without connecting to server first!");
+		return;
+	}
+
+	Common::JSONObject playersListRequest;
+	playersListRequest.setVal("cmd", new Common::JSONValue("get_players"));
+	playersListRequest.setVal("start", new Common::JSONValue((long long int)start));
+	playersListRequest.setVal("end", new Common::JSONValue((long long int)end));
+	send(playersListRequest);
+
+}
+
+bool Lobby::_checkPlayersLists(Common::JSONArray other) {
+	// Check if the two players lists are different.
+	// This exists because (_playersList != other) doesn't work.
+	if (_playersList.size() != other.size())
+		return true;
+	for (uint i = 0; i < _playersList.size(); i++) {
+		Common::JSONArray playerInfo = _playersList[i]->asArray();
+		Common::JSONArray otherInfo = other[i]->asArray();
+
+		// Check if names are different.
+		if (playerInfo[0]->asString() != otherInfo[0]->asString())
+			return true;
+		for (uint o = 1; o < 7; o++) {
+			if (playerInfo[o]->asIntegerNumber() != otherInfo[o]->asIntegerNumber())
+				return true;
+		}
+	}
+	return false;
+}
+
+void Lobby::handlePlayersList(Common::JSONArray playersList) {
+	// If the list exactly the same as before, don't do anything.
+	if (_checkPlayersLists(playersList)) {
+		_playersList = playersList;
+		if (!_inGame) {
+			// Tell the game to redisplay the list.
+			_vm->writeVar(115, 2);
+		}
+	}
+}
+
+void Lobby::getPlayerInfo(int32 idx) {
+	if ((uint)idx - 1 > _playersList.size()) {
+		warning("BYOnline: _playersList is too small for index. (%d > %d)", (int)idx, (int)_playersList.size());
+		return;
+	}
+
+	Common::JSONArray playerInfo = _playersList[idx - 1]->asArray();
+	int newArray = 0;
+	_vm->defineArray(108, ScummEngine_v90he::kDwordArray, 0, 0, 0, 6, true, &newArray);
+	_vm->writeVar(108, newArray);
+
+	_vm->writeVar(109, 0);
+	// Write player name.
+	writeStringArray(109, playerInfo[0]->asString());
+	for (uint i = 1; i < 7; ++i) {
+		_vm->writeArray(108, 0, i - 1, (int32)playerInfo[i]->asIntegerNumber());
+	}
+}
+
+void Lobby::handleGamesPlaying(int games) {
+	if (!_inGame)
+		_gamesPlaying = games;
+}
+
+void Lobby::setPhoneStatus(int status) {
+	if (!_socket) {
+		return;
+	}
+
+	Common::JSONObject phoneStatus;
+	phoneStatus.setVal("cmd", new Common::JSONValue("set_phone_status"));
+	phoneStatus.setVal("status", new Common::JSONValue((long long int)status));
+	send(phoneStatus);
+}
+
+void Lobby::challengePlayer(int32 playerId, int32 stadium) {
+	if (!_socket) {
+		warning("BYOnline: Tried to challenge player without connecting to server first!");
+		return;
+	}
+
+	Common::JSONObject challengePlayerRequest;
+	challengePlayerRequest.setVal("cmd", new Common::JSONValue("challenge_player"));
+	challengePlayerRequest.setVal("user", new Common::JSONValue((long long int)playerId));
+	challengePlayerRequest.setVal("stadium", new Common::JSONValue((long long int)stadium));
+	send(challengePlayerRequest);
+}
+
+void Lobby::handleReceiveChallenge(int playerId, int stadium, Common::String name) {
+	int args[25];
+	memset(args, 0, sizeof(args));
+
+	writeStringArray(0, name);
+
+	// Setup the arguments
+	args[0] = OP_REMOTE_RECEIVE_CHALLENGE;
+	args[1] = playerId;
+	args[2] = stadium;
+	args[3] = _vm->VAR(0);
+
+	// Run the script
+	runRemoteStartScript(args);
+}
+
+void Lobby::challengeTimeout(int playerId) {
+	if (!_socket) {
+		warning("BYOnline: Tried to timeout challenge without connecting to server first!");
+		return;
+	}
+
+	Common::JSONObject challengeTimeoutRequuest;
+	challengeTimeoutRequuest.setVal("cmd", new Common::JSONValue("challenge_timeout"));
+	challengeTimeoutRequuest.setVal("user", new Common::JSONValue((long long int)playerId));
+	send(challengeTimeoutRequuest);
+}
+
+void Lobby::sendBusy(int playerId) {
+	if (!_socket) {
+		return;
+	}
+
+	Common::JSONObject busyRequest;
+	busyRequest.setVal("cmd", new Common::JSONValue("receiver_busy"));
+	busyRequest.setVal("user", new Common::JSONValue((long long int)playerId));
+	send(busyRequest);
+}
+
+void Lobby::handleReceiverBusy() {
+	int args[25];
+	memset(args, 0, sizeof(args));
+
+	// Setup the arguments
+	args[0] = OP_REMOTE_OPPONENT_BUSY;
+
+	// Run the script
+	runRemoteStartScript(args);
+}
+
+int32 Lobby::answerPhone(int playerId) {
+	if (!_socket) {
+		warning("BYOnline: Tried to answer phone without connecting to server first!");
+		return 0;
+	}
+
+	Common::JSONObject answerPhoneRequest;
+	answerPhoneRequest.setVal("cmd", new Common::JSONValue("considering_challenge"));
+	answerPhoneRequest.setVal("user", new Common::JSONValue((long long int)playerId));
+	send(answerPhoneRequest);
+
+	if (_playersList.size()) {
+		for (uint i = 0; i < _playersList.size(); i++) {
+			Common::JSONArray playerInfo = _playersList[i]->asArray();
+			if ((int)playerInfo[1]->asIntegerNumber() == playerId) {
+				// Write player name.
+				writeStringArray(109, playerInfo[0]->asString());
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+void Lobby::handleConsideringChallenge() {
+	int args[25];
+	memset(args, 0, sizeof(args));
+
+	// Setup the arguments
+	args[0] = OP_REMOTE_OPPONENT_ANSWERS;
+
+	// Run the script
+	runRemoteStartScript(args);
+}
+
+void Lobby::counterChallenge(int stadium) {
+	if (!_socket) {
+		warning("BYOnline: Tried to counter challenge without connecting to server first!");
+		return;
+	}
+
+	Common::JSONObject counterChallengeRequest;
+	counterChallengeRequest.setVal("cmd", new Common::JSONValue("counter_challenge"));
+	counterChallengeRequest.setVal("stadium", new Common::JSONValue((long long int)stadium));
+	send(counterChallengeRequest);
+}
+
+void Lobby::handleCounterChallenge(int stadium) {
+	int args[25];
+	memset(args, 0, sizeof(args));
+
+	// Setup the arguments
+	args[0] = OP_REMOTE_COUNTER_CHALLENGE;
+	args[1] = stadium;
+
+	// Run the script
+	runRemoteStartScript(args);
+}
+
+void Lobby::declineChallenge(int playerId) {
+	if (!_socket) {
+		warning("BYOnline: Tried to decline challenge without connecting to server first!");
+		return;
+	}
+
+	Common::JSONObject declineChallengeRequest;
+	declineChallengeRequest.setVal("cmd", new Common::JSONValue("decline_challenge"));
+	declineChallengeRequest.setVal("user", new Common::JSONValue((long long int)playerId));
+	send(declineChallengeRequest);
+}
+
+void Lobby::handleDeclineChallenge(int notResponding) {
+	int args[25];
+	memset(args, 0, sizeof(args));
+
+	// Setup the arguments
+	args[0] = OP_REMOTE_OPPONENT_DECLINES;
+	args[1] = notResponding;
+
+	// Run the script
+	runRemoteStartScript(args);
+}
+
+void Lobby::acceptChallenge(int playerId) {
+	if (!_socket) {
+		warning("BYOnline: Tried to accept challenge without connecting to server first!");
+		return;
+	}
+
+	_playerId = playerId;
+
+	Common::JSONObject acceptChallengeRequest;
+	acceptChallengeRequest.setVal("cmd", new Common::JSONValue("accept_challenge"));
+	acceptChallengeRequest.setVal("user", new Common::JSONValue((long long int)playerId));
+	send(acceptChallengeRequest);
+
+	// TODO: Competitive mods toggle
+	// if (_vm->_game.id == GID_BASEBALL2001 && _vm->readVar(559) == 19) {  // Only if in Prince Rupert
+	// 	// Request teams for this client and opponent
+	// 	Common::JSONObject getTeamsRequest;
+	// 	getTeamsRequest.setVal("cmd", new Common::JSONValue("get_teams"));
+	// 	getTeamsRequest.setVal("opponent_id", new Common::JSONValue((long long int)playerId));
+	// 	send(getTeamsRequest);
+	// }
+}
+
+void Lobby::handleAcceptChallenge() {
+	int args[25];
+	memset(args, 0, sizeof(args));
+
+	// Setup the arguments
+	args[0] = OP_REMOTE_OPPONENT_ACCEPTS;
+
+	// Run the script
+	runRemoteStartScript(args);
+}
+
 } // End of namespace Scumm
 
 
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index ddee923f8a0..b8cbdd22724 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -85,15 +85,6 @@ public:
 	void send(Common::JSONObject data);
 
 	int32 dispatch(int op, int numArgs, int32 *args);
-
-	void openUrl(const char *url);
-
-	bool connect();
-	void disconnect(bool lost = false);
-	void login(const char *userName, const char *password);
-
-	void getUserProfile(int userId);
-	void setIcon(int icon);
 protected:
 	ScummEngine_v90he *_vm;
 	Common::String _gameName;
@@ -101,6 +92,18 @@ protected:
 
 	Common::String _buffer;
 
+	Common::JSONArray _playersList;
+
+	int _userId;
+	int _playerId; // Opponent's user ID.
+
+	int _areaIdForPopulation; // The area id we're waiting for population for (to prevent getting population for one area while wanting another).
+
+	bool _inArea;
+	int _gamesPlaying;
+
+	bool _inGame;
+
 	void writeStringArray(int array, Common::String string);
 	void runRemoteStartScript(int *args);
 	void systemAlert(int type, Common::String message);
@@ -110,10 +113,68 @@ protected:
 
 	void handleHeartbeat();
 
+	void openUrl(const char *url);
+
+	bool connect();
+	void disconnect(bool lost = false);
+
+	void login(const char *userName, const char *password);
 	void handleLoginResp(int errorCode, int userId, Common::String response);
+
+	void getUserProfile(int userId);
 	void handleProfileInfo(Common::JSONArray profile);
 
-	int _userId;
+	void handleTeams(Common::JSONArray userTeam, Common::JSONArray opponentTeam, int error, Common::String message);
+
+	void setIcon(int icon);
+
+	void getPopulation(int areaId, int unknown);
+	void handlePopulation(int areaId, int population);
+
+	void locatePlayer(int userNameArray);
+	void handleLocateResp(int code, int areaId, Common::String area);
+
+	void enterArea(int32 areaId);
+	void leaveArea();
+
+	void getPlayersList(int start, int end);
+	bool _checkPlayersLists(Common::JSONArray other);
+	void handlePlayersList(Common::JSONArray playersList);
+	void getPlayerInfo(int32 idx);
+
+	void handleGamesPlaying(int games);
+
+	void setPhoneStatus(int status);
+
+	void sendBusy(int playerId);
+	void handleReceiverBusy();
+
+	void challengePlayer(int32 playerId, int32 stadium);
+	void handleReceiveChallenge(int playerId, int stadium, Common::String name);
+
+	void challengeTimeout(int playerId);
+
+	int32 answerPhone(int playerId);
+	void handleConsideringChallenge();
+
+	void counterChallenge(int stadium);
+	void handleCounterChallenge(int stadium);
+
+	void declineChallenge(int playerId);
+	void handleDeclineChallenge(int notResponding);
+
+	void acceptChallenge(int playerId);
+	void handleAcceptChallenge();
+
+	void startHostingGame(int playerId);
+	void handleHostGameResp(int resp);
+
+	void handleGameSession(int sessionId);
+	void handleGameRelay(int relayId);
+
+	void gameStarted(int hoster, int player, int playerNameArray);
+	void gameFinished();
+
 };
 
 } // End of namespace Scumm


Commit: 73cc6cc48864d2d82edda31f45605899c438d148
    https://github.com/scummvm/scummvm/commit/73cc6cc48864d2d82edda31f45605899c438d148
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Finish mitgrating lobby code.

Changed paths:
    engines/scumm/he/logic/baseball2001.cpp
    engines/scumm/he/logic/football.cpp
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h
    engines/scumm/he/script_v72he.cpp
    engines/scumm/script_v6.cpp


diff --git a/engines/scumm/he/logic/baseball2001.cpp b/engines/scumm/he/logic/baseball2001.cpp
index 14035ca5289..7c6152a7848 100644
--- a/engines/scumm/he/logic/baseball2001.cpp
+++ b/engines/scumm/he/logic/baseball2001.cpp
@@ -20,8 +20,32 @@
  */
 
 #include "scumm/he/intern_he.h"
+#ifdef USE_ENET
+#include "scumm/he/net/net_main.h"
+#ifdef USE_LIBCURL
+#include "scumm/he/net/net_lobby.h"
+#endif
+#include "scumm/he/net/net_defines.h"
+#endif
+
 #include "scumm/he/logic_he.h"
 
+// DirectPlay opcodes:
+#define OP_NET_REMOTE_START_SCRIPT	1492
+#define OP_NET_QUERY_PROVIDERS		1497
+#define OP_NET_CLOSE_PROVIDER		1500
+#define OP_NET_QUERY_SESSIONS		1501
+#define OP_NET_GET_SESSION_NAME		1502
+#define OP_NET_JOIN_SESSION			1504
+#define OP_NET_END_SESSION			1505
+#define OP_NET_ADD_USER				1506
+#define OP_NET_WHO_SENT_THIS		1508
+#define OP_NET_REMOTE_SEND_ARRAY	1509
+#define OP_NET_INIT					1513
+#define OP_NET_WHO_AM_I				1510
+#define OP_NET_INIT_LAN_GAME		1515
+#define OP_NET_SET_PROVIDER_BY_NAME	1516
+
 namespace Scumm {
 
 /**
@@ -33,19 +57,99 @@ public:
 	LogicHEbaseball2001(ScummEngine_v90he *vm) : LogicHE(vm) {}
 
 	int versionID() override;
+	int startOfFrame() override;
 	int32 dispatch(int op, int numArgs, int32 *args) override;
+
+protected:
+#ifdef USE_ENET
+	void netRemoteStartScript(int numArgs, int32 *args);
+	void netRemoteSendArray(int32 *args);
+#endif
 };
 
 int LogicHEbaseball2001::versionID() {
 	return 1;
 }
 
+int LogicHEbaseball2001::startOfFrame() {
+#ifdef USE_ENET
+#ifdef USE_LIBCURL
+	_vm->_lobby->doNetworkOnceAFrame();
+#endif
+	_vm->_net->doNetworkOnceAFrame(0);
+#endif
+	return 0;
+}
+
 int32 LogicHEbaseball2001::dispatch(int op, int numArgs, int32 *args) {
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+	if (op > 2120 && op < 3003 && op != OP_NET_CHECK_INTERNET_STATUS) 
+		return _vm->_lobby->dispatch(op, numArgs, args);
+#endif
+
 	int res = 0;
 
 	switch (op) {
-	case 3001:
-		// Check network status
+case OP_NET_INIT:
+		// Initialize network system, this gets called at boot up and
+		// sets VAR_NETWORK_AVAILABLE (100).  We just return a 1 if
+		// ENet is compiled.
+#ifdef USE_ENET
+		res = 1;
+#endif
+		break;
+
+#ifdef USE_ENET
+	case OP_NET_REMOTE_START_SCRIPT:
+		netRemoteStartScript(numArgs, args);
+		break;
+
+	case OP_NET_QUERY_SESSIONS:
+		res = _vm->_net->querySessions();
+		break;
+
+	case OP_NET_JOIN_SESSION:
+		if (_vm->_lobby->_sessionId) {
+			res = _vm->_net->joinSessionById(_vm->_lobby->_sessionId);
+			if (res) {
+				_vm->_net->stopQuerySessions();
+			}
+		}
+		break;
+
+	case OP_NET_END_SESSION:
+		res = _vm->_net->endSession();
+		break;
+
+	case OP_NET_ADD_USER:
+		char userName[MAX_PLAYER_NAME];
+		_vm->getStringFromArray(args[0], userName, sizeof(userName));
+		res = _vm->_net->addUser(userName, userName);
+		break;
+
+	case OP_NET_WHO_SENT_THIS:
+		res = _vm->_net->whoSentThis();
+		break;
+
+	case OP_NET_REMOTE_SEND_ARRAY:
+		netRemoteSendArray(args);
+		break;
+
+	case OP_NET_WHO_AM_I:
+		res = _vm->_net->whoAmI();
+		break;
+#endif // USE_ENET
+
+	case OP_NET_CHECK_INTERNET_STATUS:
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+		// We can only use the lobby system if both
+		// libcurl (for lobby communication) and
+		// ENet (for gameplay communication) is enabled.
+
+		// TODO: Actually check if we're connected to the
+		// Internet.
+		res = 1;
+#endif
 		break;
 
 	default:
@@ -55,6 +159,32 @@ int32 LogicHEbaseball2001::dispatch(int op, int numArgs, int32 *args) {
 	return res;
 }
 
+#ifdef USE_ENET
+void LogicHEbaseball2001::netRemoteStartScript(int numArgs, int32 *args) {
+	int priority = PN_PRIORITY_HIGH;
+
+	int targetUserId;
+	if (_vm->_net->_isHost)
+		targetUserId = 2;
+	else
+		targetUserId = 1;
+
+	_vm->_net->remoteStartScript(PN_SENDTYPE_INDIVIDUAL, targetUserId, priority, numArgs - 3, &args[3]);
+}
+
+void LogicHEbaseball2001::netRemoteSendArray(int32 *args) {
+	int priority = PN_PRIORITY_HIGH;
+
+	int targetUserId;
+	if (_vm->_net->_isHost)
+		targetUserId = 2;
+	else
+		targetUserId = 1;
+
+	_vm->_net->remoteSendArray(PN_SENDTYPE_INDIVIDUAL, targetUserId, priority, args[3]);
+}
+#endif // USE_ENET
+
 LogicHE *makeLogicHEbaseball2001(ScummEngine_v90he *vm) {
 	return new LogicHEbaseball2001(vm);
 }
diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index 872d5b1df71..7dd0d4f6651 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -39,7 +39,9 @@
 #define OP_NET_CLOSE_PROVIDER		1500
 #define OP_NET_QUERY_SESSIONS		1501
 #define OP_NET_GET_SESSION_NAME		1502
+#define OP_NET_JOIN_SESSION			1504
 #define OP_NET_END_SESSION			1505
+#define OP_NET_ADD_USER				1506
 #define OP_NET_WHO_SENT_THIS		1508
 #define OP_NET_REMOTE_SEND_ARRAY	1509
 #define OP_NET_INIT					1513
@@ -98,7 +100,7 @@ int LogicHEfootball::startOfFrame() {
 int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 #if defined(USE_ENET) && defined(USE_LIBCURL)
 	if (op > 2120 && op < 3003 && op != OP_NET_CHECK_INTERNET_STATUS &&
-		_vm->_lobby) 
+		_vm->_lobby)
 		return _vm->_lobby->dispatch(op, numArgs, args);
 #endif
 
@@ -153,10 +155,29 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 		netRemoteStartScript(numArgs, args);
 		break;
 
+	case OP_NET_QUERY_SESSIONS:
+		res = _vm->_net->querySessions();
+		break;
+
+	case OP_NET_JOIN_SESSION:
+		if (_vm->_lobby->_sessionId) {
+			res = _vm->_net->joinSessionById(_vm->_lobby->_sessionId);
+			if (res) {
+				_vm->_net->stopQuerySessions();
+			}
+		}
+		break;
+
 	case OP_NET_END_SESSION:
 		res = _vm->_net->endSession();
 		break;
 
+	case OP_NET_ADD_USER:
+		char userName[MAX_PLAYER_NAME];
+		_vm->getStringFromArray(args[0], userName, sizeof(userName));
+		res = _vm->_net->addUser(userName, userName);
+		break;
+
 	case OP_NET_WHO_SENT_THIS:
 		res = _vm->_net->whoSentThis();
 		break;
@@ -183,8 +204,8 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 		break;
 
 	case 1493: case 1494: case 1495: case 1496:
-	case 1498: case 1499: case 1501:
-	case 1502: case 1503: case 1504: case 1506:
+	case 1498: case 1499:
+	case 1502: case 1503:
 	case 1507: case 1511:
 	case 1512: case 1514: case 1555:
 		// DirectPlay-related
@@ -418,7 +439,7 @@ private:
 	int32 _maxX;
 	int32 _minX;
 
-	int _requestedSessionId = -2;
+	int _requestedSessionIndex = -2;
 };
 
 int32 LogicHEfootball2002::dispatch(int op, int numArgs, int32 *args) {
@@ -463,8 +484,8 @@ int32 LogicHEfootball2002::dispatch(int op, int numArgs, int32 *args) {
 #ifdef USE_ENET
 		res = _vm->_net->setProviderByName(args[0], args[1]);
 		if (res)
-			_requestedSessionId = _vm->networkSessionDialog();
-		if (_requestedSessionId == -2) {
+			_requestedSessionIndex = _vm->networkSessionDialog();
+		if (_requestedSessionIndex == -2) {
 			// Cancelled out the join dialog.
 			_vm->_net->closeProvider();
 			res = 0;
@@ -478,7 +499,7 @@ int32 LogicHEfootball2002::dispatch(int op, int numArgs, int32 *args) {
 		break;
 
 	case OP_NET_QUERY_SESSIONS:
-		if (_requestedSessionId > -1)
+		if (_requestedSessionIndex > -1)
 			// Emulate that we've found a session.
 			res = 1;
 		break;
@@ -632,7 +653,7 @@ int LogicHEfootball2002::largestFreeBlock() {
 #ifdef USE_ENET
 int LogicHEfootball2002::netGetSessionName(int index) {
 	char name[MAX_SESSION_NAME];
-	_vm->_net->getSessionName(_requestedSessionId, name, sizeof(name));
+	_vm->_net->getSessionName(_requestedSessionIndex, name, sizeof(name));
 	return _vm->setupStringArrayFromString(name);
 }
 
@@ -655,7 +676,7 @@ int LogicHEfootball2002::netInitLanGame(int32 *args) {
 		// And host our new game.
 		res = _vm->_net->hostGame(sessionName, userName);
 	} else {
-		res = _vm->_net->joinSession(_requestedSessionId);
+		res = _vm->_net->joinSession(_requestedSessionIndex);
 		if (res)
 			_vm->_net->addUser(userName, userName);
 		_vm->_net->stopQuerySessions();
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 6a58cc4c0f7..57c5a9435a2 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -34,11 +34,14 @@ Lobby::Lobby(ScummEngine_v90he *vm) : _vm(vm) {
 	_socket = nullptr;
 
 	_userId = 0;
+	_userName = "";
+
+	_sessionId = 0;
 }
 
 Lobby::~Lobby() {
 	if (_socket)
-		delete _socket;
+		disconnect();
 }
 
 void Lobby::writeStringArray(int array, Common::String string) {
@@ -113,8 +116,9 @@ void Lobby::processLine(Common::String line) {
 		} else if (command == "login_resp") {
 			int errorCode = root["error_code"]->asIntegerNumber();
 			int userId = root["id"]->asIntegerNumber();
+			Common::String sessionServer = root["sessionServer"]->asString();
 			Common::String response = root["response"]->asString();
-			handleLoginResp(errorCode, userId, response);
+			handleLoginResp(errorCode, userId, sessionServer, response);
 		} else if (command == "profile_info") {
 			Common::JSONArray profile = root["profile"]->asArray();
 			handleProfileInfo(profile);
@@ -151,8 +155,8 @@ void Lobby::processLine(Common::String line) {
 		} else if (command == "accept_challenge") {
 			handleAcceptChallenge();
 		} else if (command == "game_session") {
-			// int session = root["session"]->asIntegerNumber();
-			// handleGameSession((int)session);
+			int session = root["session"]->asIntegerNumber();
+			handleGameSession((int)session);
 		} else if (command == "game_relay") {
 			// int relay = root["relay"]->asIntegerNumber();
 			// handleGameRelay(relay);
@@ -186,8 +190,8 @@ int32 Lobby::dispatch(int op, int numArgs, int32 *args) {
 		disconnect();
 		break;
 	case OP_NET_LOGIN:
-		char userName[16];
-		char password[16];
+		char userName[MAX_USER_NAME];
+		char password[MAX_USER_NAME];
 
 		_vm->getStringFromArray(args[0], userName, sizeof(userName));
 		_vm->getStringFromArray(args[1], password, sizeof(password));
@@ -207,9 +211,9 @@ int32 Lobby::dispatch(int op, int numArgs, int32 *args) {
 	case OP_NET_GET_PLAYERS_INFO:
 		getPlayerInfo(args[0]);
 		break;
-	// case OP_NET_START_HOSTING_GAME:
-		// startHostingGame(args[0]);
-		// break;
+	case OP_NET_START_HOSTING_GAME:
+		startHostingGame(args[0]);
+		break;
 	case OP_NET_CALL_PLAYER:
 		challengePlayer(args[0], args[1]);
 		break;
@@ -234,15 +238,15 @@ int32 Lobby::dispatch(int op, int numArgs, int32 *args) {
 	case OP_NET_LEAVE_AREA:
 		leaveArea();
 		break;
-	// case OP_NET_GAME_FINISHED:
-		// gameFinished();
-		// break;
-	// case OP_NET_GAME_STARTED:
-		// gameStarted(args[0], args[1], args[2]);
-		// break;
-	// case OP_NET_UPDATE_PROFILE_ARRAY:
-	// 	sendGameResults(args[0], args[1], args[2]);
-	// 	break;
+	case OP_NET_GAME_FINISHED:
+		gameFinished();
+		break;
+	case OP_NET_GAME_STARTED:
+		gameStarted(args[0], args[1], args[2]);
+		break;
+	case OP_NET_UPDATE_PROFILE_ARRAY:
+		sendGameResults(args[0], args[1], args[2]);
+		break;
 	case OP_NET_LOCATE_PLAYER:
 		locatePlayer(args[0]);
 		break;
@@ -380,6 +384,9 @@ void Lobby::disconnect(bool lost) {
 
 	delete _socket;
 	_socket = nullptr;
+
+	_userId = 0;
+	_userName = "";
 }
 
 void Lobby::runRemoteStartScript(int *args) {
@@ -409,9 +416,10 @@ void Lobby::systemAlert(int type, Common::String message) {
 }
 
 void Lobby::login(const char *userName, const char *password) {
+	_userName = userName;
 	Common::JSONObject loginRequestParameters;
 	loginRequestParameters.setVal("cmd", new Common::JSONValue("login"));
-	loginRequestParameters.setVal("user", new Common::JSONValue((Common::String)userName));
+	loginRequestParameters.setVal("user", new Common::JSONValue(_userName));
 	loginRequestParameters.setVal("pass", new Common::JSONValue((Common::String)password));
 	loginRequestParameters.setVal("game", new Common::JSONValue((Common::String)_gameName));
 	loginRequestParameters.setVal("version", new Common::JSONValue(gScummVMVersionLite));
@@ -419,13 +427,14 @@ void Lobby::login(const char *userName, const char *password) {
 	send(loginRequestParameters);
 }
 
-void Lobby::handleLoginResp(int errorCode, int userId, Common::String response) {
+void Lobby::handleLoginResp(int errorCode, int userId, Common::String sessionServer, Common::String response) {
 	if (errorCode > 0) {
 		writeStringArray(109, response);
 		_vm->writeVar(108, -99);
 		return;
 	}
 	_userId = userId;
+	_vm->_net->setSessionServer(sessionServer);
 	_vm->writeVar(108, 99);
 }
 
@@ -466,6 +475,29 @@ void Lobby::setIcon(int icon) {
 	send(setIconRequest);
 }
 
+void Lobby::sendGameResults(int userId, int arrayIndex, int unknown) {
+	if (!_socket) {
+		return;
+	}
+
+	Common::JSONObject setProfileRequest;
+	setProfileRequest.setVal("cmd", new Common::JSONValue("game_results"));
+	setProfileRequest.setVal("user", new Common::JSONValue((long long int)userId));
+
+	ScummEngine_v90he::ArrayHeader *ah = (ScummEngine_v90he::ArrayHeader *)_vm->getResourceAddress(rtString, arrayIndex & ~0x33539000);
+	int32 size = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
+		(FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
+
+	Common::JSONArray arrayData;
+	for (int i = 0; i < size; i++) {
+		// Assuming they're dword type
+		int32 data = (int32)READ_LE_UINT32(ah->data + i * 4);
+		arrayData.push_back(new Common::JSONValue((long long int)data));
+	}
+	setProfileRequest.setVal("fields", new Common::JSONValue(arrayData));
+	send(setProfileRequest);
+}
+
 void Lobby::getPopulation(int areaId, int unknown) {
 	_areaIdForPopulation = areaId;
 
@@ -487,7 +519,7 @@ void Lobby::locatePlayer(int usernameArray) {
 		return;
 	}
 
-	char userName[16];
+	char userName[MAX_USER_NAME];
 	_vm->getStringFromArray(usernameArray, userName, sizeof(userName));
 
 	Common::JSONObject locatePlayerRequest;
@@ -511,7 +543,7 @@ void Lobby::enterArea(int32 areaId) {
 		return;
 	}
 	if (!_socket) {
-		warning("BYOnline: Tried to enter area %d without connecting to server first!", (int)areaId);
+		warning("LOBBY: Tried to enter area %d without connecting to server first!", (int)areaId);
 		return;
 	}
 
@@ -556,7 +588,7 @@ void Lobby::leaveArea() {
 
 void Lobby::getPlayersList(int start, int end) {
 	if (!_socket) {
-		warning("BYOnline: Tried to fetch players list without connecting to server first!");
+		warning("LOBBY: Tried to fetch players list without connecting to server first!");
 		return;
 	}
 
@@ -601,7 +633,7 @@ void Lobby::handlePlayersList(Common::JSONArray playersList) {
 
 void Lobby::getPlayerInfo(int32 idx) {
 	if ((uint)idx - 1 > _playersList.size()) {
-		warning("BYOnline: _playersList is too small for index. (%d > %d)", (int)idx, (int)_playersList.size());
+		warning("LOBBY: _playersList is too small for index. (%d > %d)", (int)idx, (int)_playersList.size());
 		return;
 	}
 
@@ -636,7 +668,7 @@ void Lobby::setPhoneStatus(int status) {
 
 void Lobby::challengePlayer(int32 playerId, int32 stadium) {
 	if (!_socket) {
-		warning("BYOnline: Tried to challenge player without connecting to server first!");
+		warning("LOBBY: Tried to challenge player without connecting to server first!");
 		return;
 	}
 
@@ -665,7 +697,7 @@ void Lobby::handleReceiveChallenge(int playerId, int stadium, Common::String nam
 
 void Lobby::challengeTimeout(int playerId) {
 	if (!_socket) {
-		warning("BYOnline: Tried to timeout challenge without connecting to server first!");
+		warning("LOBBY: Tried to timeout challenge without connecting to server first!");
 		return;
 	}
 
@@ -699,7 +731,7 @@ void Lobby::handleReceiverBusy() {
 
 int32 Lobby::answerPhone(int playerId) {
 	if (!_socket) {
-		warning("BYOnline: Tried to answer phone without connecting to server first!");
+		warning("LOBBY: Tried to answer phone without connecting to server first!");
 		return 0;
 	}
 
@@ -734,7 +766,7 @@ void Lobby::handleConsideringChallenge() {
 
 void Lobby::counterChallenge(int stadium) {
 	if (!_socket) {
-		warning("BYOnline: Tried to counter challenge without connecting to server first!");
+		warning("LOBBY: Tried to counter challenge without connecting to server first!");
 		return;
 	}
 
@@ -758,7 +790,7 @@ void Lobby::handleCounterChallenge(int stadium) {
 
 void Lobby::declineChallenge(int playerId) {
 	if (!_socket) {
-		warning("BYOnline: Tried to decline challenge without connecting to server first!");
+		warning("LOBBY: Tried to decline challenge without connecting to server first!");
 		return;
 	}
 
@@ -782,7 +814,7 @@ void Lobby::handleDeclineChallenge(int notResponding) {
 
 void Lobby::acceptChallenge(int playerId) {
 	if (!_socket) {
-		warning("BYOnline: Tried to accept challenge without connecting to server first!");
+		warning("LOBBY: Tried to accept challenge without connecting to server first!");
 		return;
 	}
 
@@ -814,6 +846,102 @@ void Lobby::handleAcceptChallenge() {
 	runRemoteStartScript(args);
 }
 
+void Lobby::startHostingGame(int playerId) {
+	if (!_socket)
+		return;
+	
+	_playerId = playerId;
+	_vm->writeVar(111, 0);
+
+	// Create ENet instance.
+	if (!_vm->_net->setProviderByName(0, 0)) {
+		_vm->writeVar(111, 1);
+		return;
+	}
+
+	// TODO: Actual session name.
+	if (_vm->_net->hostGame(const_cast<char *>(_userName.c_str()), const_cast<char *>(_userName.c_str()))) {
+		// Wait till the session server assigns us a session id.
+		uint tickCount = 0;
+		while(_vm->_net->_sessionId == -1) {
+			_vm->_net->doNetworkOnceAFrame(12);
+			tickCount += 5;
+			g_system->delayMillis(5);
+			if (tickCount >= 5000)
+				break;
+		}
+		int sessionId = _vm->_net->_sessionId;
+		if (sessionId > 0) {
+			_inGame = true;
+			// Send our session over to our opponent.
+			Common::JSONObject sendSessionRequest;
+			sendSessionRequest.setVal("cmd", new Common::JSONValue("send_session"));
+			sendSessionRequest.setVal("user", new Common::JSONValue((long long int)_playerId));
+			sendSessionRequest.setVal("session", new Common::JSONValue((long long int)sessionId));
+			send(sendSessionRequest);
+
+			// Tell the game that we're hosting.
+			_vm->writeVar(111, 99);
+		} else
+			_vm->writeVar(111, 1);
+	} else
+		_vm->writeVar(111, 1);
+}
+
+void Lobby::handleGameSession(int sessionId) {
+	_sessionId = sessionId;
+	_inGame = true;
+
+	if (_vm->_net->setProviderByName(0, 0)) {
+		// Tell the game to start connecting to our host.
+		int args[25];
+		memset(args, 0, sizeof(args));
+
+		// Setup the arguments
+		args[0] = OP_REMOTE_START_CONNECTION;
+
+		// Run the script
+		runRemoteStartScript(args);
+	}
+}
+
+void Lobby::gameStarted(int hoster, int player, int playerNameArray) {
+	// if (_vm->_game.id == GID_BASEBALL2001 && _vm->readVar(399) == 1 && _vm->readVar(686) == 1) {  // Only if we're online and in Prince Rupert
+		// 	// Request teams for this client and opponent
+		// Common::JSONObject getTeamsRequest;
+		// getTeamsRequest.setVal("cmd", new Common::JSONValue("get_teams"));
+		// getTeamsRequest.setVal("opponent_id", new Common::JSONValue((long long int)player));
+		// send(getTeamsRequest);
+	// }
+
+	char playerName[16];
+	_vm->getStringFromArray(playerNameArray, playerName, sizeof(playerName));
+
+	if (hoster != _userId) {
+		warning("LOBBY: Got game started op but the hoster wasn't us!");
+		return;
+	}
+
+	// Don't accept anymore sessions.
+	_vm->_net->disableSessionJoining();
+
+	Common::JSONObject gameStartedRequest;
+	gameStartedRequest.setVal("cmd", new Common::JSONValue("game_started"));
+	gameStartedRequest.setVal("user", new Common::JSONValue((long long int)player));
+
+	send(gameStartedRequest);
+}
+
+void Lobby::gameFinished() {
+	_inGame = false;
+	_vm->_net->closeProvider();
+
+	Common::JSONObject gameFinishedRequest;
+	gameFinishedRequest.setVal("cmd", new Common::JSONValue("game_finished"));
+
+	send(gameFinishedRequest);
+}
+
 } // End of namespace Scumm
 
 
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index b8cbdd22724..4a747012087 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -22,9 +22,13 @@
 #ifndef SCUMM_HE_NET_LOBBY_H
 #define SCUMM_HE_NET_LOBBY_H
 
-#include "common/formats/json.h"
 #include "backends/networking/curl/socket.h"
 #include "backends/networking/curl/url.h"
+#include "common/formats/json.h"
+
+#include "scumm/he/net/net_main.h"
+
+#define MAX_USER_NAME 16
 
 // Boneyards (Lobby) opcodes.
 #define OP_NET_OPEN_WEB_URL						2121
@@ -85,6 +89,8 @@ public:
 	void send(Common::JSONObject data);
 
 	int32 dispatch(int op, int numArgs, int32 *args);
+
+	int _sessionId;
 protected:
 	ScummEngine_v90he *_vm;
 	Common::String _gameName;
@@ -95,6 +101,7 @@ protected:
 	Common::JSONArray _playersList;
 
 	int _userId;
+	Common::String _userName;
 	int _playerId; // Opponent's user ID.
 
 	int _areaIdForPopulation; // The area id we're waiting for population for (to prevent getting population for one area while wanting another).
@@ -119,7 +126,7 @@ protected:
 	void disconnect(bool lost = false);
 
 	void login(const char *userName, const char *password);
-	void handleLoginResp(int errorCode, int userId, Common::String response);
+	void handleLoginResp(int errorCode, int userId, Common::String sessionServer, Common::String response);
 
 	void getUserProfile(int userId);
 	void handleProfileInfo(Common::JSONArray profile);
@@ -127,6 +134,7 @@ protected:
 	void handleTeams(Common::JSONArray userTeam, Common::JSONArray opponentTeam, int error, Common::String message);
 
 	void setIcon(int icon);
+	void sendGameResults(int userId, int arrayIndex, int unknown);
 
 	void getPopulation(int areaId, int unknown);
 	void handlePopulation(int areaId, int population);
diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 06773aced61..912401eadd3 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -101,6 +101,18 @@ Common::String Net::getStringFromAddress(Address address) {
 	return Common::String::format("%s:%d", address.host.c_str(), address.port);
 }
 
+void Net::setSessionServer(Common::String sessionServer) {
+	debug(1, "Net::setSessionServer(\"%s\")", sessionServer.c_str());
+
+	ConfMan.setBool("enable_session_server", true);
+	ConfMan.setBool("enable_lan_broadcast", false);
+
+	_sessionServerAddress = getAddressFromString(sessionServer);
+	// Set port to default if not defined.
+	if (!_sessionServerAddress.port)
+		_sessionServerAddress.port = 9120;
+}
+
 int Net::hostGame(char *sessionName, char *userName) {
 	if (createSession(sessionName)) {
 		if (addUser(userName, userName)) {
@@ -184,7 +196,6 @@ bool Net::connectToSession(Common::String address, int port) {
 	if (!_sessionHost)
 		return false;
 
-	_isHost = false;
 	return true;
 }
 
@@ -306,23 +317,28 @@ int Net::getTotalPlayers() {
 	return _numUsers + _numBots;
 }
 
-int Net::joinSession(int sessionIndex) {
-	debug(1, "Net::joinSession(%d)", sessionIndex); // PN_JoinSession
+int Net::joinSessionById(int sessionId) {
+	debug(1, "Net::joinSessionById(%d)", sessionId);
 	if (_sessions.empty()) {
 		warning("Net::joinSession(): no sessions");
 		return 0;
 	}
 
-	if (sessionIndex >= (int)_sessions.size()) {
-		warning("Net::joinSession(): session number too big: %d >= %d", sessionIndex, _sessions.size());
-		return 0;
+	for (Common::Array<Session>::iterator i = _sessions.begin(); i != _sessions.end(); i++) {
+		if (i->id == sessionId) {
+			return doJoinSession(*i);
+		}
 	}
+	warning("Net::joinSessionById(): session %d not found!", sessionId);
+	return 0;
 
-	Session session = _sessions[sessionIndex];
+}
+
+int Net::doJoinSession(Session session) {
 	if (!session.local && _sessionServerHost) {
 		Common::String joinSession = Common::String::format(
-			"{\"cmd\":\"join_session\",\"game\":\"%s\",\"version\":\"%s\",\"session\":%d}",
-			_gameName.c_str(), _gameVersion.c_str(), sessionIndex);
+			"{\"cmd\":\"join_session\",\"game\":\"%s\",\"version\":\"%s\",\"id\":%d}",
+			_gameName.c_str(), _gameVersion.c_str(), session.id);
 		_sessionServerHost->send(joinSession.c_str(), 0);
 
 		// Give the host time to hole punch us.
@@ -371,6 +387,22 @@ int Net::joinSession(int sessionIndex) {
 	return true;
 }
 
+int Net::joinSession(int sessionIndex) {
+	debug(1, "Net::joinSession(%d)", sessionIndex); // PN_JoinSession
+	if (_sessions.empty()) {
+		warning("Net::joinSession(): no sessions");
+		return 0;
+	}
+
+	if (sessionIndex >= (int)_sessions.size()) {
+		warning("Net::joinSession(): session number too big: %d >= %d", sessionIndex, _sessions.size());
+		return 0;
+	}
+
+	Session session = _sessions[sessionIndex];
+	return doJoinSession(session);
+}
+
 int Net::endSession() {
 	debug(1, "Net::endSession()"); // PN_EndSession
 
@@ -421,6 +453,8 @@ int Net::endSession() {
 	_myUserId = -1;
 	_fromUserId = -1;
 
+	_isHost = false;
+
 	_hostDataQueue.clear();
 	_peerIndexQueue.clear();
 
@@ -545,7 +579,7 @@ int32 Net::updateQuerySessions() {
 	debug(1, "Net::updateQuerySessions(): begin"); // UpdateQuerySessions
 
 	if (_sessionServerHost) {
-		// Get internet-based sessions from the sessin server.
+		// Get internet-based sessions from the session server.
 		Common::String getSessions = Common::String::format(
 			"{\"cmd\":\"get_sessions\",\"game\":\"%s\",\"version\":\"%s\"}",
 			_gameName.c_str(), _gameVersion.c_str());
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index 0647782fba5..1fc49dde743 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -35,6 +35,7 @@ public:
 	Net(ScummEngine_v90he *vm);
 	~Net();
 
+private:
 	struct Address {
 		Common::String host;
 		int port;
@@ -43,7 +44,16 @@ public:
 		};
 	};
 
-private:
+	struct Session {
+		bool local = false;
+		int id = -1;
+		Common::String host;
+		int port;
+		Common::String name;
+		int players;
+		uint32 timestamp;
+	};
+
 	Address getAddressFromString(Common::String address);
 	Common::String getStringFromAddress(Address address);
 public:
@@ -55,7 +65,9 @@ public:
 	int whoAmI();
 	int createSession(char *name);
 	int joinSession(int sessionIndex);
+	int joinSessionById(int sessionId);
 	int endSession();
+	void setSessionServer(Common::String sessionServer);
 	void disableSessionJoining();
 	void enableSessionJoining();
 	void setBotsCount(int botsCount);
@@ -83,6 +95,7 @@ public:
 
 private:
 	bool connectToSession(Common::String address, int port);
+	int doJoinSession(Session session);
 	bool serviceBroadcast();
 	void handleBroadcastData(Common::String data, Common::String host, int port);
 	void serviceSessionServer();
@@ -137,16 +150,6 @@ public:
 	Common::Queue<Common::JSONValue *> _hostDataQueue;
 	Common::Queue<int> _peerIndexQueue;
 
-	struct Session {
-		bool local = false;
-		int id = -1;
-		Common::String host;
-		int port;
-		Common::String name;
-		int players;
-		uint32 timestamp;
-	};
-
 	Common::Array<Session> _sessions;
 	int _hostPort;
 
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index 9d7de9beaa1..8d44600605f 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -1104,7 +1104,7 @@ void ScummEngine_v72he::o72_arrayOps() {
 	int dim1end, dim1start, dim2end, dim2start;
 	int id, len, b, c, list[128];
 	int offs, tmp, tmp2;
-	uint tmp3;
+	uint tmp3, type;
 
 	byte subOp = fetchScriptByte();
 	int array = fetchScriptWord();
@@ -1190,7 +1190,80 @@ void ScummEngine_v72he::o72_arrayOps() {
 			dim2start++;
 		}
 		break;
-	case SO_FORMATTED_STRING:
+	case 138:		// SO_COMPLEX_ARRAY_MATH_OPERATION
+			{
+				// Borrowed code from script_v100he.cpp
+				// Used by script 84 (Send end of play info) in Backyard Football during online play.
+				int array2 = fetchScriptWord();
+				int array1 = fetchScriptWord();
+				type = pop();
+				int a1_dim1end = pop();
+				int a1_dim1start = pop();
+				int a1_dim2end = pop();
+				int a1_dim2start = pop();
+				int a2_dim1end = pop();
+				int a2_dim1start = pop();
+				int a2_dim2end = pop();
+				int a2_dim2start = pop();
+				dim1end = pop();
+				dim1start = pop();
+				dim2end = pop();
+				dim2start = pop();
+
+				debug(0, "Complex: %d = %d[%d to %d][%d to %d] %c %d[%d to %d][%d to %d]", array,
+					array1, a1_dim1start, a1_dim2end, a1_dim1start, a1_dim2end,
+					" +-&|^"[type],
+					array2, a2_dim1start, a2_dim2end, a2_dim1start, a2_dim2end);
+
+				int a12_num = a1_dim2end - a1_dim2start + 1;
+				int a11_num = a1_dim1end - a1_dim1start + 1;
+				int a22_num = a2_dim2end - a2_dim2start + 1;
+				int a21_num = a2_dim1end - a2_dim1start + 1;
+				int d12_num = dim2end - dim2start + 1;
+				int d11_num = dim1end - dim1start + 1;
+
+				id = readVar(array);
+				if (id == 0) {
+					defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
+				}
+				if (a12_num != a22_num || a12_num != d12_num || a11_num != a21_num || a11_num != d11_num) {
+					error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num);
+				}
+
+				for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start, ++dim2start) {
+					int a2dim1 = a2_dim1start;
+					int a1dim1 = a1_dim1start;
+					int dim1 = dim1start;
+					for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1, ++dim1) {
+						int val1 = readArray(array1, a1_dim2start, a1dim1);
+						int val2 = readArray(array2, a2_dim2start, a2dim1);
+						int res;
+
+						switch (type) {
+						case 1: // Addition
+							res = val2 + val1;
+							break;
+						case 2: // Subtraction
+							res = val2 - val1;
+							break;
+						case 3: // Binary AND
+							res = val2 & val1;
+							break;
+						case 4: // Binary OR
+							res = val2 | val1;
+							break;
+						case 5: // Binary XOR
+							res = val2 ^ val1;
+							break;
+						default:
+							error("o72_arrayOps: case 132 unknown type %d)", type);
+						}
+						writeArray(array, dim2start, dim1, res);
+					}
+				}
+				break;
+			}
+	case 194:		// SO_FORMATTED_STRING
 		decodeScriptString(string);
 		len = resStrLen(string);
 		data = defineArray(array, kStringArray, 0, 0, 0, len);
@@ -1853,6 +1926,14 @@ void ScummEngine_v72he::o72_readINI() {
 	case SO_DWORD: // number
 		if (!strcmp((char *)option, "DisablePrinting") || !strcmp((char *)option, "NoPrinting")) {
 			push(1);
+		} else if (!strcmp((char *)option, "DisableMaiaUpdates")) {
+			// WORKAROUND: Override the update checks.
+			// This gets checked in Baseball 2001 and will check for
+			// updates before connecting to the servers, since we
+			// don't support game updates and such updates appears to
+			// be lost, skip through the whole thing.  This disables
+			// the update button on the login screen as well.
+			push(1);
 		} else if (!strcmp((char *)option, "TextOn")) {
 			push(ConfMan.getBool("subtitles"));
 		} else if (!strcmp((char *)option, "Disk") && (_game.id == GID_BIRTHDAYRED || _game.id == GID_BIRTHDAYYELLOW)) {
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index 3e49c3dbe5b..76455c98e9a 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -617,7 +617,16 @@ void ScummEngine_v6::o6_div() {
 
 void ScummEngine_v6::o6_land() {
 	int a = pop();
-	push(pop() && a);
+	int b = pop();
+	// WORKAROUND: When entering an area, the game will check if
+	// vars 133 and 134 are set, else it will wait for 5 seconds before
+	// showing the coach list.  var133 is set 1 somewhere but var134
+	// is always set at 0. I am going to assume this is a script bug,
+	// so let's skip the 5 second wait.
+	if (_game.id == GID_BASEBALL2001 && _currentRoom == 40 && vm.slot[_currentScript].number == 2122)
+		push(1);
+	else
+		push(b && a);
 }
 
 void ScummEngine_v6::o6_lor() {
@@ -753,6 +762,17 @@ void ScummEngine_v6::o6_jump() {
 	}
 
 	_scriptPointer += offset;
+
+	// WORKAROUND:  When getting the area popuation, the scripts does not break after getting
+	// the popuation.  Not only this may slow down the game a bit, it sends quite a bit of bandwidth
+	// considering we're outside the game.  So let's break the script for 5 seconds 
+	// before jumping back to the beginning.
+	if ((_game.id == GID_BASEBALL2001 && _currentRoom == 39 && vm.slot[_currentScript].number == 2090 && offset == -904) ||
+		(_game.id == GID_BASEBALL2001 && _currentRoom == 40 && vm.slot[_currentScript].number == 2101 && offset == -128)) {
+		vm.slot[_currentScript].delay = 5 * 60; // 5 seconds
+		vm.slot[_currentScript].status = ssPaused;
+		o6_breakHere();
+	}
 }
 
 void ScummEngine_v6::o6_startScript() {


Commit: 568f1207840e4c86cc6211ac5101cf80c9f6bdd2
    https://github.com/scummvm/scummvm/commit/568f1207840e4c86cc6211ac5101cf80c9f6bdd2
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Enable network play on Mac versions.

Changed paths:
    engines/scumm/script_v6.cpp


diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index 76455c98e9a..a09a3398b26 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -534,10 +534,24 @@ void ScummEngine_v6::o6_eq() {
 	int a = pop();
 	int b = pop();
 
+	// WORKAROUND: Online play is disabled in the Macintosh versions of Backyard Football and Backyard Baseball 2001
+	// because the original U32 makes use of DirectPlay, a Windows exclusive API; we now have our own implementation
+	// which is cross-platform compatable.  We get around that by tricking those checks that we are playing on
+	// the Windows version. These scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a).
+	if (_game.id == GID_FOOTBALL && _currentRoom == 2 && (vm.slot[_currentScript].number == 2049 || vm.slot[_currentScript].number == 2050 ||
+		vm.slot[_currentScript].number == 498) && a == 2 && b == 2) {
+		push(0);
+	} else if (_game.id == GID_BASEBALL2001 && _currentRoom == 2 && (vm.slot[_currentScript].number == 10002 || vm.slot[_currentScript].number == 2050) &&
+		a == 2 && b == 2) {
+		push(0);
+	} else if (_game.id == GID_FOOTBALL2002 && _currentRoom == 3 && vm.slot[_currentScript].number == 2079 &&
+		a == 2 && b == 2) {
+		push(0);
+
 	// WORKAROUND: Forces the game version string set via script 1 to be used in both Macintosh and Windows versions,
 	// when checking for save game compatibility. Allows saved games to be shared between Macintosh and Windows versions.
 	// The scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a).
-	if (_game.id == GID_BASEBALL2001 && (vm.slot[_currentScript].number == 291 || vm.slot[_currentScript].number == 292) &&
+	} else if (_game.id == GID_BASEBALL2001 && (vm.slot[_currentScript].number == 291 || vm.slot[_currentScript].number == 292) &&
 		a == 2 && b == 1) {
 		push(1);
 	} else {


Commit: 7fcf9e2440509a8c7d34417f6fdc51101ae947e2
    https://github.com/scummvm/scummvm/commit/7fcf9e2440509a8c7d34417f6fdc51101ae947e2
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Disable "InternetConnect" INI setting.

Changed paths:
    engines/scumm/he/script_v72he.cpp


diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index 8d44600605f..858d90a9b7f 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -1934,6 +1934,14 @@ void ScummEngine_v72he::o72_readINI() {
 			// be lost, skip through the whole thing.  This disables
 			// the update button on the login screen as well.
 			push(1);
+		} else if (!strcmp((char *)option, "InternetConnect")) {
+			// WORKAROUND: In the patched version of Backyard
+			// Football 2002, they added an option to join a game
+			// over the internet by manually inputing an IP address.
+			// Our network implementation does not support this currently,
+			// and we have our own way of connecting to joins over the
+			// Internet anyways, so force disable it.
+			push(0);
 		} else if (!strcmp((char *)option, "TextOn")) {
 			push(ConfMan.getBool("subtitles"));
 		} else if (!strcmp((char *)option, "Disk") && (_game.id == GID_BIRTHDAYRED || _game.id == GID_BIRTHDAYYELLOW)) {


Commit: 5114c0f4387b4645d601ceaa7130455ec20c65e7
    https://github.com/scummvm/scummvm/commit/5114c0f4387b4645d601ceaa7130455ec20c65e7
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Keep querying until the session we want exists.

Changed paths:
    engines/scumm/he/logic/baseball2001.cpp
    engines/scumm/he/logic/football.cpp
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h


diff --git a/engines/scumm/he/logic/baseball2001.cpp b/engines/scumm/he/logic/baseball2001.cpp
index 7c6152a7848..c27b14d2808 100644
--- a/engines/scumm/he/logic/baseball2001.cpp
+++ b/engines/scumm/he/logic/baseball2001.cpp
@@ -105,16 +105,25 @@ case OP_NET_INIT:
 		break;
 
 	case OP_NET_QUERY_SESSIONS:
-		res = _vm->_net->querySessions();
+#ifdef USE_LIBCURL
+		if (_vm->_lobby->_sessionId) {
+			_vm->_net->querySessions();
+			// Only proceed if we've found the session
+			// we're looking for.
+			res = _vm->_net->ifSessionExist(_vm->_lobby->_sessionId);
+		}
+#endif
 		break;
 
 	case OP_NET_JOIN_SESSION:
+#ifdef USE_LIBCURL
 		if (_vm->_lobby->_sessionId) {
 			res = _vm->_net->joinSessionById(_vm->_lobby->_sessionId);
 			if (res) {
 				_vm->_net->stopQuerySessions();
 			}
 		}
+#endif
 		break;
 
 	case OP_NET_END_SESSION:
diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index 7dd0d4f6651..c56708513c8 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -144,7 +144,7 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 	case OP_NET_INIT:
 		// Initialize network system, this gets called at boot up and
 		// sets VAR_NETWORK_AVAILABLE (100).  We just return a 1 if
-		// ENet is compiled.
+		// ENet is compiled.  Used in both 1999 and 2002.
 #ifdef USE_ENET
 		res = 1;
 #endif
@@ -156,16 +156,25 @@ int32 LogicHEfootball::dispatch(int op, int numArgs, int32 *args) {
 		break;
 
 	case OP_NET_QUERY_SESSIONS:
-		res = _vm->_net->querySessions();
+#ifdef USE_LIBCURL
+		if (_vm->_lobby->_sessionId) {
+			_vm->_net->querySessions();
+			// Only proceed if we've found the session
+			// we're looking for.
+			res = _vm->_net->ifSessionExist(_vm->_lobby->_sessionId);
+		}
+#endif
 		break;
 
 	case OP_NET_JOIN_SESSION:
+#ifdef USE_LIBCURL
 		if (_vm->_lobby->_sessionId) {
 			res = _vm->_net->joinSessionById(_vm->_lobby->_sessionId);
 			if (res) {
 				_vm->_net->stopQuerySessions();
 			}
 		}
+#endif
 		break;
 
 	case OP_NET_END_SESSION:
diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 912401eadd3..73296cbc146 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -329,9 +329,24 @@ int Net::joinSessionById(int sessionId) {
 			return doJoinSession(*i);
 		}
 	}
-	warning("Net::joinSessionById(): session %d not found!", sessionId);
+	warning("Net::joinSessionById(): session %d not found", sessionId);
 	return 0;
+}
+
+int Net::ifSessionExist(int sessionId) {
+	debug(1, "Net::ifSessionExist(%d)", sessionId);
+	if (_sessions.empty()) {
+		debug(1, "Net::ifSessionExist(): no sessions");
+		return 0;
+	}
 
+	for (Common::Array<Session>::iterator i = _sessions.begin(); i != _sessions.end(); i++) {
+		if (i->id == sessionId) {
+			return 1;
+		}
+	}
+	debug(1, "Net::ifSessionExist(): session %d not found.", sessionId);
+	return 0;
 }
 
 int Net::doJoinSession(Session session) {
@@ -636,7 +651,7 @@ void Net::stopQuerySessions() {
 
 int Net::querySessions() {
 	debug(1, "Net::querySessions()"); // PN_QuerySessions
-	// Deprecated OP used in Backyard Football 2002 to query sessions,
+	// Deprecated OP used in Football and Baseball to query sessions,
 	// emulate this by using the functions used in Moonbase Commander:
 	startQuerySessions();
 
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index 1fc49dde743..ff367f6958e 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -66,6 +66,7 @@ public:
 	int createSession(char *name);
 	int joinSession(int sessionIndex);
 	int joinSessionById(int sessionId);
+	int ifSessionExist(int sessionId);
 	int endSession();
 	void setSessionServer(Common::String sessionServer);
 	void disableSessionJoining();


Commit: d85c20671467423b5bef7266c06f410468842297
    https://github.com/scummvm/scummvm/commit/d85c20671467423b5bef7266c06f410468842297
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Fix compiling without ENet.

Changed paths:
    engines/scumm/he/logic/baseball2001.cpp
    engines/scumm/he/logic/football.cpp
    engines/scumm/scumm.cpp


diff --git a/engines/scumm/he/logic/baseball2001.cpp b/engines/scumm/he/logic/baseball2001.cpp
index c27b14d2808..5aef724de91 100644
--- a/engines/scumm/he/logic/baseball2001.cpp
+++ b/engines/scumm/he/logic/baseball2001.cpp
@@ -31,20 +31,24 @@
 #include "scumm/he/logic_he.h"
 
 // DirectPlay opcodes:
-#define OP_NET_REMOTE_START_SCRIPT	1492
-#define OP_NET_QUERY_PROVIDERS		1497
-#define OP_NET_CLOSE_PROVIDER		1500
-#define OP_NET_QUERY_SESSIONS		1501
-#define OP_NET_GET_SESSION_NAME		1502
-#define OP_NET_JOIN_SESSION			1504
-#define OP_NET_END_SESSION			1505
-#define OP_NET_ADD_USER				1506
-#define OP_NET_WHO_SENT_THIS		1508
-#define OP_NET_REMOTE_SEND_ARRAY	1509
-#define OP_NET_INIT					1513
-#define OP_NET_WHO_AM_I				1510
-#define OP_NET_INIT_LAN_GAME		1515
-#define OP_NET_SET_PROVIDER_BY_NAME	1516
+#define OP_NET_REMOTE_START_SCRIPT		1492
+#define OP_NET_QUERY_PROVIDERS			1497
+#define OP_NET_CLOSE_PROVIDER			1500
+#define OP_NET_QUERY_SESSIONS			1501
+#define OP_NET_GET_SESSION_NAME			1502
+#define OP_NET_JOIN_SESSION				1504
+#define OP_NET_END_SESSION				1505
+#define OP_NET_ADD_USER					1506
+#define OP_NET_WHO_SENT_THIS			1508
+#define OP_NET_REMOTE_SEND_ARRAY		1509
+#define OP_NET_INIT						1513
+#define OP_NET_WHO_AM_I					1510
+#define OP_NET_INIT_LAN_GAME			1515
+#define OP_NET_SET_PROVIDER_BY_NAME		1516
+
+// MAIA (Updater) opcodes.
+#define OP_NET_CHECK_INTERNET_STATUS	3001
+
 
 namespace Scumm {
 
diff --git a/engines/scumm/he/logic/football.cpp b/engines/scumm/he/logic/football.cpp
index c56708513c8..052892d4d41 100644
--- a/engines/scumm/he/logic/football.cpp
+++ b/engines/scumm/he/logic/football.cpp
@@ -34,20 +34,24 @@
 #include "scumm/he/logic_he.h"
 
 // DirectPlay opcodes:
-#define OP_NET_REMOTE_START_SCRIPT	1492
-#define OP_NET_QUERY_PROVIDERS		1497
-#define OP_NET_CLOSE_PROVIDER		1500
-#define OP_NET_QUERY_SESSIONS		1501
-#define OP_NET_GET_SESSION_NAME		1502
-#define OP_NET_JOIN_SESSION			1504
-#define OP_NET_END_SESSION			1505
-#define OP_NET_ADD_USER				1506
-#define OP_NET_WHO_SENT_THIS		1508
-#define OP_NET_REMOTE_SEND_ARRAY	1509
-#define OP_NET_INIT					1513
-#define OP_NET_WHO_AM_I				1510
-#define OP_NET_INIT_LAN_GAME		1515
-#define OP_NET_SET_PROVIDER_BY_NAME	1516
+#define OP_NET_REMOTE_START_SCRIPT		1492
+#define OP_NET_QUERY_PROVIDERS			1497
+#define OP_NET_CLOSE_PROVIDER			1500
+#define OP_NET_QUERY_SESSIONS			1501
+#define OP_NET_GET_SESSION_NAME			1502
+#define OP_NET_JOIN_SESSION				1504
+#define OP_NET_END_SESSION				1505
+#define OP_NET_ADD_USER					1506
+#define OP_NET_WHO_SENT_THIS			1508
+#define OP_NET_REMOTE_SEND_ARRAY		1509
+#define OP_NET_INIT						1513
+#define OP_NET_WHO_AM_I					1510
+#define OP_NET_INIT_LAN_GAME			1515
+#define OP_NET_SET_PROVIDER_BY_NAME		1516
+
+// MAIA (Updater) opcodes.
+#define OP_NET_CHECK_INTERNET_STATUS	3001
+
 
 namespace Scumm {
 
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 6d0645df23d..5151dc0ba95 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -3457,7 +3457,7 @@ bool ScummEngine::displayMessageYesNo(const char *message, ...) {
 	return runDialog(dialog) == GUI::kMessageOK;
 }
 
-#ifdef ENABLE_HE
+#if defined(ENABLE_HE) && defined(USE_ENET)
 int ScummEngine_v90he::networkSessionDialog() {
 	GUI::MessageDialog dialog(_("Would you like to host or join a network play session?"), _("Host"), _("Join"));
 	int res = runDialog(dialog);


Commit: 4aede052e36bc006aa93798b1ac7726c8d18ca12
    https://github.com/scummvm/scummvm/commit/4aede052e36bc006aa93798b1ac7726c8d18ca12
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Migrate competitive mods.

Changed paths:
    engines/scumm/dialogs.cpp
    engines/scumm/dialogs.h
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h
    engines/scumm/script.cpp
    engines/scumm/script_v6.cpp


diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index b55cb30393b..b739ebc3fbf 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -1365,8 +1365,9 @@ HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Co
 		// Lobby configuration (Do not include LAN settings)
 #ifdef USE_LIBCURL
 		text->setLabel(_("Online Server:"));
-		_lobbyServerAddr = new GUI::EditTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.LobbyServerAddress", Common::U32String(""), _("Address of the server to connect to for online play."));
+		_lobbyServerAddr = new GUI::EditTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.LobbyServerAddress", Common::U32String(""), _("Address of the server to connect to for online play.  It must start with either \"https://\" or \"http://\" schemas."));
 		_serverResetButton = addClearButton(widgetsBoss(), "HENetworkGameOptionsDialog.ServerReset", kResetServersCmd);
+		_enableCompetitiveMods = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.EnableCompetitiveMods", _("Enable online competitive mods"), _("Enables custom-made modifications intented for online competitive play."));
 #endif
 	} else {
 		// Network configuration (Include LAN settings)
@@ -1383,9 +1384,14 @@ void HENetworkGameOptionsWidget::load() {
 	if (_gameid == "football" || _gameid == "baseball2001") {
 #ifdef USE_LIBCURL
 		Common::String lobbyServerAddr = "https://multiplayer.scummvm.org:9130";
+		bool enableCompetitiveMods = false;
+
 		if (ConfMan.hasKey("lobby_server", _domain))
 			lobbyServerAddr = ConfMan.get("lobby_server", _domain);
 		_lobbyServerAddr->setEditString(lobbyServerAddr);
+		if (ConfMan.hasKey("enable_competitive_mods", _domain))
+			enableCompetitiveMods = ConfMan.getBool("enable_competitive_mods", _domain);
+		_enableCompetitiveMods->setState(enableCompetitiveMods);
 #endif
 	} else {
 		bool enableSessionServer = true;
@@ -1411,6 +1417,7 @@ bool HENetworkGameOptionsWidget::save() {
 	if (_gameid == "football" || _gameid == "baseball2001") {
 #ifdef USE_LIBCURL
 		ConfMan.set("lobby_server", _lobbyServerAddr->getEditString(), _domain);
+		ConfMan.setBool("enable_competitive_mods", _enableCompetitiveMods->getState(), _domain);
 #endif
 	} else {
 		ConfMan.setBool("enable_session_server", _enableSessionServer->getState(), _domain);
@@ -1432,6 +1439,7 @@ void HENetworkGameOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Com
 					.addWidget("LobbyServerAddress", "EditTextWidget")
 					.addWidget("ServerReset", "", 15, 15)
 				.closeLayout()
+				.addWidget("EnableCompetitiveMods", "Checkbox")
 			.closeLayout()
 		.closeDialog();
 #endif
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index 0e8fb922be9..33c0a9968d4 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -352,6 +352,8 @@ private:
 
 	GUI::EditTextWidget *_lobbyServerAddr;
 	GUI::ButtonWidget *_lobbyResetButton;
+
+	GUI::CheckboxWidget *_enableCompetitiveMods;
 };
 #endif
 
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 57c5a9435a2..68ae6171bd5 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -157,15 +157,12 @@ void Lobby::processLine(Common::String line) {
 		} else if (command == "game_session") {
 			int session = root["session"]->asIntegerNumber();
 			handleGameSession((int)session);
-		} else if (command == "game_relay") {
-			// int relay = root["relay"]->asIntegerNumber();
-			// handleGameRelay(relay);
 		} else if (command == "teams") {
-			// int error = root["error"]->asIntegerNumber();
-			// Common::String message = root["message"]->asString();
-			// Common::JSONArray userTeam = root["user"]->asArray();
-			// Common::JSONArray opponentTeam = root["opponent"]->asArray();
-			// handleTeams(userTeam, opponentTeam, (int)error, message);
+			int error = root["error"]->asIntegerNumber();
+			Common::String message = root["message"]->asString();
+			Common::JSONArray userTeam = root["user"]->asArray();
+			Common::JSONArray opponentTeam = root["opponent"]->asArray();
+			handleTeams(userTeam, opponentTeam, error, message);
 		}
 	}
 }
@@ -465,6 +462,45 @@ void Lobby::handleProfileInfo(Common::JSONArray profile) {
 	_vm->writeVar(111, 1);
 }
 
+void Lobby::handleTeams(Common::JSONArray userTeam, Common::JSONArray opponentTeam, int error, Common::String message) {
+	if (ConfMan.getBool("enable_competitive_mods")) {
+		if (error == 1) {
+			warning("LOBBY: Unable to retrieve custom teams: %s", message.c_str());
+			_vm->writeVar(747, 0);
+			return;
+		}
+		// We're going to store our team in array 748, which seems to be otherwise unused
+		// Then we'll pull from that array as needed later
+		int userTeamArray = 0;
+		_vm->defineArray(748, ScummEngine_v90he::kIntArray, 0, 0, 0, userTeam.size(), true, &userTeamArray);
+		_vm->writeVar(748, userTeamArray);
+
+		for (uint i = 0; i < userTeam.size(); i++) {
+			if (userTeam[i]->isIntegerNumber()) {
+				_vm->writeArray(748, 0, i, userTeam[i]->asIntegerNumber());
+			} else {
+				warning("LOBBY: Value for user team index %d is not an integer!", i);
+			}
+		}
+
+		// And similarly store the opponent's team in array 749
+		int opponentTeamArray = 0;
+		_vm->defineArray(749, ScummEngine_v90he::kIntArray, 0, 0, 0, opponentTeam.size(), true, &opponentTeamArray);
+		_vm->writeVar(749, opponentTeamArray);
+
+		for (uint i = 0; i < opponentTeam.size(); i++) {
+			if (opponentTeam[i]->isIntegerNumber()) {
+				_vm->writeArray(749, 0, i, opponentTeam[i]->asIntegerNumber());
+			} else {
+				warning("LOBBY: Value for opponent team index %d is not an integer!", i);
+			}
+		}
+
+		// Write a one to var747 to indicate that Prince Rupert teams should be pulled from arrays 748 and 749
+		_vm->writeVar(747, 1);
+	}
+}
+
 void Lobby::setIcon(int icon) {
 	if (!_socket)
 		return;
@@ -825,14 +861,15 @@ void Lobby::acceptChallenge(int playerId) {
 	acceptChallengeRequest.setVal("user", new Common::JSONValue((long long int)playerId));
 	send(acceptChallengeRequest);
 
-	// TODO: Competitive mods toggle
-	// if (_vm->_game.id == GID_BASEBALL2001 && _vm->readVar(559) == 19) {  // Only if in Prince Rupert
-	// 	// Request teams for this client and opponent
-	// 	Common::JSONObject getTeamsRequest;
-	// 	getTeamsRequest.setVal("cmd", new Common::JSONValue("get_teams"));
-	// 	getTeamsRequest.setVal("opponent_id", new Common::JSONValue((long long int)playerId));
-	// 	send(getTeamsRequest);
-	// }
+	if (ConfMan.getBool("enable_competitive_mods")) {
+		if (_vm->_game.id == GID_BASEBALL2001 && _vm->readVar(559) == 19) {  // Only if in Prince Rupert
+			// Request teams for this client and opponent
+			Common::JSONObject getTeamsRequest;
+			getTeamsRequest.setVal("cmd", new Common::JSONValue("get_teams"));
+			getTeamsRequest.setVal("opponent_id", new Common::JSONValue((long long int)playerId));
+			send(getTeamsRequest);
+		}
+	}
 }
 
 void Lobby::handleAcceptChallenge() {
@@ -849,7 +886,7 @@ void Lobby::handleAcceptChallenge() {
 void Lobby::startHostingGame(int playerId) {
 	if (!_socket)
 		return;
-	
+
 	_playerId = playerId;
 	_vm->writeVar(111, 0);
 
@@ -906,13 +943,16 @@ void Lobby::handleGameSession(int sessionId) {
 }
 
 void Lobby::gameStarted(int hoster, int player, int playerNameArray) {
-	// if (_vm->_game.id == GID_BASEBALL2001 && _vm->readVar(399) == 1 && _vm->readVar(686) == 1) {  // Only if we're online and in Prince Rupert
-		// 	// Request teams for this client and opponent
-		// Common::JSONObject getTeamsRequest;
-		// getTeamsRequest.setVal("cmd", new Common::JSONValue("get_teams"));
-		// getTeamsRequest.setVal("opponent_id", new Common::JSONValue((long long int)player));
-		// send(getTeamsRequest);
-	// }
+	if (ConfMan.getBool("enable_competitive_mods")) {
+		// Only if we're online and in Prince Rupert
+		if (_vm->_game.id == GID_BASEBALL2001 && _vm->readVar(399) == 1 && _vm->readVar(686) == 1) {
+			// Request teams for this client and opponent
+			Common::JSONObject getTeamsRequest;
+			getTeamsRequest.setVal("cmd", new Common::JSONValue("get_teams"));
+			getTeamsRequest.setVal("opponent_id", new Common::JSONValue((long long int)player));
+			send(getTeamsRequest);
+		}
+	}
 
 	char playerName[16];
 	_vm->getStringFromArray(playerNameArray, playerName, sizeof(playerName));
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index 4a747012087..ffd68507c9b 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -62,7 +62,6 @@
 
 // MAIA (Updater) opcodes.
 #define OP_NET_UPDATE_INIT						3000
-#define OP_NET_CHECK_INTERNET_STATUS			3001
 #define OP_NET_FETCH_UPDATES					3002
 
 // Commands for VAR_REMOTE_START_SCRIPT. (55 in football; 324 in baseball)
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 31cb10bb539..f6d07069840 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -558,6 +558,17 @@ int ScummEngine::readVar(uint var) {
 			return !ConfMan.getBool("subtitles");
 		}
 
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+		if (ConfMan.getBool("enable_competitive_mods")) {
+			// HACK: If we're reading var586, competitive mods enabled, playing online,
+			// successfully fetched custom teams, and we're not in one of the three scripts
+			// that cause bugs if 263 is returned here, return 263.
+			if ( _game.id == GID_BASEBALL2001 && var == 586 && readVar(399) == 1 && readVar(747) == 1 &&
+				!(_currentRoom == 4 && ( vm.slot[_currentScript].number == 2150 || vm.slot[_currentScript].number == 2208 || vm.slot[_currentScript].number == 2210))) {
+				return 263;
+			}
+		}
+#endif
 		assertRange(0, var, _numVariables - 1, "variable (reading)");
 		return _scummVars[var];
 	}
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index a09a3398b26..4e66a4b15ef 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -505,7 +505,28 @@ void ScummEngine_v6::o6_byteArrayRead() {
 
 void ScummEngine_v6::o6_wordArrayRead() {
 	int base = pop();
-	push(readArray(fetchScriptWord(), 0, base));
+	int array = fetchScriptWord();
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+	if (ConfMan.getBool("enable_competitive_mods")) {
+		// If we're pulling from the randomly selected teams for online play
+		// at Prince Rupert, read from variables 748 and 749 instead
+		if (_game.id == GID_BASEBALL2001 && _currentRoom == 6 && vm.slot[_currentScript].number == 2071 &&
+			readVar(399) == 1 &&  // We're online and in the team name select screen
+			readVar(747) == 1) {  // We successfully got team arrays the host and opponent
+			switch (array) {
+			case 264:
+			case 321:
+				array = 748;
+				break;
+			case 265:
+			case 322:
+				array = 749;
+				break;
+			}
+		}
+	}
+#endif
+	push(readArray(array, 0, base));
 }
 
 void ScummEngine_v6::o6_byteArrayIndexedRead() {
@@ -534,11 +555,44 @@ void ScummEngine_v6::o6_eq() {
 	int a = pop();
 	int b = pop();
 
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+	int offset = _scriptPointer - _scriptOrgPointer;
+	// WORKAROUND: In Backyard Baseball 2001, The special rules of the Mountain Aire and Wilderness neighborhoods
+	// are incorrect.  They were set to "3 innings" and "no swing spot" respectively, while they were supposed to be set to
+	// "no special rules" and "3 innings".  This is a script bug which assumed to be fixed in later post-retail updates, but
+	// since we don't have access to any of those, this workaround will have to do.
+	if (_game.id == GID_BASEBALL2001 && vm.slot[_currentScript].number == 419 && ((a == 9 && b == 9) || (a == 8 && b == 8))) {
+		switch (a) {
+		case 9:
+			// Mountain Aire (No special rules)
+			writeVar(695, 0);
+			break;
+		case 8:
+			// Wilderness (3 innings)
+			writeVar(695, 64);
+			break;
+		}
+
+		// Clean up stack and stop the script
+		fetchScriptWord();
+		pop();
+		stopObjectCode();
+
+	// HACK: This script doesn't allow Super Colossal Dome to be chosen for online play, by checking if the selected
+	// field's value is 5 (SCD's number) and incrementing/decrementing if it is. To allow SCD to be used, we return 0
+	// for those checks.
+	} else if (ConfMan.getBool("enable_competitive_mods") && _game.id == GID_BASEBALL2001 && _currentRoom == 40 &&
+		vm.slot[_currentScript].number == 2106 && a == 5 && (offset == 16754 || offset == 16791)) {
+		push(0);
+
 	// WORKAROUND: Online play is disabled in the Macintosh versions of Backyard Football and Backyard Baseball 2001
 	// because the original U32 makes use of DirectPlay, a Windows exclusive API; we now have our own implementation
 	// which is cross-platform compatable.  We get around that by tricking those checks that we are playing on
 	// the Windows version. These scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a).
+	} else if (_game.id == GID_FOOTBALL && _currentRoom == 2 && (vm.slot[_currentScript].number == 2049 || vm.slot[_currentScript].number == 2050 ||
+#else
 	if (_game.id == GID_FOOTBALL && _currentRoom == 2 && (vm.slot[_currentScript].number == 2049 || vm.slot[_currentScript].number == 2050 ||
+#endif
 		vm.slot[_currentScript].number == 498) && a == 2 && b == 2) {
 		push(0);
 	} else if (_game.id == GID_BASEBALL2001 && _currentRoom == 2 && (vm.slot[_currentScript].number == 10002 || vm.slot[_currentScript].number == 2050) &&
@@ -779,7 +833,7 @@ void ScummEngine_v6::o6_jump() {
 
 	// WORKAROUND:  When getting the area popuation, the scripts does not break after getting
 	// the popuation.  Not only this may slow down the game a bit, it sends quite a bit of bandwidth
-	// considering we're outside the game.  So let's break the script for 5 seconds 
+	// considering we're outside the game.  So let's break the script for 5 seconds
 	// before jumping back to the beginning.
 	if ((_game.id == GID_BASEBALL2001 && _currentRoom == 39 && vm.slot[_currentScript].number == 2090 && offset == -904) ||
 		(_game.id == GID_BASEBALL2001 && _currentRoom == 40 && vm.slot[_currentScript].number == 2101 && offset == -128)) {
@@ -1392,6 +1446,23 @@ void ScummEngine_v6::o6_getRandomNumberRange() {
 	int min = pop();
 	int rnd = _rnd.getRandomNumber(0x7fff);
 	rnd = min + (rnd % (max - min + 1));
+#if defined(USE_ENET) && defined(USE_LIBCURL)
+	if (ConfMan.getBool("enable_competitive_mods")) {
+		// For using predefined teams in Prince Rupert, instead of choosing player IDs randomly
+		// let's pull from the variables that contain the teams
+		if (_game.id == GID_BASEBALL2001 && vm.slot[_currentScript].number == 298 &&
+			readVar(399) == 1 && readVar(747) == 1) {
+			int offset = _scriptPointer - _scriptOrgPointer;
+			if (offset == 117) {
+				// Host's team
+				rnd = readArray(748, 0, vm.localvar[_currentScript][1]);
+			} else if (offset == 210) {
+				// Opponent's team
+				rnd = readArray(749, 0, vm.localvar[_currentScript][1]);
+			}
+		}
+	}
+#endif
 	if (VAR_RANDOM_NR != 0xFF)
 		VAR(VAR_RANDOM_NR) = rnd;
 	push(rnd);


Commit: 7fb576decb014ed6d2bbfdcbe5cce58a8e2bfb32
    https://github.com/scummvm/scummvm/commit/7fb576decb014ed6d2bbfdcbe5cce58a8e2bfb32
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Accept server address change mid-game.

Changed paths:
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h


diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 73296cbc146..bcdbe13e2e6 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -43,12 +43,7 @@ Net::Net(ScummEngine_v90he *vm) : _latencyTime(1), _fakeLatency(false), _vm(vm)
 	_broadcastSocket = nullptr;
 
 	_sessionServerAddress = Address {"multiplayer.scummvm.org", 9120};
-	if (ConfMan.hasKey("session_server")) {
-		_sessionServerAddress = getAddressFromString(ConfMan.get("session_server"));
-		// Set port to default if not defined.
-		if (!_sessionServerAddress.port)
-			_sessionServerAddress.port = 9120;
-	}
+	_forcedAddress = false;
 
 	_sessionServerPeer = -1;
 	_sessionServerHost = nullptr;
@@ -104,6 +99,7 @@ Common::String Net::getStringFromAddress(Address address) {
 void Net::setSessionServer(Common::String sessionServer) {
 	debug(1, "Net::setSessionServer(\"%s\")", sessionServer.c_str());
 
+	_forcedAddress = true;
 	ConfMan.setBool("enable_session_server", true);
 	ConfMan.setBool("enable_lan_broadcast", false);
 
@@ -287,6 +283,12 @@ int Net::createSession(char *name) {
 		enableLanBroadcast = ConfMan.getBool("enable_lan_broadcast");
 
 	if (enableSessionServer) {
+		if (!_forcedAddress && ConfMan.hasKey("session_server")) {
+			_sessionServerAddress = getAddressFromString(ConfMan.get("session_server"));
+			// Set port to default if not defined.
+			if (!_sessionServerAddress.port)
+				_sessionServerAddress.port = 9120;
+		}
 		if (_sessionHost->connectPeer(_sessionServerAddress.host, _sessionServerAddress.port)) {
 			// FIXME: Get the IP address of the session server when a domain address is used.
 
@@ -578,6 +580,12 @@ int32 Net::startQuerySessions(bool connectToSessionServer) {
 
 	if (connectToSessionServer && enableSessionServer) {
 		if (!_sessionServerHost) {
+			if (!_forcedAddress && ConfMan.hasKey("session_server")) {
+				_sessionServerAddress = getAddressFromString(ConfMan.get("session_server"));
+				// Set port to default if not defined.
+				if (!_sessionServerAddress.port)
+					_sessionServerAddress.port = 9120;
+			}
 			_sessionServerHost = _enet->connectToHost(_sessionServerAddress.host, _sessionServerAddress.port);
 			if (!_sessionServerHost)
 				warning("Failed to connect to session server!  You'll won't be able to join internet sessions");
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index ff367f6958e..2488be00ca2 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -160,6 +160,7 @@ public:
 	// For creating/joining sessions over the Internet.
 	Networking::Host *_sessionServerHost;
 	Address _sessionServerAddress;
+	bool _forcedAddress;
 	bool _gotSessions;
 	int _sessionServerPeer;
 	bool _isRelayingGame; // If we're relaying in-game data over the session server or not.


Commit: 653b1a19dfb7e189914576a3df4b54c01bc44625
    https://github.com/scummvm/scummvm/commit/653b1a19dfb7e189914576a3df4b54c01bc44625
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Re-add audio override option if needed.

Changed paths:
    engines/scumm/dialogs.cpp
    engines/scumm/dialogs.h


diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index b739ebc3fbf..27303394680 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -1355,10 +1355,13 @@ HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Co
 		ScummOptionsContainerWidget(boss, name, "HENetworkGameOptionsDialog", domain), _gameid(gameid) {
 	Common::String extra = ConfMan.get("extra", domain);
 
-	// TODO: Re-add "Load modded audio" option.
+	// Add back the "Load modded audio" option.
+	const Common::String guiOptionsString = ConfMan.get("guioptions", domain);
+	const Common::String guiOptions = parseGameGUIOptions(guiOptionsString);
+	if (guiOptions.contains(GUIO_AUDIO_OVERRIDE))
+		_audioOverride = new GUI::CheckboxWidget(widgetsBoss(), "HENetworkGameOptionsDialog.AudioOverride", _("Load modded audio"), _("Replace music, sound effects, and speech clips with modded audio files, if available."));
 
 	GUI::StaticTextWidget *text = new GUI::StaticTextWidget(widgetsBoss(), "HENetworkGameOptionsDialog.SessionServerLabel", _("Multiplayer Server:"));
-
 	text->setAlign(Graphics::TextAlign::kTextAlignEnd);
 
 	if (_gameid == "football" || _gameid == "baseball2001") {
@@ -1381,6 +1384,12 @@ HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Co
 }
 
 void HENetworkGameOptionsWidget::load() {
+	if (_audioOverride) {
+		bool audioOverride = true;
+		if (ConfMan.hasKey("audio_override", _domain))
+			audioOverride = ConfMan.getBool("audio_override", _domain);
+		_audioOverride->setState(audioOverride);
+	}
 	if (_gameid == "football" || _gameid == "baseball2001") {
 #ifdef USE_LIBCURL
 		Common::String lobbyServerAddr = "https://multiplayer.scummvm.org:9130";
@@ -1414,6 +1423,8 @@ void HENetworkGameOptionsWidget::load() {
 }
 
 bool HENetworkGameOptionsWidget::save() {
+	if (_audioOverride)
+		ConfMan.setBool("audio_override", _audioOverride->getState(), _domain);
 	if (_gameid == "football" || _gameid == "baseball2001") {
 #ifdef USE_LIBCURL
 		ConfMan.set("lobby_server", _lobbyServerAddr->getEditString(), _domain);
@@ -1433,6 +1444,7 @@ void HENetworkGameOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Com
 		layouts.addDialog(layoutName, overlayedLayout)
 			.addLayout(GUI::ThemeLayout::kLayoutVertical, 5)
 				.addPadding(0, 0, 12, 0)
+				.addWidget("AudioOverride", "Checkbox")
 				.addLayout(GUI::ThemeLayout::kLayoutHorizontal, 12)
 					.addPadding(0, 0, 12, 0)
 					.addWidget("SessionServerLabel", "OptionsLabel")
diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h
index 33c0a9968d4..ae3ce2f652e 100644
--- a/engines/scumm/dialogs.h
+++ b/engines/scumm/dialogs.h
@@ -343,6 +343,8 @@ private:
 	void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
 	void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data) override;
 
+	GUI::CheckboxWidget *_audioOverride;
+
 	GUI::CheckboxWidget *_enableSessionServer;
 
 	GUI::EditTextWidget *_sessionServerAddr;


Commit: af2477f12217e587071ba1da7cdd207d7d288b97
    https://github.com/scummvm/scummvm/commit/af2477f12217e587071ba1da7cdd207d7d288b97
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Make some network class members public.

Changed paths:
    engines/scumm/he/net/net_main.h


diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index 2488be00ca2..68662220343 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -120,6 +120,14 @@ public:
 	int _latencyTime; // ms
 	bool _fakeLatency;
 
+	bool _isHost;  // true = hosting game, false = joined game.
+
+	int _myUserId;
+	int _fromUserId;
+
+	int _sessionId; // Session ID received from the session server.
+
+private:
 	ScummEngine_v90he *_vm;
 
 	Common::String _gameName;
@@ -138,14 +146,9 @@ public:
 	Common::HashMap<int, Common::String> _userIdToAddress;
 	Common::HashMap<Common::String, int> _addressToUserId;
 
-	int _myUserId;
-	int _fromUserId;
-
-	int _sessionId; // Session ID received from the session server.
 	Common::String _sessionName;
 	Networking::Host *_sessionHost;
 
-	bool _isHost;  // true = hosting game, false = joined game.
 	bool _isShuttingDown;
 
 	Common::Queue<Common::JSONValue *> _hostDataQueue;


Commit: 84866701b72f101c7f3aff12c96eb7434b61cd44
    https://github.com/scummvm/scummvm/commit/84866701b72f101c7f3aff12c96eb7434b61cd44
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: CURL: Timeout if send/recv takes too long

Changed paths:
    backends/networking/curl/socket.cpp


diff --git a/backends/networking/curl/socket.cpp b/backends/networking/curl/socket.cpp
index 9b96e8c192f..306b6b46eb6 100644
--- a/backends/networking/curl/socket.cpp
+++ b/backends/networking/curl/socket.cpp
@@ -22,6 +22,7 @@
 #define FORBIDDEN_SYMBOL_ALLOW_ALL
 #include "backends/networking/curl/socket.h"
 #include "common/debug.h"
+#include "common/system.h"
 #include <curl/curl.h>
 
 // Auxiliary function that waits on the socket.
@@ -106,9 +107,13 @@ size_t CurlSocket::send(const char *data, int len) {
 	// or times out.
 	while (((left > 0) && (len > 0))) {
 		size_t nsent = 0;
+		uint32 tickCount = g_system->getMillis() + 5000;
 		while (res == CURLE_AGAIN) {
-			// TODO: Time out if it takes too long.
 			res = curl_easy_send(_easy, data + nsent_total, left - nsent_total, &nsent);
+			if (g_system->getMillis() >= tickCount) {
+				warning("libcurl: Took too long attempting to send data to socket");
+				return nsent;
+			}
 		}
 		if (res == CURLE_OK) {
 			nsent_total += nsent;
@@ -125,13 +130,17 @@ size_t CurlSocket::send(const char *data, int len) {
 size_t CurlSocket::recv(void *data, int maxLen) {
 	size_t nread = 0;
 	CURLcode res = CURLE_AGAIN;
+	uint32 tickCount = g_system->getMillis() + 5000;
 	while (res == CURLE_AGAIN) {
-		// TODO: Time out if it takes too long.
 		res = curl_easy_recv(_easy, data, maxLen, &nread);
+		if (g_system->getMillis() >= tickCount) {
+			warning("libcurl: Took too long attempting to read data from socket");
+			return nread;
+		}
 	}
 	if(res != CURLE_OK) {
 		warning("libcurl Error on receiving data: %s\n", curl_easy_strerror(res));
-		return -1;
+		return nread;
 	}
 
 	debug(1, "libcurl: Received %lu bytes", (uint64)nread);


Commit: f9f2023b2a16f5b163e34e0a8a6bc483c16807d9
    https://github.com/scummvm/scummvm/commit/f9f2023b2a16f5b163e34e0a8a6bc483c16807d9
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Fix space indentation to tab.

Changed paths:
    backends/networking/curl/socket.cpp
    backends/networking/enet/enet.cpp


diff --git a/backends/networking/curl/socket.cpp b/backends/networking/curl/socket.cpp
index 306b6b46eb6..0ee8411924d 100644
--- a/backends/networking/curl/socket.cpp
+++ b/backends/networking/curl/socket.cpp
@@ -74,7 +74,7 @@ bool CurlSocket::connect(Common::String url) {
 		curl_easy_setopt(_easy, CURLOPT_URL, url.c_str());
 		// Just connect to the host, do not do any transfers.
 		curl_easy_setopt(_easy, CURLOPT_CONNECT_ONLY, 1L);
-		
+
 		// Uncomment this to disable SSL certificate verification
 		// (e.g. self-signed certs).
 		// curl_easy_setopt(_easy, CURLOPT_SSL_VERIFYPEER, 0L);
diff --git a/backends/networking/enet/enet.cpp b/backends/networking/enet/enet.cpp
index 381bcf75dec..6e874926e3d 100644
--- a/backends/networking/enet/enet.cpp
+++ b/backends/networking/enet/enet.cpp
@@ -132,9 +132,9 @@ Socket *ENet::createSocket(Common::String address, int port) {
 	}
 
 	enet_socket_set_option (enetSocket, ENET_SOCKOPT_NONBLOCK, 1);
-    enet_socket_set_option (enetSocket, ENET_SOCKOPT_BROADCAST, 1);
+	enet_socket_set_option (enetSocket, ENET_SOCKOPT_BROADCAST, 1);
 	enet_socket_set_option (enetSocket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
-    enet_socket_set_option (enetSocket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
+	enet_socket_set_option (enetSocket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
 
 	return new Socket(enetSocket);
 }


Commit: af035e3535d0a292c0c7e11cb857d9cf21519576
    https://github.com/scummvm/scummvm/commit/af035e3535d0a292c0c7e11cb857d9cf21519576
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Fix crash when loading options in-game.

Changed paths:
    engines/scumm/dialogs.cpp


diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 27303394680..5c52b5b2f76 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -1356,6 +1356,7 @@ HENetworkGameOptionsWidget::HENetworkGameOptionsWidget(GuiObject *boss, const Co
 	Common::String extra = ConfMan.get("extra", domain);
 
 	// Add back the "Load modded audio" option.
+	_audioOverride = nullptr;
 	const Common::String guiOptionsString = ConfMan.get("guioptions", domain);
 	const Common::String guiOptions = parseGameGUIOptions(guiOptionsString);
 	if (guiOptions.contains(GUIO_AUDIO_OVERRIDE))


Commit: eba89934c3436291b4b089ee883179b8d3b744ad
    https://github.com/scummvm/scummvm/commit/eba89934c3436291b4b089ee883179b8d3b744ad
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Unmark custom teams when disconnected.

Changed paths:
    engines/scumm/he/net/net_lobby.cpp


diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index 68ae6171bd5..c0577d468a4 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -379,6 +379,10 @@ void Lobby::disconnect(bool lost) {
 		systemAlert(901, "You have been disconnected from our server. Returning to login screen.");
 	}
 
+	if (ConfMan.getBool("enable_competitive_mods"))
+		// set var747 (custom teams status) to 0
+		_vm->writeVar(747, 0);
+
 	delete _socket;
 	_socket = nullptr;
 


Commit: edce46d28ed3fd517d727c16db4b8405c5598b65
    https://github.com/scummvm/scummvm/commit/edce46d28ed3fd517d727c16db4b8405c5598b65
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Send max players to session server.

Changed paths:
    engines/scumm/he/net/net_main.cpp


diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index bcdbe13e2e6..22b25f86280 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -296,8 +296,8 @@ int Net::createSession(char *name) {
 			_sessionServerPeer = 0;
 			// Create session to the session server.
 			Common::String req = Common::String::format(
-				"{\"cmd\":\"host_session\",\"game\":\"%s\",\"version\":\"%s\",\"name\":\"%s\"}",
-				_gameName.c_str(), _gameVersion.c_str(), name);
+				"{\"cmd\":\"host_session\",\"game\":\"%s\",\"version\":\"%s\",\"name\":\"%s\",\"maxplayers\":%d}",
+				_gameName.c_str(), _gameVersion.c_str(), name, _maxPlayers);
 			debug(1, "NETWORK: Sending to session server: %s", req.c_str());
 			_sessionHost->send(req.c_str(), _sessionServerPeer);
 		} else {


Commit: b7334716917b82fc7d74a6b558efeb0e0410610d
    https://github.com/scummvm/scummvm/commit/b7334716917b82fc7d74a6b558efeb0e0410610d
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Fix case error message.

Changed paths:
    engines/scumm/he/script_v72he.cpp


diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index 858d90a9b7f..61084b09d16 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -1256,7 +1256,7 @@ void ScummEngine_v72he::o72_arrayOps() {
 							res = val2 ^ val1;
 							break;
 						default:
-							error("o72_arrayOps: case 132 unknown type %d)", type);
+							error("o72_arrayOps: case 138 unknown type %d)", type);
 						}
 						writeArray(array, dim2start, dim1, res);
 					}


Commit: 11d9631003f4b5cf119eeb0d0f78909044a5c58c
    https://github.com/scummvm/scummvm/commit/11d9631003f4b5cf119eeb0d0f78909044a5c58c
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: CURL: Add missing end lines.

Changed paths:
    backends/networking/curl/socket.h
    backends/networking/curl/url.h


diff --git a/backends/networking/curl/socket.h b/backends/networking/curl/socket.h
index 5d95129a26a..7d365eea067 100644
--- a/backends/networking/curl/socket.h
+++ b/backends/networking/curl/socket.h
@@ -55,4 +55,4 @@ private:
 
 } // End of namespace Networking
 
-#endif
\ No newline at end of file
+#endif
diff --git a/backends/networking/curl/url.h b/backends/networking/curl/url.h
index 7255221b72c..d0608749e35 100644
--- a/backends/networking/curl/url.h
+++ b/backends/networking/curl/url.h
@@ -44,4 +44,4 @@ private:
 
 } // End of Namespace Networking
 
-#endif
\ No newline at end of file
+#endif


Commit: 3ce5203e52757ebf61ab8e3fbdc608b233004ec8
    https://github.com/scummvm/scummvm/commit/3ce5203e52757ebf61ab8e3fbdc608b233004ec8
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Fix code formatting.

Changed paths:
    engines/scumm/he/net/net_defines.h
    engines/scumm/he/net/net_lobby.cpp
    engines/scumm/he/net/net_lobby.h
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h
    engines/scumm/script.cpp


diff --git a/engines/scumm/he/net/net_defines.h b/engines/scumm/he/net/net_defines.h
index d91a8fa9fae..c3efea18036 100644
--- a/engines/scumm/he/net/net_defines.h
+++ b/engines/scumm/he/net/net_defines.h
@@ -52,4 +52,4 @@ const int MAX_IP_SIZE = 32;
 
 } // End of namespace Scumm
 
-#endif
\ No newline at end of file
+#endif
diff --git a/engines/scumm/he/net/net_lobby.cpp b/engines/scumm/he/net/net_lobby.cpp
index c0577d468a4..2eeb37cd439 100644
--- a/engines/scumm/he/net/net_lobby.cpp
+++ b/engines/scumm/he/net/net_lobby.cpp
@@ -904,7 +904,7 @@ void Lobby::startHostingGame(int playerId) {
 	if (_vm->_net->hostGame(const_cast<char *>(_userName.c_str()), const_cast<char *>(_userName.c_str()))) {
 		// Wait till the session server assigns us a session id.
 		uint tickCount = 0;
-		while(_vm->_net->_sessionId == -1) {
+		while (_vm->_net->_sessionId == -1) {
 			_vm->_net->doNetworkOnceAFrame(12);
 			tickCount += 5;
 			g_system->delayMillis(5);
@@ -987,5 +987,3 @@ void Lobby::gameFinished() {
 }
 
 } // End of namespace Scumm
-
-
diff --git a/engines/scumm/he/net/net_lobby.h b/engines/scumm/he/net/net_lobby.h
index ffd68507c9b..c182f12e7dd 100644
--- a/engines/scumm/he/net/net_lobby.h
+++ b/engines/scumm/he/net/net_lobby.h
@@ -186,5 +186,4 @@ protected:
 
 } // End of namespace Scumm
 
-
-#endif
\ No newline at end of file
+#endif
diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 22b25f86280..d529a402182 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -153,7 +153,7 @@ int Net::joinGame(Common::String IP, char *userName) {
 			_broadcastSocket->send(address.host.c_str(), 9130, "{\"cmd\": \"get_session\"}");
 
 			uint tickCount = 0;
-			while(!_sessions.size()) {
+			while (!_sessions.size()) {
 				serviceBroadcast();
 				// Wait for one minute for response before giving up
 				tickCount += 5;
@@ -226,7 +226,7 @@ int Net::addUser(char *shortName, char *longName) {
 	_sessionHost->send(addUser.c_str(), 0, 0, true);
 
 	uint tickCount = 0;
-	while(_myUserId == -1) {
+	while (_myUserId == -1) {
 		remoteReceiveData(12);
 		// Wait for five seconds for our user id before giving up
 		tickCount += 5;
@@ -383,7 +383,7 @@ int Net::doJoinSession(Session session) {
 				_sessionServerHost->send(startRelay.c_str(), 0);
 
 				uint tickCount = 0;
-				while(_myUserId == -1) {
+				while (_myUserId == -1) {
 					serviceSessionServer();
 					// Wait for five seconds for our user id before giving up
 					tickCount += 5;
@@ -610,7 +610,7 @@ int32 Net::updateQuerySessions() {
 
 		_gotSessions = false;
 		uint32 tickCount = g_system->getMillis() + 1000;
-		while(g_system->getMillis() < tickCount) {
+		while (g_system->getMillis() < tickCount) {
 			serviceSessionServer();
 			if (_gotSessions)
 				break;
@@ -621,7 +621,7 @@ int32 Net::updateQuerySessions() {
 		_broadcastSocket->send("255.255.255.255", 9130, "{\"cmd\": \"get_session\"}");
 
 		uint32 tickCount = g_system->getMillis() + 500;
-		while(g_system->getMillis() < tickCount) {
+		while (g_system->getMillis() < tickCount) {
 			serviceBroadcast();
 		}
 	}
@@ -880,7 +880,7 @@ void Net::serviceSessionServer() {
 		return;
 
 	uint8 type = _sessionServerHost->service();
-	switch(type) {
+	switch (type) {
 	case ENET_EVENT_TYPE_NONE:
 		break;
 	case ENET_EVENT_TYPE_DISCONNECT:
@@ -1389,7 +1389,7 @@ void Net::handleGameDataHost(Common::JSONValue *json, int peerIndex) {
 	int toparam = json->child("toparam")->asIntegerNumber();
 	bool reliable = json->child("reliable")->asBool();
 
-	switch(to) {
+	switch (to) {
 	case PN_SENDTYPE_INDIVIDUAL:
 		{
 			if (toparam == _myUserId) {
@@ -1444,4 +1444,4 @@ void Net::handleGameDataHost(Common::JSONValue *json, int peerIndex) {
 	}
 }
 
-} // End of namespace Scumm
\ No newline at end of file
+} // End of namespace Scumm
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index 68662220343..0928ea3abde 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -22,10 +22,10 @@
 #ifndef SCUMM_HE_NET_MAIN_H
 #define SCUMM_HE_NET_MAIN_H
 
-#include "common/formats/json.h"
 #include "backends/networking/enet/enet.h"
 #include "backends/networking/enet/host.h"
 #include "backends/networking/enet/socket.h"
+#include "common/formats/json.h"
 namespace Scumm {
 
 class ScummEngine_v90he;
@@ -56,6 +56,7 @@ private:
 
 	Address getAddressFromString(Common::String address);
 	Common::String getStringFromAddress(Address address);
+
 public:
 	int hostGame(char *sessionName, char *userName);
 	int joinGame(Common::String IP, char *userName);
@@ -104,7 +105,7 @@ private:
 	bool remoteReceiveData(uint32 tickCount);
 
 public:
-	//getters
+	// getters
 	bool getHostName(char *hostname, int length);
 	bool getIPfromName(char *ip, int ipLength, char *nameBuffer);
 	void getSessionName(int sessionNumber, char *buffer, int length);
@@ -112,15 +113,15 @@ public:
 	void getProviderName(int providerIndex, char *buffer, int length);
 
 private:
-	//mostly getters
+	// mostly getters
 	int getTotalPlayers();
 
 public:
-	//fields
+	// fields
 	int _latencyTime; // ms
 	bool _fakeLatency;
 
-	bool _isHost;  // true = hosting game, false = joined game.
+	bool _isHost; // true = hosting game, false = joined game.
 
 	int _myUserId;
 	int _fromUserId;
@@ -171,4 +172,4 @@ private:
 
 } // End of namespace Scumm
 
-#endif
\ No newline at end of file
+#endif
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index f6d07069840..29d8c4b7e2e 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -563,8 +563,8 @@ int ScummEngine::readVar(uint var) {
 			// HACK: If we're reading var586, competitive mods enabled, playing online,
 			// successfully fetched custom teams, and we're not in one of the three scripts
 			// that cause bugs if 263 is returned here, return 263.
-			if ( _game.id == GID_BASEBALL2001 && var == 586 && readVar(399) == 1 && readVar(747) == 1 &&
-				!(_currentRoom == 4 && ( vm.slot[_currentScript].number == 2150 || vm.slot[_currentScript].number == 2208 || vm.slot[_currentScript].number == 2210))) {
+			if (_game.id == GID_BASEBALL2001 && var == 586 && readVar(399) == 1 && readVar(747) == 1 &&
+				!(_currentRoom == 4 && (vm.slot[_currentScript].number == 2150 || vm.slot[_currentScript].number == 2208 || vm.slot[_currentScript].number == 2210))) {
 				return 263;
 			}
 		}


Commit: df90878e622149b4d2b22a9da96a00cab53c6cac
    https://github.com/scummvm/scummvm/commit/df90878e622149b4d2b22a9da96a00cab53c6cac
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Remove unreachable code.

Changed paths:
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h


diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index d529a402182..567ab8577b9 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -1109,17 +1109,16 @@ void Net::handleBroadcastData(Common::String data, Common::String host, int port
 	}
 }
 
-bool Net::remoteReceiveData(uint32 tickCount) {
+void Net::remoteReceiveData(uint32 tickCount) {
 	uint8 messageType = _sessionHost->service();
 	switch (messageType) {
 	case ENET_EVENT_TYPE_NONE:
-		return true;
+		break;
 	case ENET_EVENT_TYPE_CONNECT:
 		{
 			debug(1, "NETWORK: New connection from %s:%d", _sessionHost->getHost().c_str(), _sessionHost->getPort());
-			return true;
+			break;
 		}
-		return true;
 	case ENET_EVENT_TYPE_DISCONNECT:
 		{
 			Common::String address = Common::String::format("%s:%d", _sessionHost->getHost().c_str(), _sessionHost->getPort());
@@ -1140,9 +1139,8 @@ bool Net::remoteReceiveData(uint32 tickCount) {
 					_vm->runScript(2104, 1, 0, 0); // leave-game
 				}
 			}
-			return true;
+			break;
 		}
-		return true;
 	case ENET_EVENT_TYPE_RECEIVE:
 		{
 			Common::String host = _sessionHost->getHost();
@@ -1153,7 +1151,7 @@ bool Net::remoteReceiveData(uint32 tickCount) {
 			if (peerIndex == -1) {
 				warning("NETWORK: Unable to get peer index for host %s:%d", host.c_str(), port);
 				_sessionHost->destroyPacket();
-				return false;
+				break;
 			}
 
 			Common::String data = _sessionHost->getPacketData();
@@ -1161,7 +1159,7 @@ bool Net::remoteReceiveData(uint32 tickCount) {
 
 			if (peerIndex == _sessionServerPeer) {
 				handleSessionServerData(data);
-				return true;
+				break;
 			}
 
 			Common::JSONValue *json = Common::JSON::parse(data.c_str());
@@ -1169,12 +1167,12 @@ bool Net::remoteReceiveData(uint32 tickCount) {
 				// Just about anything could come from the broadcast address, so do not warn.
 				warning("NETWORK: Received non-JSON string.  Got: \"%s\"", data.c_str());
 				_sessionHost->destroyPacket();
-				return false;
+				break;
 			}
 			if (!json->isObject()){
 				warning("NETWORK: Received non JSON object from broadcast socket: \"%s\"", data.c_str());
 				_sessionHost->destroyPacket();
-				return false;
+				break;
 			}
 
 			Common::JSONObject root = json->asObject();
@@ -1186,7 +1184,7 @@ bool Net::remoteReceiveData(uint32 tickCount) {
 						Common::String name = root["name"]->asString();
 						if (getTotalPlayers() > 4) {
 							// We are full.
-							return 0;
+							break;
 						}
 						_userIdToName[++_userIdCounter] = name;
 						_numUsers++;
@@ -1217,7 +1215,7 @@ bool Net::remoteReceiveData(uint32 tickCount) {
 					userId = _addressToUserId[address];
 					if (userId == -1) {
 						warning("Got remove_user but we don't know the user for address: %s", address.c_str());
-						return false;
+						break;
 					}
 					destroyPlayer(userId);
 				} else if (command == "game") {
@@ -1230,10 +1228,10 @@ bool Net::remoteReceiveData(uint32 tickCount) {
 			if (_sessionHost)
 				_sessionHost->destroyPacket();
 		}
-		return true;
 		break;
+	default:
+		warning("NETWORK: Received unknown event type %d", messageType);
 	}
-	return true;
 }
 
 void Net::doNetworkOnceAFrame(int msecs) {
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index 0928ea3abde..dd6b6cdaa71 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -102,7 +102,7 @@ private:
 	void handleBroadcastData(Common::String data, Common::String host, int port);
 	void serviceSessionServer();
 	void handleSessionServerData(Common::String data);
-	bool remoteReceiveData(uint32 tickCount);
+	void remoteReceiveData(uint32 tickCount);
 
 public:
 	// getters


Commit: c7d16aea5c5f45dccead2f245d43ddf80dafff74
    https://github.com/scummvm/scummvm/commit/c7d16aea5c5f45dccead2f245d43ddf80dafff74
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: ENET: Document code.

Changed paths:
    backends/networking/enet/enet.h
    backends/networking/enet/host.h
    backends/networking/enet/socket.h


diff --git a/backends/networking/enet/enet.h b/backends/networking/enet/enet.h
index 70c78bd2eaf..928bfd1b2c8 100644
--- a/backends/networking/enet/enet.h
+++ b/backends/networking/enet/enet.h
@@ -31,15 +31,61 @@ class Socket;
 
 class ENet {
 public:
+	/**
+	 * The main object that allows for ENet host and socket creation.
+	 */
 	ENet();
 	~ENet();
 
+	/**
+	 * Initializes the ENet library.  Must be called first before any other functions.
+	 * @return true if successful, false on failure.
+	 */
 	bool initialize();
+	/**
+	 * Creates a new ENet Host instance for listening for/connecting to peers.
+	 * @param address the address at which other peers may connect to this host.  "0.0.0.0" may be to used to use the default host.
+	 * @param port a port number this host will use.
+	 * @param numClients the maximum number of peers that should be allocated for the host.
+	 * @param numChannels the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
+	 * @param incBand downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+	 * @param outBand upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
+	 * @retval Networking::Host object on success
+	 * @retval nullptr on failure
+	 * @see Networking::Host
+	 */
 	Host *createHost(Common::String address, int port, int numClients, int numChannels = 1, int incBand = 0, int outBand = 0);
+	/**
+	 * Creates a new ENet Host instance, and attempts to connect to the assigned address and port.
+	 * @param hostAddress the address this host will use to connect to this peer.  "0.0.0.0" may be to used to use the default host.
+	 * @param hostPort a port number this host will use.  If not used, the host will use an allocated port given by the operating system.
+	 * @param address the address of the peer that will attempt to connect to.
+	 * @param port the port number of the peer that will attempt to connect to.
+	 * @param timeout specifies the connection timeout in milliseconds, 5 full seconds by default.  Will fail if the given time has passed.
+	 * @param numChannels the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT. This must match with the connecting peer.
+	 * @param incBand downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. This must match with the connecting peer.
+	 * @param outBand upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. This must match with the connecting peer.
+	 * @retval Networking::Host object on success
+	 * @retval nullptr on failure
+	 * @see Networking::Host
+	 */
 	Host *connectToHost(Common::String hostAddress, int hostPort, Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
 	Host *connectToHost(Common::String address, int port, int timeout = 5000, int numChannels = 1, int incBand = 0, int outBand = 0);
+	/**
+	 * Creates a Networking::Socket instance which is a representation of a raw UDP socket.
+	 * Useful for and sending and receiving data that is outside the ENet library protocol.
+	 * @param address the address this socket will send to and/or receive data from.
+	 * @param port the port number this socket will send to and/or receive data from.
+	 * @retval Networking::Socket object on success
+	 * @retval nullptr on failure
+	 * @see Networking::Socket
+	 */
 	Socket *createSocket(Common::String address, int port);
 private:
+	/** 
+	 * Indicates if the ENet library has successfully initialized or not.
+	 * @see initialize()
+	 */
 	bool _initialized;
 };
 
diff --git a/backends/networking/enet/host.h b/backends/networking/enet/host.h
index 3e1e23f1359..5b1432761e5 100644
--- a/backends/networking/enet/host.h
+++ b/backends/networking/enet/host.h
@@ -43,33 +43,131 @@ namespace Networking {
 
 class Host {
 public:
+	/**
+	 * A representation of ENetHost.
+	 * @param host A pointer to ENetHost given by the createHost function.
+	 * @see Networking::ENet::createHost
+	*/
 	Host(ENetHost *host);
+	/**
+	 * A representation of ENetHost, connected to a server peer.
+	 * @param host A pointer to ENetHost given by the connnectToHost function.
+	 * @param serverPeer a pointer to a connected peer given by the connnectToHost function.
+	 * @see Networking::ENet::connectToHost
+	 */
 	Host(ENetHost *host, ENetPeer *serverPeer);
 	~Host();
 
+	/**
+	 * Services the host which receives or sends pending messages,
+	 * intended to be called at the start of a game loop.
+	 * @param timeout number of milliseconds that ENet should wait for events.
+	 * @retval > 0 if an event occurred within the specified time limit.
+	 * @retval 0 if no event occurred.
+	 * @retval < 0 on failure.
+	 */
 	uint8 service(int timeout = 0);
 
+	/**
+	 * Connected to a foreign peer.
+	 * @param address the address of the peer that will attempt to connect to.
+	 * @param port the port number of the peer that will attempt to connect to.
+	 * @param timeout specifies the connection timeout in milliseconds, 5 full seconds by default.  Will fail if the given time has passed.
+	 * @param numChannels the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT. This must match with the connecting peer.
+	 * @retval true on successful.
+	 * @retval false on failure.
+	 */
 	bool connectPeer(Common::String address, int port, int timeout = 5000, int numChannels = 1);
+	/**
+	 * Disconnects a specific peer from the host.
+	 * @param peerIndex Index of a peer to disconnect.
+	 * @note A peer index can be retrieved from the getPeerIndexFromHost function.
+	 */
 	void disconnectPeer(int peerIndex);
 
+	/**
+	 * Creates a packet and sends to a peer.
+	 * @param data Contents of the packet's data; the packet's data will remain uninitialized if data is NULL.
+	 * @param peerIndex Index of a peer to send the packet to.
+	 * @param channel channel on which to send; 0 by default.
+	 * @param reliable Indicates that the packet must be received by the target peer and resend attempts will be made until the packet is delivered.
+	 * @retval true on successful.
+	 * @retval false on failure.
+	 * @note Currently, if this object is created by the connectToHost function, data will always get sent to _serverPeer regardless of index given.
+	 */
 	bool send(const char *data, int peerIndex, int channel = 0, bool reliable = true);
+	/**
+	 * Sends raw data to an address outside the ENet protocol using its UDP socket directly.
+	 * @param address Address to send data to.
+	 * @param port Port number to send data to.
+	 * @param data The data which will be sent.
+	 * @retval true on successful.
+	 * @retval false on failure.
+	 * @note This sends data as a raw connection-less UDP socket, the same functionaility as a Networking::Socket object, but retains the address and port this host object is using.
+	 * @note Useful for hole-punching a peer, so it can connect to it.
+	 */
 	bool sendRawData(Common::String address, int port, const char *data);
 
+	/**
+	 * Gets the host name of a peer that have recently connected, disconnected or received packet from.
+	 * @return String containing the host name.
+	 * @note service() must be called and returned > 0 for this function to work.
+	 */
 	Common::String getHost();
+	/**
+	 * Gets the port number of a peer that have recently connected, disconnected or received packet from.
+	 * @return A port number.
+	 * @note service() must be called and returned > 0 for this function to work.
+	 */
 	int getPort();
 
+	/**
+	 * Gets an index from a connected peer.
+	 * @param host A peer's host name
+	 * @param port A peer's port number.
+	 * @retval >= 0 containing a peer index if successfully found.
+	 * @retval -1 if not found.
+	 */
 	int getPeerIndexFromHost(Common::String host, int port);
 
+	/**
+	 * Gets the data from the most-recently received packet.
+	 * @return String containing the packet's data.
+	 * @note service() must be called and returned ENET_EVENT_TYPE_RECEIVE (3) for this function to work.
+	 */
 	Common::String getPacketData();
+	/**
+	 * Deallocate the packet, must be called upon receiving and finished using the packet's data.
+	 */
 	void destroyPacket();
 private:
+
+	/**
+	 * Pointer to ENetHost this object represents.
+	 * @see Networking::ENet::createHost
+	 * @see Networking::ENet::connectToHost
+	 */
 	ENetHost *_host;
-	ENetPeer *_serverPeer; // Only used for clients.
+	/**
+	 * A representing server peer connected by the connectToHost function.
+	 * @see Networking::ENet::connectToHost
+	 */
+	ENetPeer *_serverPeer;
+	/**
+	 * String containing the recent host name connected, disconnected or received from.
+	 * @see getHost()
+	*/
 	Common::String _recentHost;
+	/**
+	 * String containing the recent host name connected, disconnected or received from.
+	 * @see getPort()
+	*/
 	int _recentPort;
+	/**
+	 * String containing the recent host name connected, disconnected or received from.
+	 * @see getPacketData()
+	*/
 	ENetPacket *_recentPacket;
-
-
 };
 
 } // End of namespace Networking
diff --git a/backends/networking/enet/socket.h b/backends/networking/enet/socket.h
index ed26fc260c6..8bb5299d3a9 100644
--- a/backends/networking/enet/socket.h
+++ b/backends/networking/enet/socket.h
@@ -39,20 +39,67 @@ namespace Networking {
 
 class Socket {
 public:
+	/**
+	 * A representation of a raw UDP socket.
+	 * @param socket Contains the socket itself.
+	 */
 	Socket(ENetSocket socket);
 	~Socket();
 
+	/**
+	 * Send data to the specified address and port.
+	 * @param address Address to send data to.
+	 * @param port Port number to send data to.
+	 * @param data The data which will be sent.
+	 * @retval true on successful.
+	 * @retval false on failure.
+	 */
 	bool send(Common::String address, int port, const char *data);
+	/**
+	 * Checks for received data.
+	 * @retval true if received data.
+	 * @retval false otherwise.
+	 */
 	bool receive();
 
+	/**
+	 * Get the data received from socket.
+	 * @return String containing received data.
+	 * @note receive() must be called and returned true to get actual data.
+	 */
 	Common::String getData();
 
+	/**
+	 * Get the host name of received data.
+	 * @return The host name
+	 * @note receive() must be called and returned true to get host name.
+	 */
 	Common::String getHost();
+	/**
+	 * Get the port number of received data.
+	 * @return The port number
+	 * @note receive() must be called and returned true to get port.
+	 */
 	int getPort();
 private:
+	/**
+	 * Representation of the UDP socket.
+	 */
 	ENetSocket _socket;
+	/**
+	 * String containing the recent data received, 
+	 * @see getData()
+	 */
 	Common::String _recentData;
+	/**
+	 * String containing the last received host.
+	 * @see getHost()
+	*/
 	Common::String _recentHost;
+	/**
+	 * The last last received port number
+	 * @see getPort()
+	*/
 	int _recentPort;
 };
 


Commit: f2dac5e9b346361fcd4f771851e74d4204c9e0e6
    https://github.com/scummvm/scummvm/commit/f2dac5e9b346361fcd4f771851e74d4204c9e0e6
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
BACKENDS: CURL: Document URL methods.

Changed paths:
    backends/networking/curl/url.h


diff --git a/backends/networking/curl/url.h b/backends/networking/curl/url.h
index d0608749e35..c93cf1f29db 100644
--- a/backends/networking/curl/url.h
+++ b/backends/networking/curl/url.h
@@ -32,12 +32,35 @@ public:
 	CurlURL();
 	~CurlURL();
 
+	/**
+	 * Parses an URL string by calling curl_url_set  Must be used before using other methods.
+	 * @param url is a string containing the URL. e.g. "https://scummvm.org".
+	 * @retval true if successful.
+	 * @retval false on failure or if using an older version of libcurl.
+	 */
 	bool parseURL(Common::String url);
 
+	/**
+	 * Extracts the scheme of an URL parsed previously by parseURL.
+	 * @retval String of the URL's scheme. e.g. "https".
+	 * @retval Empty string on failure.
+	 */
 	Common::String getScheme();
+	/**
+	 * Extracts the host name of an URL parsed previously by parseURL.
+	 * @retval String of the URL's host name. e.g. "scummvm.org".
+	 * @retval Empty string on failure.
+	 */
 	Common::String getHost();
+	/**
+	 * Extracts the port of an URL parsed previously by parseURL.
+	 * @param returnDefault tells libcurl to return the default port according to the URL's scheme if not explicitly defined
+	 * @retval The URL's port number if one exists.
+	 * @retval 0 if no port found. 
+	 * @retval default port if returnDefault is true.
+	 * @retval -1 on failure.
+	 */
 	int getPort(bool returnDefault = false);
-	Common::String getPath();
 private:
 	CURLU *_url;
 };


Commit: 27c66b6c340515e710ac03f7e8f423940634ad06
    https://github.com/scummvm/scummvm/commit/27c66b6c340515e710ac03f7e8f423940634ad06
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Remove commented out code.

Changed paths:
    engines/scumm/he/moonbase/moonbase.cpp


diff --git a/engines/scumm/he/moonbase/moonbase.cpp b/engines/scumm/he/moonbase/moonbase.cpp
index a512f9cdf06..ad63cc09cd5 100644
--- a/engines/scumm/he/moonbase/moonbase.cpp
+++ b/engines/scumm/he/moonbase/moonbase.cpp
@@ -24,9 +24,6 @@
 #include "scumm/he/intern_he.h"
 #include "scumm/he/moonbase/moonbase.h"
 #include "scumm/he/moonbase/ai_main.h"
-// #ifdef USE_LIBCURL
-// #include "scumm/he/moonbase/net_main.h"
-// #endif
 
 namespace Scumm {
 


Commit: 139a414d238ff1d60cedd3c59edbb9a49e5e7cad
    https://github.com/scummvm/scummvm/commit/139a414d238ff1d60cedd3c59edbb9a49e5e7cad
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Document main network code.

Changed paths:
    engines/scumm/he/net/net_main.cpp
    engines/scumm/he/net/net_main.h


diff --git a/engines/scumm/he/net/net_main.cpp b/engines/scumm/he/net/net_main.cpp
index 567ab8577b9..f2aede2add1 100644
--- a/engines/scumm/he/net/net_main.cpp
+++ b/engines/scumm/he/net/net_main.cpp
@@ -130,7 +130,6 @@ int Net::hostGame(char *sessionName, char *userName) {
 }
 
 int Net::joinGame(Common::String IP, char *userName) {
-	// This gets called when attempting to join with the --join-game command line param.
 	debug(1, "Net::joinGame(\"%s\", \"%s\")", IP.c_str(), userName); // PN_JoinTCPIPGame
 	Address address = getAddressFromString(IP);
 
@@ -227,7 +226,7 @@ int Net::addUser(char *shortName, char *longName) {
 
 	uint tickCount = 0;
 	while (_myUserId == -1) {
-		remoteReceiveData(12);
+		remoteReceiveData();
 		// Wait for five seconds for our user id before giving up
 		tickCount += 5;
 		g_system->delayMillis(5);
@@ -265,9 +264,9 @@ int Net::createSession(char *name) {
 
 	_sessionId = -1;
 	_sessionName = name;
-	// Normally we would do only one peer (0) or three peers (2) but we are reserving one
+	// Normally we would do only one peer or three peers but we are reserving one
 	// for our connection to the session server.
-	_sessionHost = _enet->createHost("0.0.0.0", 0, _maxPlayers);
+	_sessionHost = _enet->createHost("0.0.0.0", 0, _maxPlayers + 1);
 
 	if (!_sessionHost) {
 		return 0;
@@ -477,7 +476,7 @@ int Net::endSession() {
 
 	_isRelayingGame = false;
 
-	return 0;
+	return 1;
 }
 
 void Net::disableSessionJoining() {
@@ -502,16 +501,16 @@ void Net::setBotsCount(int botsCount) {
 }
 
 int32 Net::setProviderByName(int32 parameter1, int32 parameter2) {
-	// char name[MAX_PROVIDER_NAME];
-	// char ipaddress[MAX_IP_SIZE];
+	char name[MAX_PROVIDER_NAME];
+	char ipaddress[MAX_IP_SIZE];
 
-	// ipaddress[0] = '\0';
+	ipaddress[0] = '\0';
 
-	// _vm->getStringFromArray(parameter1, name, sizeof(name));
-	// if (parameter2)
-	// 	_vm->getStringFromArray(parameter2, ipaddress, sizeof(ipaddress));
+	_vm->getStringFromArray(parameter1, name, sizeof(name));
+	if (parameter2)
+		_vm->getStringFromArray(parameter2, ipaddress, sizeof(ipaddress));
 
-	// debug(1, "Net::setProviderByName(\"%s\", \"%s\")", name, ipaddress); // PN_SetProviderByName
+	debug(1, "Net::setProviderByName(\"%s\", \"%s\")", name, ipaddress); // PN_SetProviderByName
 
 	// Emulate that we found a TCP/IP provider
 
@@ -571,6 +570,11 @@ bool Net::destroyPlayer(int32 userId) {
 int32 Net::startQuerySessions(bool connectToSessionServer) {
 	debug(1, "Net::startQuerySessions()");
 
+	if (!_enet) {
+		warning("NETWORKING: ENet not initialized yet");
+		return 0;
+	}
+
 	bool enableSessionServer = true;
 	bool enableLanBroadcast = true;
 	if (ConfMan.hasKey("enable_session_server"))
@@ -595,7 +599,7 @@ int32 Net::startQuerySessions(bool connectToSessionServer) {
 	if (enableLanBroadcast && !_broadcastSocket) {
 		_broadcastSocket = _enet->createSocket("0.0.0.0", 0);
 	}
-	return 0;
+	return 1;
 }
 
 int32 Net::updateQuerySessions() {
@@ -1109,7 +1113,7 @@ void Net::handleBroadcastData(Common::String data, Common::String host, int port
 	}
 }
 
-void Net::remoteReceiveData(uint32 tickCount) {
+void Net::remoteReceiveData() {
 	uint8 messageType = _sessionHost->service();
 	switch (messageType) {
 	case ENET_EVENT_TYPE_NONE:
@@ -1238,7 +1242,7 @@ void Net::doNetworkOnceAFrame(int msecs) {
 	if (!_enet || !_sessionHost)
 		return;
 
-	remoteReceiveData(msecs);
+	remoteReceiveData();
 
 	if (_sessionServerHost)
 		serviceSessionServer();
diff --git a/engines/scumm/he/net/net_main.h b/engines/scumm/he/net/net_main.h
index dd6b6cdaa71..78163665fab 100644
--- a/engines/scumm/he/net/net_main.h
+++ b/engines/scumm/he/net/net_main.h
@@ -36,6 +36,9 @@ public:
 	~Net();
 
 private:
+	/**
+	 * An address structure which stores the host and port.
+	 */
 	struct Address {
 		Common::String host;
 		int port;
@@ -43,9 +46,11 @@ private:
 			return host == other.host && port == other.port;
 		};
 	};
-
+	/**
+	 * Structure for session storage.
+	 */
 	struct Session {
-		bool local = false;
+		bool local = false; ///< Indicates if session is found over LAN.
 		int id = -1;
 		Common::String host;
 		int port;
@@ -53,67 +58,448 @@ private:
 		int players;
 		uint32 timestamp;
 	};
-
+	/**
+	 * Converts a formatted string into an Address object.
+	 * 
+	 * @param address A proper formatted string e.g. "127.0.0.1:9120", it may or may not contain a port number.
+	 * @return Address
+	 */
 	Address getAddressFromString(Common::String address);
+	/**
+	 * Converts an Address object into a formatted string.
+	 * 
+	 * @param address An address object.
+	 * @return Common::String
+	 */
 	Common::String getStringFromAddress(Address address);
 
 public:
+	/**
+	 * Creates a session and add ourselves as a user.
+	 * 
+	 * @param sessionName Name if a session.
+	 * @param userName User name to add.
+	 * @retval 1 on success.
+	 * @retval 0 on falure.
+	 */
 	int hostGame(char *sessionName, char *userName);
+	/**
+	 * Joins a session with the speicified IP address and user name.
+	 * This gets called when attempting to join a Moonbase Commander
+	 * game with the --join-game command line param.
+	 * 
+	 * @param IP IP Address to join
+	 * @param userName Username to use.
+	 * @retval 1 on success.
+	 * @retval 0 on failure.
+	 */
 	int joinGame(Common::String IP, char *userName);
+
+	/**
+	 * Adds a user to the session and assigns a user id.
+	 * 
+	 * @param shortName Short username
+	 * @param longName Long username.
+	 * @retval 1 on success.
+	 * @retval 0 on failure.
+	 */
 	int addUser(char *shortName, char *longName);
+	/**
+	 * Remove ourselves if we have a user id for this session.
+	 * 
+	 * @retval Always returns 1.
+	 * @see destroyPlayer
+	 * 
+	 */
 	int removeUser();
+
+	/**
+	 * Gets the user id who sent the previously received packet.
+	 * 
+	 * @retval Last sent user id.
+	 */
 	int whoSentThis();
+	/**
+	 * Gets our assigned user id.
+	 * 
+	 * @retval Our user id.
+	 */
 	int whoAmI();
+
+	/**
+	 * @brief Creates and host an network game session.
+	 * 
+	 * @param name Session name
+	 * @retval 1 on success.
+	 * @retval 0 on failure.
+	 */
 	int createSession(char *name);
+	/**
+	 * Join a session by their given index.
+	 * 
+	 * @param sessionIndex Index of a session to join.
+	 * @retval 1 on success.
+	 * @retval 0 on falure.
+	 * 
+	 * @note Use the QuerySessions methods to get sessions.
+	 */
 	int joinSession(int sessionIndex);
+	/**
+	 * Join a session by their given id instead of index.
+	 * 
+	 * @param sessionId ID of session to join
+	 * @retval 1 on success.
+	 * @retval 0 on falure.
+	 * 
+	 * @note Use the QuerySessions methods to get sessions.
+	 */
 	int joinSessionById(int sessionId);
+	/**
+	 * Checks if the queried session id exist.
+	 * 
+	 * @param sessionId Session ID to search for.
+	 * @retval 1 if found.
+	 * @retval 0 if not found.
+	 */
 	int ifSessionExist(int sessionId);
+
+	/**
+	 * Ends and closes an active game session.
+	 * 
+	 * @retval Always returns 1
+	 */
 	int endSession();
+
+	/**
+	 * Force a session server address to connect to when creating and joining sessions,
+	 * overriding an existing configuration if any.
+	 * 
+	 * @param sessionServer Address to a session server address.
+	 * @note This will disable LAN hosting.
+	 */
 	void setSessionServer(Common::String sessionServer);
+	/**
+	 * Disallows anymore players from joining our session.
+	 */
 	void disableSessionJoining();
+	/**
+	 * Allows more players to join our session.
+	 * @note Currently stubbed.
+	 * 
+	 */
 	void enableSessionJoining();
+
+	/**
+	 * @brief Set AI Player count.
+	 * 
+	 * @param botsCount Number of AI players currently created.
+	 */
 	void setBotsCount(int botsCount);
+
+	/**
+	 * @brief Set and initializes the provider given by their name. 
+	 * 
+	 * @param parameter1 SCUMM string array providing the name of the provider.
+	 * @param parameter2 SCUMM string array providing the optional paramater
+	 * 
+	 * @retval 1 if successful
+	 * @retval 0 on failure.
+	 * 
+	 * @note Currently this will only initialize the ENet provider, regardless of name given.
+	 */
 	int32 setProviderByName(int32 parameter1, int32 parameter2);
+
+	/**
+	 * @brief Sets the fake latency.
+	 * 
+	 * @param time Fake latency time in milliseconds.
+	 */
 	void setFakeLatency(int time);
+
+	/**
+	 * Destroys and remove an existing player.
+	 * 
+	 * @param userId ID of player to remove.
+	 * @retval true on removed 
+	 * @retval false on failure. 
+	 */
 	bool destroyPlayer(int32 userId);
+	/**
+	 * Setup and begin to query for active game sessions
+	 * 
+	 * @param connectToSessionServer Indicates that it should connect to the session server for Internet-wide connections
+	 * @retval 1 if ready
+	 * @retval 0 if not.
+	 * @see updateQuerySessions()
+	 * @see stopQuerySessions()
+	 */
 	int32 startQuerySessions(bool connectToSessionServer = true);
+	/**
+	 * Make a session query request and updates the session list.
+	 * 
+	 * @return Number of sessions found.
+	 */
 	int32 updateQuerySessions();
+	/**
+	 * Stops and shuts down querying for sessions.
+	 * @note This will clear the sessions list.
+	 */
 	void stopQuerySessions();
+	/**
+	 * Shortcut for querying sessions, calls startQuerySessions and returns updateQuerySessions.
+	 * 
+	 * @return Number of sessions found.
+	 */
 	int querySessions();
+
+	/**
+	 * Query aviliable providers.
+	 * 
+	 * @return Always 1 currently.
+	 */
 	int queryProviders();
+	/**
+	 * @brief Set the provider by the index.
+	 * 
+	 * @param providerIndex index of a provider
+	 * @retval 1 if successful
+	 * @retval 0 on failure
+	 * 
+	 * @note Currently stubbed.
+	 */
 	int setProvider(int providerIndex);
+	/**
+	 * Close and shutsdown an active provider.
+	 * 
+	 * @return Always returns 1.
+	 */
 	int closeProvider();
+
+	/**
+	 * Initializes the provider, session, and adds user all at once.
+	 * 
+	 * @retval true on success.
+	 * @retval false on failure. 
+	 * 
+	 * @note Currently stubbed.
+	 */
 	bool initAll();
+	/**
+	 * Initializes the provider.
+	 * 
+	 * @return true on success.
+	 * @return false on failure.
+	 * 
+	 * @note Currently stubbed.
+	 */
 	bool initProvider();
+	/**
+	 * Initializes the session.
+	 * 
+	 * @return true on success.
+	 * @return false on failure.
+	 * 
+	 * @note Currently stubbed.
+	 */
 	bool initSession();
+	/**
+	 * Initializes the user.
+	 * 
+	 * @return true on success.
+	 * @return false on failure.
+	 * 
+	 * @note Currently stubbed.
+	 */
 	bool initUser();
+
+/**
+	 * Sends a packet to a remote peer(s) which will call VAR_REMOTE_START_SCRIPT.
+	 * 
+	 * @param typeOfSend A type of send this packet goes to, can be an indiviual peer, a group, host, or everybody.
+	 * @param sendTypeParam A parameter for this type of send, e.g. for an indiviual send, this can be a user id of a peer to send to.
+	 * @param priority Tells the provider to ensure that this packet has been sent and received.
+	 * @param argsCount Number of args it should contain for the VAR_REMOTE_START_SCRIPT call.
+	 * @param args The arguments themselves.
+	 */
 	void remoteStartScript(int typeOfSend, int sendTypeParam, int priority, int argsCount, int32 *args);
-	int remoteSendData(int typeOfSend, int sendTypeParam, int type, Common::String data, int priority, int defaultRes = 0, bool wait = false, int callid = 0);
+	/**
+	 * Sends an SCUMM array to a remote peer(s), calling VAR_NETWORK_RECEIVE_ARRAY_SCRIPT there.
+	 * 
+	 * @param typeOfSend A type of send this packet goes to, can be an indiviual peer, a group, host, or everybody.
+	 * @param sendTypeParam A parameter for this type of send, e.g. for an indiviual send, this can be a user id of a peer to send to.
+	 * @param priority Tells the provider to ensure that this packet has been sent and received.
+	 * @param arrayIndex An index pointing to an SCUMM array to pack and send with.
+	 */
 	void remoteSendArray(int typeOfSend, int sendTypeParam, int priority, int arrayIndex);
+
+	/**
+	 * Sends a packet to a peer calling VAR_NETWORK_RECEIVE_ARRAY_SCRIPT, and it'll return its return value back to us.
+	 * 
+	 * @param typeOfSend A type of send this packet goes to, can be an indiviual peer, a group, host, or everybody.
+	 * @param sendTypeParam A parameter for this type of send, e.g. for an indiviual send, this can be a user id of a peer to send to.
+	 * @param priority Tells the provider to ensure that this packet has been sent and received.
+	 * @param defaultReturnValue The default return value to return in case of a time out.
+	 * @param argsCount Number of args it should contain for the VAR_REMOTE_START_SCRIPT call.
+	 * @param args The arguments themselves.
+	 * @retval A peer's return value.
+	 * @retval defaultReturnValue if timed out.
+	 * 
+	 * @note This has been stubbed, as no game seems to use this.
+	 */
 	int remoteStartScriptFunction(int typeOfSend, int sendTypeParam, int priority, int defaultReturnValue, int argsCount, int32 *args);
+
+	/**
+	 * A method that should be called once at the beginning of a game loop to send/receive network data. 
+	 * 
+	 * @param msecs milliseconds to service networks for.
+	 */
 	void doNetworkOnceAFrame(int msecs);
-	void handleGameData(Common::JSONValue *json, int peerIndex);
-	void handleGameDataHost(Common::JSONValue *json, int peerIndex);
 
 private:
+	/**
+	 * Attempt to connect to a game session with its address and port.
+	 * 
+	 * @param address Address of an session to connect to.
+	 * @param port Port number of an session to connect to.
+	 * @retval true on success 
+	 * @retval false on failure.
+	 * 
+	 * @see joinGame
+	 * @see doJoinSession
+	 */
 	bool connectToSession(Common::String address, int port);
+
+	/**
+	 * Method that actually attemps to join a session.
+	 * 
+	 * @param session Session structure to join to.
+	 * @retval 1 on success
+	 * @retval 0 on failure.
+	 * 
+	 * @see joinSession
+	 * @see joinSessionById
+	 */
 	int doJoinSession(Session session);
+
+	/**
+	 * Sends remote data to peer(s).
+	 * 
+	 * @param typeOfSend A type of send this packet goes to, can be an indiviual peer, a group, host, or everybody.
+	 * @param sendTypeParam A parameter for this type of send, e.g. for an indiviual send, this can be a user id of a peer to send to.
+	 * @param type Type of packet.
+	 * @param data Data of contain in the packet.
+	 * @param priority Tells the provider to ensure that this packet has been sent and received. 
+	 * @param defaultRes Default return value (0 by default)
+	 * @param wait Wait for return value (Not currently being used).
+	 * @param callid Call ID of this packet send. (Not currently being used).
+	 * @return Always the default return value currently.
+	 */
+	int remoteSendData(int typeOfSend, int sendTypeParam, int type, Common::String data, int priority, int defaultRes = 0, bool wait = false, int callid = 0);
+
+	/**
+	 * Services the broadcast (LAN) socket.
+	 * 
+	 * @return true 
+	 * @return false 
+	 */
 	bool serviceBroadcast();
+	/**
+	 * Handles the data received from the broadcast (LAN) socket.
+	 * 
+	 * @param data Data received.
+	 * @param host Host name who sent the data.
+	 * @param port Port number who sent the data.
+	 */
 	void handleBroadcastData(Common::String data, Common::String host, int port);
+	/**
+	 * Servies the connection to the session server.
+	 */
 	void serviceSessionServer();
+	/**
+	 * Handles data received from the session server.
+	 * 
+	 * @param data Data received.
+	 */
 	void handleSessionServerData(Common::String data);
-	void remoteReceiveData(uint32 tickCount);
+	/**
+	 * Service sesssion host.
+	 */
+	void remoteReceiveData();
+	/**
+	 * Handle game data received from session host.
+	 * 
+	 * @param json JSON object containing game data.
+	 * @param peerIndex Index of a peer who sent this data.
+	 */
+	void handleGameData(Common::JSONValue *json, int peerIndex);
+	/**
+	 * Handle game data received and transfer to peers if needed.
+	 * 
+	 * @param json JSON object containing game data.
+	 * @param peerIndex Index of a peer who sent this data.
+	 * 
+	 * @see handleGameData
+	 */
+	void handleGameDataHost(Common::JSONValue *json, int peerIndex);
 
 public:
 	// getters
+
+	/**
+	 * Gets the host name of the local machine.
+	 * 
+	 * @param hostname Pointer to store the host name to.
+	 * @param length The length of the pointer.
+	 * @retval true on successful .
+	 * @retval false on failure.
+	 * 
+	 * @note This is currently subbed.
+	 */
 	bool getHostName(char *hostname, int length);
+	/**
+	 * Gets the IP address of the given local host name.
+	 * 
+	 * @param ip Pointer to store the address to.
+	 * @param ipLength The length of the pointer.
+	 * @param nameBuffer the Host name itself.
+	 * @retval true on successful .
+	 * @retval false on failure..
+	 * 
+	 * @note This is currently subbed.
+	 */
 	bool getIPfromName(char *ip, int ipLength, char *nameBuffer);
+	/**
+	 * Get the session name from the session index.
+	 * 
+	 * @param sessionNumber The session's index.
+	 * @param buffer A buffer to store the name to.
+	 * @param length Maxinum length of the buffer.
+	 */
 	void getSessionName(int sessionNumber, char *buffer, int length);
+	/**
+	 * Get the session's player count.
+	 * 
+	 * @param sessionNumber The session's index.
+	 * @return Player count.
+	 */
 	int getSessionPlayerCount(int sessionNumber);
+	/**
+	 * @brief Get the name of a provider of the given index.
+	 * 
+	 * @param providerIndex 
+	 * @param buffer 
+	 * @param length 
+	 */
 	void getProviderName(int providerIndex, char *buffer, int length);
 
 private:
 	// mostly getters
+
+	/**
+	 * @brief Get total players joined in the session, including AI players.
+	 * 
+	 * @return Player count.
+	 */
 	int getTotalPlayers();
 
 public:
@@ -167,7 +553,7 @@ private:
 	bool _forcedAddress;
 	bool _gotSessions;
 	int _sessionServerPeer;
-	bool _isRelayingGame; // If we're relaying in-game data over the session server or not.
+	bool _isRelayingGame; ///< If we're relaying in-game data over the session server or not.
 };
 
 } // End of namespace Scumm


Commit: af06a58c1881f93d954e383e7387b17264f1f263
    https://github.com/scummvm/scummvm/commit/af06a58c1881f93d954e383e7387b17264f1f263
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Fix compile error.

Changed paths:
    engines/scumm/dialogs.cpp


diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 5c52b5b2f76..b7be2014763 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/config-manager.h"
+#include "common/gui_options.h"
 #include "common/savefile.h"
 #include "common/system.h"
 #include "common/events.h"


Commit: 6642a24ea49ca2f080727c0ccab7ec2116394bea
    https://github.com/scummvm/scummvm/commit/6642a24ea49ca2f080727c0ccab7ec2116394bea
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Re-add code lost during rebasing.

Changed paths:
    engines/scumm/he/script_v80he.cpp


diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp
index 3dfd037c4b2..cde84776a9b 100644
--- a/engines/scumm/he/script_v80he.cpp
+++ b/engines/scumm/he/script_v80he.cpp
@@ -195,6 +195,8 @@ void ScummEngine_v80he::o80_readConfigFile() {
 		break;
 	case ScummEngine_v100he::SO_STRING: // HE 100
 	case SO_STRING: // string
+		if (!strcmp((char *)option, "joinip") && ConfMan.get("join_game") != "null")
+			entry = ConfMan.get("join_game");
 		writeVar(0, 0);
 		len = resStrLen((const byte *)entry.c_str());
 		data = defineArray(0, kStringArray, 0, 0, 0, len);


Commit: 82bc36db8de4b73a1cb9a1549e45d07eead6a66d
    https://github.com/scummvm/scummvm/commit/82bc36db8de4b73a1cb9a1549e45d07eead6a66d
Author: Little Cat (toontownlittlecat at gmail.com)
Date: 2023-03-06T00:07:13+01:00

Commit Message:
SCUMM HE: Label SO_COMPLEX_ARRAY_MATH_OPERATION.

Changed paths:
    engines/scumm/he/intern_he.h
    engines/scumm/he/script_v72he.cpp


diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index 837514f43bb..8a92860d2c5 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -355,6 +355,7 @@ protected:
 		SO_COMPLEX_ARRAY_ASSIGNMENT = 126,
 		SO_COMPLEX_ARRAY_COPY_OPERATION = 127,
 		SO_RANGE_ARRAY_ASSIGNMENT = 128,
+		SO_COMPLEX_ARRAY_MATH_OPERATION = 138,
 		SO_FORMATTED_STRING = 194,
 		SO_UNDIM_ARRAY = 204,
 	};
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index 61084b09d16..c2b7aff2301 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -1190,9 +1190,8 @@ void ScummEngine_v72he::o72_arrayOps() {
 			dim2start++;
 		}
 		break;
-	case 138:		// SO_COMPLEX_ARRAY_MATH_OPERATION
+	case SO_COMPLEX_ARRAY_MATH_OPERATION:
 			{
-				// Borrowed code from script_v100he.cpp
 				// Used by script 84 (Send end of play info) in Backyard Football during online play.
 				int array2 = fetchScriptWord();
 				int array1 = fetchScriptWord();




More information about the Scummvm-git-logs mailing list