diff --git a/src/Makefile.cfg b/src/Makefile.cfg
index 4a2c0687be1a4ce7a448ffe47043c0cc25f3e2c9..5c56978e717da251d54494ab68399213b2f92e82 100644
--- a/src/Makefile.cfg
+++ b/src/Makefile.cfg
@@ -12,14 +12,15 @@
 # to avoid a false positive with the version detection...
 
 SUPPORTED_GCC_VERSIONS:=\
-	91\
-	81 82 83\
-	71 72\
+	101 102\
+	91 92 93\
+	81 82 83 84\
+	71 72 73 74 75\
 	61 62 63 64\
-	51 52 53 54\
+	51 52 53 54 55\
 	40 41 42 43 44 45 46 47 48 49
 
-LATEST_GCC_VERSION=9.1
+LATEST_GCC_VERSION=10.2
 
 # gcc or g++
 ifdef PREFIX
@@ -68,7 +69,27 @@ ifeq   (,$(filter GCC%,$(.VARIABLES)))
  endif
 endif
 
+ifdef GCC102
+GCC101=1
+endif
+
+ifdef GCC101
+GCC93=1
+endif
+
+ifdef GCC93
+GCC92=1
+endif
+
+ifdef GCC92
+GCC91=1
+endif
+
 ifdef GCC91
+GCC84=1
+endif
+
+ifdef GCC84
 GCC83=1
 endif
 
@@ -81,6 +102,18 @@ GCC81=1
 endif
 
 ifdef GCC81
+GCC75=1
+endif
+
+ifdef GCC75
+GCC74=1
+endif
+
+ifdef GCC74
+GCC73=1
+endif
+
+ifdef GCC73
 GCC72=1
 endif
 
@@ -105,6 +138,10 @@ GCC61=1
 endif
 
 ifdef GCC61
+GCC55=1
+endif
+
+ifdef GCC55
 GCC54=1
 endif
 
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 71e2e947e63078719fa14f5515bc72d45541935d..d3cc0b0e278f45fc1f55b8c0a1c74e959838bd19 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3615,6 +3615,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
 		CL_RemovePlayer(pnum, kickreason);
 }
 
+static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
+consvar_t cv_netticbuffer = {"netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
 consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL	};
 consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
 static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
@@ -5380,6 +5383,10 @@ void TryRunTics(tic_t realtics)
 				ExtraDataTicker();
 				gametic++;
 				consistancy[gametic%BACKUPTICS] = Consistancy();
+
+				// Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
+				if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
+					break;
 			}
 	}
 }
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index cb23883a8de60517b26bc46c23df00230e270795..d537984df8a227b53a5b2dc43cc8c4d3669cbba1 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -540,7 +540,7 @@ extern UINT32 realpingtable[MAXPLAYERS];
 extern UINT32 playerpingtable[MAXPLAYERS];
 extern tic_t servermaxping;
 
-extern consvar_t cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout;
+extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout;
 extern consvar_t cv_resynchattempts, cv_blamecfail;
 extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
 
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 592734067ffc629b4525251f6d8f207fa40faa60..6aa168aa7680fbe804e382f71ac4142f017e9964 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -701,6 +701,7 @@ void D_RegisterClientCommands(void)
 #endif
 	CV_RegisterVar(&cv_rollingdemos);
 	CV_RegisterVar(&cv_netstat);
+	CV_RegisterVar(&cv_netticbuffer);
 
 #ifdef NETGAME_DEVMODE
 	CV_RegisterVar(&cv_fishcake);
diff --git a/src/dehacked.c b/src/dehacked.c
index 7107c3a1e4e797540aca6a44ac4429828744c387..a856ab3524a7e68c5307b6fa764f3d2b3ae68193 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -808,9 +808,11 @@ static void readskincolor(MYFILE *f, INT32 num)
 			{
 				size_t namesize = sizeof(skincolors[num].name);
 				char truncword[namesize];
+				UINT16 dupecheck;
 
 				deh_strlcpy(truncword, word2, namesize, va("Skincolor %d: name", num)); // truncate here to check for dupes
-				if (truncword[0] != '\0' && (!stricmp(truncword, skincolors[SKINCOLOR_NONE].name) || R_GetColorByName(truncword)))
+				dupecheck = R_GetColorByName(truncword);
+				if (truncword[0] != '\0' && (!stricmp(truncword, skincolors[SKINCOLOR_NONE].name) || (dupecheck && dupecheck != num)))
 				{
 					size_t lastchar = strlen(truncword);
 					char oldword[lastchar+1];
@@ -4726,11 +4728,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
 				{
 					if (i == 0 && word2[0] != '0') // If word2 isn't a number
 						i = get_skincolor(word2); // find a skincolor by name
-					if (i < numskincolors && i >= (INT32)SKINCOLOR_FIRSTFREESLOT)
+					if (i && i < numskincolors)
 						readskincolor(f, i);
 					else
 					{
-						deh_warning("Skincolor %d out of range (%d - %d)", i, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
+						deh_warning("Skincolor %d out of range (1 - %d)", i, numskincolors-1);
 						ignorelines(f);
 					}
 				}
diff --git a/src/g_demo.c b/src/g_demo.c
index 6c58f12fb4b7137c1c8be099cde85d3253093aaa..57a955cb130a3dfa886ab73ad408bfd0b5fce68e 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -2382,9 +2382,12 @@ static void WriteDemoChecksum(void)
 static void G_StopDemoRecording(void)
 {
 	boolean saved = false;
-	WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
-	WriteDemoChecksum();
-	saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
+	if (demo_p)
+	{
+		WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
+		WriteDemoChecksum();
+		saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
+	}
 	free(demobuffer);
 	demorecording = false;
 
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 0d7404c7740760c81793912f58d5b2781c2e01bf..ab864f3831ec76c94b27748f068ef7902455cbab 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -214,6 +214,9 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col
 		poly_color.s.blue = (UINT8)blue;
 	}
 
+	// Clamp the light level, since it can sometimes go out of the 0-255 range from animations
+	light_level = min(max(light_level, 0), 255);
+
 	Surface->PolyColor.rgba = poly_color.rgba;
 	Surface->TintColor.rgba = tint_color.rgba;
 	Surface->FadeColor.rgba = fade_color.rgba;
@@ -2757,8 +2760,11 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling,
 		HWR_SetCurrentTexture(NULL);
 
 	// reference point for flat texture coord for each vertex around the polygon
-	flatxref = (float)((polysector->origVerts[0].x & (~flatflag)) / fflatwidth);
-	flatyref = (float)((polysector->origVerts[0].y & (~flatflag)) / fflatheight);
+	flatxref = FIXED_TO_FLOAT(polysector->origVerts[0].x);
+	flatyref = FIXED_TO_FLOAT(polysector->origVerts[0].y);
+
+	flatxref = (float)(((fixed_t)flatxref & (~flatflag)) / fflatwidth);
+	flatyref = (float)(((fixed_t)flatyref & (~flatflag)) / fflatheight);
 
 	// transform
 	v3d = planeVerts;
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 08d688e1dd9036524f48c10da59b13c412977529..2603abd46dd2bc6e538765b85b4ffe8aa5681fa0 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1535,7 +1535,7 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags)
 					// Sryder: Fog
 					// multiplies input colour by input alpha, and destination colour by input colour, then adds them
 					pglBlendFunc(GL_SRC_ALPHA, GL_SRC_COLOR);
-					pglAlphaFunc(GL_NOTEQUAL, 0.0f);
+					pglAlphaFunc(GL_ALWAYS, 0.0f); // Don't discard zero alpha fragments
 					break;
 				default : // must be 0, otherwise it's an error
 					// No blending
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 0e60cbb902102e5854ff32f1ea25ca1f44b83584..94bf14b71d46fc0347cafdc7a6f9019ed76d0ac5 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1116,6 +1116,16 @@ static int lib_pPlayerCanDamage(lua_State *L)
 	return 1;
 }
 
+static int lib_pPlayerFullbright(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	INLEVEL
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	lua_pushboolean(L, P_PlayerFullbright(player));
+	return 1;
+}
+
 
 static int lib_pIsObjectInGoop(lua_State *L)
 {
@@ -3119,6 +3129,117 @@ static int lib_gBuildMapTitle(lua_State *L)
 	return 1;
 }
 
+static void
+Lpushdim (lua_State *L, int c, struct searchdim *v)
+{
+	int i;
+	lua_createtable(L, c, 0);/* I guess narr is numeric indices??? */
+	for (i = 0; i < c; ++i)
+	{
+		lua_createtable(L, 0, 2);/* and hashed indices (field)... */
+			lua_pushnumber(L, v[i].pos);
+			lua_setfield(L, -2, "pos");
+
+			lua_pushnumber(L, v[i].siz);
+			lua_setfield(L, -2, "siz");
+		lua_rawseti(L, -2, 1 + i);
+	}
+}
+
+/*
+I decided to make this return a table because userdata
+is scary and tables let the user set their own fields.
+*/
+/*
+Returns:
+
+[1] => map number
+[2] => map title
+[3] => search frequency table
+
+The frequency table is unsorted. It has the following format:
+
+{
+	['mapnum'],
+
+	['matchd'] => matches in map title string
+	['keywhd'] => matches in map keywords
+
+	The above two tables have the following format:
+
+	{
+		['pos'] => offset from start of string
+		['siz'] => length of match
+	}...
+
+	['total'] => the total matches
+}...
+*/
+static int lib_gFindMap(lua_State *L)
+{
+	const char *query = luaL_checkstring(L, 1);
+
+	INT32 map;
+	char *realname;
+	INT32 frc;
+	mapsearchfreq_t *frv;
+
+	INT32 i;
+
+	map = G_FindMap(query, &realname, &frv, &frc);
+
+	lua_settop(L, 0);
+
+	lua_pushnumber(L, map);
+	lua_pushstring(L, realname);
+
+	lua_createtable(L, frc, 0);
+	for (i = 0; i < frc; ++i)
+	{
+		lua_createtable(L, 0, 4);
+			lua_pushnumber(L, frv[i].mapnum);
+			lua_setfield(L, -2, "mapnum");
+
+			Lpushdim(L, frv[i].matchc, frv[i].matchd);
+			lua_setfield(L, -2, "matchd");
+
+			Lpushdim(L, frv[i].keywhc, frv[i].keywhd);
+			lua_setfield(L, -2, "keywhd");
+
+			lua_pushnumber(L, frv[i].total);
+			lua_setfield(L, -2, "total");
+		lua_rawseti(L, -2, 1 + i);
+	}
+
+	G_FreeMapSearch(frv, frc);
+	Z_Free(realname);
+
+	return 3;
+}
+
+/*
+Returns:
+
+[1] => map number
+[2] => map title
+*/
+static int lib_gFindMapByNameOrCode(lua_State *L)
+{
+	const char *query = luaL_checkstring(L, 1);
+	INT32 map;
+	char *realname;
+	map = G_FindMapByNameOrCode(query, &realname);
+	lua_pushnumber(L, map);
+	if (map)
+	{
+		lua_pushstring(L, realname);
+		Z_Free(realname);
+		return 2;
+	}
+	else
+		return 1;
+}
+
 static int lib_gDoReborn(lua_State *L)
 {
 	INT32 playernum = luaL_checkinteger(L, 1);
@@ -3389,6 +3510,7 @@ static luaL_Reg lib[] = {
 	{"P_DoPlayerPain",lib_pDoPlayerPain},
 	{"P_ResetPlayer",lib_pResetPlayer},
 	{"P_PlayerCanDamage",lib_pPlayerCanDamage},
+	{"P_PlayerFullbright",lib_pPlayerFullbright},
 	{"P_IsObjectInGoop",lib_pIsObjectInGoop},
 	{"P_IsObjectOnGround",lib_pIsObjectOnGround},
 	{"P_InSpaceSector",lib_pInSpaceSector},
@@ -3531,6 +3653,8 @@ static luaL_Reg lib[] = {
 	{"G_AddGametype", lib_gAddGametype},
 	{"G_BuildMapName",lib_gBuildMapName},
 	{"G_BuildMapTitle",lib_gBuildMapTitle},
+	{"G_FindMap",lib_gFindMap},
+	{"G_FindMapByNameOrCode",lib_gFindMapByNameOrCode},
 	{"G_DoReborn",lib_gDoReborn},
 	{"G_SetCustomExitVars",lib_gSetCustomExitVars},
 	{"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished},
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 0bd388b5b0e7216efe3242a9345f166fcde78e7f..6cfd91f97fefd344211728fea4754f5ca289e982 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1514,7 +1514,7 @@ static void setRamp(lua_State *L, skincolor_t* c) {
 	UINT32 i;
 	lua_pushnil(L);
 	for (i=0; i<COLORRAMPSIZE; i++) {
-		if (lua_objlen(L,-2)<COLORRAMPSIZE) {
+		if (lua_objlen(L,-2)!=COLORRAMPSIZE) {
 			luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' must be %d entries long; got %d.", COLORRAMPSIZE, lua_objlen(L,-2));
 			break;
 		}
@@ -1536,8 +1536,8 @@ static int lib_setSkinColor(lua_State *L)
 	lua_remove(L, 1); // don't care about skincolors[] userdata.
 	{
 		cnum = (UINT16)luaL_checkinteger(L, 1);
-		if (cnum < SKINCOLOR_FIRSTFREESLOT || cnum >= numskincolors)
-			return luaL_error(L, "skincolors[] index %d out of range (%d - %d)", cnum, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
+		if (!cnum || cnum >= numskincolors)
+			return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
 		info = &skincolors[cnum]; // get the skincolor to assign to.
 	}
 	luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
@@ -1588,14 +1588,19 @@ static int lib_setSkinColor(lua_State *L)
 		} else if (i == 3 || (str && fastcmp(str,"invcolor"))) {
 			UINT16 v = (UINT16)luaL_checkinteger(L, 3);
 			if (v >= numskincolors)
-				return luaL_error(L, "attempt to set skincolors[%d].invcolor to out of range value %d.", cnum, v);
+				return luaL_error(L, "skincolor_t field 'invcolor' out of range (1 - %d)", numskincolors-1);
 			info->invcolor = v;
 		} else if (i == 4 || (str && fastcmp(str,"invshade")))
 			info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
 		else if (i == 5 || (str && fastcmp(str,"chatcolor")))
 			info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
-		else if (i == 6 || (str && fastcmp(str,"accessible")))
-			info->accessible = lua_toboolean(L, 3);
+		else if (i == 6 || (str && fastcmp(str,"accessible"))) {
+			boolean v = lua_toboolean(L, 3);
+			if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
+				return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
+			else
+				info->accessible = v;
+		}
 		lua_pop(L, 1);
 	}
 	return 0;
@@ -1640,12 +1645,13 @@ static int skincolor_set(lua_State *L)
 	UINT32 i;
 	skincolor_t *info = *((skincolor_t **)luaL_checkudata(L, 1, META_SKINCOLOR));
 	const char *field = luaL_checkstring(L, 2);
+	UINT16 cnum = (UINT16)(info-skincolors);
 
 	I_Assert(info != NULL);
 	I_Assert(info >= skincolors);
 
-	if (info-skincolors < SKINCOLOR_FIRSTFREESLOT || info-skincolors >= numskincolors)
-		return luaL_error(L, "skincolors[] index %d out of range (%d - %d)", info-skincolors, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
+	if (!cnum || cnum >= numskincolors)
+		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
 
 	if (fastcmp(field,"name")) {
 		const char* n = luaL_checkstring(L, 3);
@@ -1658,7 +1664,7 @@ static int skincolor_set(lua_State *L)
 		if (info->name[0] != '\0') // don't check empty string for dupe
 		{
 			UINT16 dupecheck = R_GetColorByName(info->name);
-			if (!stricmp(info->name, skincolors[SKINCOLOR_NONE].name) || (dupecheck && (dupecheck != info-skincolors)))
+			if (!stricmp(info->name, skincolors[SKINCOLOR_NONE].name) || (dupecheck && (dupecheck != cnum)))
 				CONS_Alert(CONS_WARNING, "skincolor_t field 'name' ('%s') is a duplicate of another skincolor's name.\n", info->name);
 		}
 	} else if (fastcmp(field,"ramp")) {
@@ -1669,19 +1675,23 @@ static int skincolor_set(lua_State *L)
 		else
 			for (i=0; i<COLORRAMPSIZE; i++)
 				info->ramp[i] = (*((UINT8 **)luaL_checkudata(L, 3, META_COLORRAMP)))[i];
-		skincolor_modified[info-skincolors] = true;
+		skincolor_modified[cnum] = true;
 	} else if (fastcmp(field,"invcolor")) {
 		UINT16 v = (UINT16)luaL_checkinteger(L, 3);
 		if (v >= numskincolors)
-			return luaL_error(L, "attempt to set skincolor_t field 'invcolor' to out of range value %d.", v);
+			return luaL_error(L, "skincolor_t field 'invcolor' out of range (1 - %d)", numskincolors-1);
 		info->invcolor = v;
 	} else if (fastcmp(field,"invshade"))
 		info->invshade = (UINT8)luaL_checkinteger(L, 3)%COLORRAMPSIZE;
 	else if (fastcmp(field,"chatcolor"))
 		info->chatcolor = (UINT16)luaL_checkinteger(L, 3);
-	else if (fastcmp(field,"accessible"))
-		info->accessible = lua_toboolean(L, 3);
-	else
+	else if (fastcmp(field,"accessible")) {
+		boolean v = lua_toboolean(L, 3);
+		if (cnum < FIRSTSUPERCOLOR && v != skincolors[cnum].accessible)
+			return luaL_error(L, "skincolors[] index %d is a standard color; accessibility changes are prohibited.", cnum);
+		else
+			info->accessible = v;
+	} else
 		CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field);
 	return 1;
 }
@@ -1713,11 +1723,11 @@ static int colorramp_get(lua_State *L)
 static int colorramp_set(lua_State *L)
 {
 	UINT8 *colorramp = *((UINT8 **)luaL_checkudata(L, 1, META_COLORRAMP));
-	UINT16 cnum = (UINT16)(((uint8_t*)colorramp - (uint8_t*)(skincolors[0].ramp))/sizeof(skincolor_t));
+	UINT16 cnum = (UINT16)(((UINT8*)colorramp - (UINT8*)(skincolors[0].ramp))/sizeof(skincolor_t));
 	UINT32 n = luaL_checkinteger(L, 2);
 	UINT8 i = (UINT8)luaL_checkinteger(L, 3);
-	if (cnum < SKINCOLOR_FIRSTFREESLOT || cnum >= numskincolors)
-		return luaL_error(L, "skincolors[] index %d out of range (%d - %d)", cnum, SKINCOLOR_FIRSTFREESLOT, numskincolors-1);
+	if (!cnum || cnum >= numskincolors)
+		return luaL_error(L, "skincolors[] index %d out of range (1 - %d)", cnum, numskincolors-1);
 	if (n >= COLORRAMPSIZE)
 		return luaL_error(L, LUA_QL("skincolor_t") " field 'ramp' index %d out of range (0 - %d)", n, COLORRAMPSIZE-1);
 	if (hud_running)
diff --git a/src/m_menu.c b/src/m_menu.c
index ddadec79971f08b0021d8d8ebc8e991510a27acf..59d297e1a2710563328443f120eef0eba61b2733 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1482,21 +1482,23 @@ static menuitem_t OP_OpenGLLightingMenu[] =
 static menuitem_t OP_SoundOptionsMenu[] =
 {
 	{IT_HEADER, NULL, "Game Audio", NULL, 0},
-	{IT_STRING | IT_CVAR,  NULL,  "Sound Effects", &cv_gamesounds, 12},
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Sound Volume", &cv_soundvolume, 22},
+	{IT_STRING | IT_CVAR,  NULL,  "Sound Effects", &cv_gamesounds, 6},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Sound Volume", &cv_soundvolume, 11},
 
-	{IT_STRING | IT_CVAR,  NULL,  "Digital Music", &cv_gamedigimusic, 42},
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Digital Music Volume", &cv_digmusicvolume,  52},
+	{IT_STRING | IT_CVAR,  NULL,  "Digital Music", &cv_gamedigimusic, 21},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Digital Music Volume", &cv_digmusicvolume,  26},
 
-	{IT_STRING | IT_CVAR,  NULL,  "MIDI Music", &cv_gamemidimusic, 72},
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 82},
+	{IT_STRING | IT_CVAR,  NULL,  "MIDI Music", &cv_gamemidimusic, 36},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 41},
+	
+	{IT_STRING | IT_CVAR,  NULL,  "Music Preference", &cv_musicpref, 51},
 
-	{IT_HEADER, NULL, "Miscellaneous", NULL, 102},
-	{IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 114},
-	{IT_STRING | IT_CVAR, NULL, "Reset Music Upon Dying", &cv_resetmusic, 124},
-	{IT_STRING | IT_CVAR, NULL, "Default 1-Up sound", &cv_1upsound, 134},
+	{IT_HEADER, NULL, "Miscellaneous", NULL, 61},
+	{IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 67},
+	{IT_STRING | IT_CVAR, NULL, "Reset Music Upon Dying", &cv_resetmusic, 72},
+	{IT_STRING | IT_CVAR, NULL, "Default 1-Up sound", &cv_1upsound, 77},
 
-	{IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 154},
+	{IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 87},
 };
 
 #ifdef HAVE_OPENMPT
@@ -2195,7 +2197,7 @@ menu_t OP_ColorOptionsDef =
 	0,
 	NULL
 };
-menu_t OP_SoundOptionsDef = DEFAULTMENUSTYLE(
+menu_t OP_SoundOptionsDef = DEFAULTSCROLLMENUSTYLE(
 	MTREE2(MN_OP_MAIN, MN_OP_SOUND),
 	"M_SOUND", OP_SoundOptionsMenu, &OP_MainDef, 30, 30);
 menu_t OP_SoundAdvancedDef = DEFAULTMENUSTYLE(
diff --git a/src/p_local.h b/src/p_local.h
index b6c34f357a18e2ca55211cca139eb429c5d871af..4077fecf6b36c2aa880b6d6cfa7c3ab546682377 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -142,6 +142,7 @@ void P_SetPlayerAngle(player_t *player, angle_t angle);
 angle_t P_GetLocalAngle(player_t *player);
 void P_SetLocalAngle(player_t *player, angle_t angle);
 void P_ForceLocalAngle(player_t *player, angle_t angle);
+boolean P_PlayerFullbright(player_t *player);
 
 boolean P_IsObjectInGoop(mobj_t *mo);
 boolean P_IsObjectOnGround(mobj_t *mo);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 6f3f53559103b4bc16143d0cc727e53152744f35..9cd5667da69dca732e1eedf7ac0fd45eea17e12b 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -442,7 +442,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 
 			mobj->sprite2 = spr2;
 			mobj->frame = frame|(st->frame&~FF_FRAMEMASK);
-			if (player->powers[pw_super] || (player->powers[pw_carry] == CR_NIGHTSMODE && (player->charflags & (SF_SUPER|SF_NONIGHTSSUPER)) == SF_SUPER)) // Super colours? Super bright!
+			if (P_PlayerFullbright(player))
 				mobj->frame |= FF_FULLBRIGHT;
 		}
 		// Regular sprites
diff --git a/src/p_setup.c b/src/p_setup.c
index 2fd7ba5b095207db4cd1838bbaa1e17f693f85ef..e032b7be24c3fd7d9a4609d982f379ab64e06cfb 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -355,8 +355,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	mapheaderinfo[num]->mustrack = 0;
 	mapheaderinfo[num]->muspos = 0;
 	mapheaderinfo[num]->musinterfadeout = 0;
-	mapheaderinfo[num]->musintername[0] = '\0';
-	mapheaderinfo[num]->muspostbossname[6] = 0;
+	mapheaderinfo[num]->musintername[0] = 0;
+	mapheaderinfo[num]->muspostbossname[0] = 0;
 	mapheaderinfo[num]->muspostbosstrack = 0;
 	mapheaderinfo[num]->muspostbosspos = 0;
 	mapheaderinfo[num]->muspostbossfadein = 0;
diff --git a/src/p_user.c b/src/p_user.c
index 679f4064b4950378769df1c2cbf785e175d530c9..44793b0cc1074684361c9bdccc3bc04ecb45cecf 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -5536,7 +5536,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 
 		if ((!(gametyperules & GTR_TEAMFLAGS) || !player->gotflag) && !player->exiting)
 		{
-			if (player->secondjump == 1 && player->charability != CA_DOUBLEJUMP)
+			if (player->secondjump == 1 && player->charability != CA_DOUBLEJUMP && player->charability != CA_THOK)
 			{
 				fixed_t potentialmomz;
 				if (player->charability == CA_SLOWFALL)
@@ -7975,20 +7975,13 @@ void P_MovePlayer(player_t *player)
 	// Locate the capsule for this mare.
 	else if (maptol & TOL_NIGHTS)
 	{
-		if ((player->powers[pw_carry] == CR_NIGHTSMODE)
-		&& (player->exiting
-		|| !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
-			&& player->mo->state < &states[S_PLAY_NIGHTS_TRANS6]))) // Note the < instead of <=
+		if (P_PlayerFullbright(player))
 		{
-			skin_t *skin = ((skin_t *)(player->mo->skin));
-			if (( skin->flags & (SF_SUPER|SF_NONIGHTSSUPER) ) == SF_SUPER)
-			{
-				player->mo->color = skin->supercolor
-					+ ((player->nightstime == player->startedtime)
-						? 4
-						: abs((((signed)leveltime >> 1) % 9) - 4)); // This is where super flashing is handled.
-				G_GhostAddColor(GHC_SUPER);
-			}
+			player->mo->color = ((skin_t *)player->mo->skin)->supercolor
+				+ ((player->nightstime == player->startedtime)
+					? 4
+					: abs((((signed)leveltime >> 1) % 9) - 4)); // This is where super flashing is handled.
+			G_GhostAddColor(GHC_SUPER);
 		}
 
 		if (!player->capsule && !player->bonustime)
@@ -12895,3 +12888,12 @@ void P_ForceLocalAngle(player_t *player, angle_t angle)
 	else if (player == &players[secondarydisplayplayer])
 		localangle2 = angle;
 }
+
+boolean P_PlayerFullbright(player_t *player)
+{
+	return (player->powers[pw_super]
+		|| ((player->powers[pw_carry] == CR_NIGHTSMODE && (((skin_t *)player->mo->skin)->flags & (SF_SUPER|SF_NONIGHTSSUPER)) == SF_SUPER) // Super colours? Super bright!
+		&& (player->exiting
+			|| !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
+			&& player->mo->state < &states[S_PLAY_NIGHTS_TRANS6])))); // Note the < instead of <=
+}
diff --git a/src/s_sound.c b/src/s_sound.c
index 5ed9fd83a22d8f475c7400878db264d329a42bd1..072a69f6c13de72fcaa2311f6f4e56f27c9fe150 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -60,6 +60,7 @@ static void Command_RestartAudio_f(void);
 static void GameMIDIMusic_OnChange(void);
 static void GameSounds_OnChange(void);
 static void GameDigiMusic_OnChange(void);
+static void MusicPref_OnChange(void);
 
 #ifdef HAVE_OPENMPT
 static void ModFilter_OnChange(void);
@@ -129,6 +130,14 @@ consvar_t cv_gamedigimusic = {"digimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_O
 consvar_t cv_gamemidimusic = {"midimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameMIDIMusic_OnChange, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_gamesounds = {"sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameSounds_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
+// Music preference
+static CV_PossibleValue_t cons_musicpref_t[] = {
+	{0, "Digital"},
+	{1, "MIDI"},
+	{0, NULL}
+};
+consvar_t cv_musicpref = {"musicpref", "Digital", CV_SAVE|CV_CALL|CV_NOINIT, cons_musicpref_t, MusicPref_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
 // Window focus sound sytem toggles
 consvar_t cv_playmusicifunfocused = {"playmusicifunfocused", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_playsoundsifunfocused = {"playsoundsifunfocused", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -301,6 +310,7 @@ void S_RegisterSoundStuff(void)
 	CV_RegisterVar(&cv_gamesounds);
 	CV_RegisterVar(&cv_gamedigimusic);
 	CV_RegisterVar(&cv_gamemidimusic);
+	CV_RegisterVar(&cv_musicpref);
 #ifdef HAVE_OPENMPT
 	CV_RegisterVar(&cv_modfilter);
 #endif
@@ -1847,19 +1857,6 @@ const char *S_MusicName(void)
 	return music_name;
 }
 
-boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping)
-{
-	if (!I_SongPlaying())
-		return false;
-
-	strncpy(mname, music_name, 7);
-	mname[6] = 0;
-	*mflags = music_flags;
-	*looping = music_looping;
-
-	return (boolean)mname[0];
-}
-
 boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi)
 {
 	return (
@@ -2100,6 +2097,8 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst)
 	boolean mapmuschanged = false;
 	musicstack_t *result;
 	musicstack_t *entry = Z_Calloc(sizeof (*result), PU_MUSIC, NULL);
+	boolean currentmidi = (I_SongType() == MU_MID || I_SongType() == MU_MID_EX);
+	boolean midipref = cv_musicpref.value;
 
 	if (status)
 		result = S_GetMusicStackEntry(status, fromfirst, -1);
@@ -2154,7 +2153,8 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst)
 		return false;
 	}
 
-	if (strncmp(entry->musname, S_MusicName(), 7)) // don't restart music if we're already playing it
+	if (strncmp(entry->musname, S_MusicName(), 7) || // don't restart music if we're already playing it
+		(midipref != currentmidi && S_PrefAvailable(midipref, entry->musname))) // but do if the user's preference has changed
 	{
 		if (music_stack_fadeout)
 			S_ChangeMusicEx(entry->musname, entry->musflags, entry->looping, 0, music_stack_fadeout, 0);
@@ -2201,10 +2201,12 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst)
 
 static lumpnum_t S_GetMusicLumpNum(const char *mname)
 {
-	if (!S_DigMusicDisabled() && S_DigExists(mname))
-		return W_GetNumForName(va("o_%s", mname));
-	else if (!S_MIDIMusicDisabled() && S_MIDIExists(mname))
-		return W_GetNumForName(va("d_%s", mname));
+	boolean midipref = cv_musicpref.value;
+	
+	if (S_PrefAvailable(midipref, mname))
+		return W_GetNumForName(va(midipref ? "d_%s":"o_%s", mname));
+	else if (S_PrefAvailable(!midipref, mname))
+		return W_GetNumForName(va(midipref ? "o_%s":"d_%s", mname));
 	else
 		return LUMPERROR;
 }
@@ -2330,6 +2332,8 @@ static void S_ChangeMusicToQueue(void)
 void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms)
 {
 	char newmusic[7];
+	boolean currentmidi = (I_SongType() == MU_MID || I_SongType() == MU_MID_EX);
+	boolean midipref = cv_musicpref.value;
 
 	if (S_MusicDisabled())
 		return;
@@ -2359,7 +2363,8 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32
 		I_FadeSong(0, prefadems, S_ChangeMusicToQueue);
 		return;
 	}
-	else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET))
+	else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET) || 
+		(midipref != currentmidi && S_PrefAvailable(midipref, newmusic)))
  	{
 		CONS_Debug(DBG_DETAILED, "Now playing song %s\n", newmusic);
 
@@ -2665,32 +2670,24 @@ void GameDigiMusic_OnChange(void)
 		digital_disabled = false;
 		I_StartupSound(); // will return early if initialised
 		I_InitMusic();
-		S_StopMusic();
+
 		if (Playing())
 			P_RestoreMusic(&players[consoleplayer]);
-		else
+		else if ((!cv_musicpref.value || midi_disabled) && S_DigExists("_clear"))
 			S_ChangeMusicInternal("_clear", false);
 	}
 	else
 	{
 		digital_disabled = true;
-		if (S_MusicType() != MU_MID)
+		if (S_MusicType() != MU_MID && S_MusicType() != MU_MID_EX)
 		{
-			if (midi_disabled)
-				S_StopMusic();
-			else
+			S_StopMusic();
+			if (!midi_disabled)
 			{
-				char mmusic[7];
-				UINT16 mflags;
-				boolean looping;
-
-				if (S_MusicInfo(mmusic, &mflags, &looping) && S_MIDIExists(mmusic))
-				{
-					S_StopMusic();
-					S_ChangeMusic(mmusic, mflags, looping);
-				}
+				if (Playing())
+					P_RestoreMusic(&players[consoleplayer]);
 				else
-					S_StopMusic();
+					S_ChangeMusicInternal("_clear", false);
 			}
 		}
 	}
@@ -2708,9 +2705,10 @@ void GameMIDIMusic_OnChange(void)
 		midi_disabled = false;
 		I_StartupSound(); // will return early if initialised
 		I_InitMusic();
+
 		if (Playing())
 			P_RestoreMusic(&players[consoleplayer]);
-		else
+		else if ((cv_musicpref.value || digital_disabled) && S_MIDIExists("_clear"))
 			S_ChangeMusicInternal("_clear", false);
 	}
 	else
@@ -2718,26 +2716,30 @@ void GameMIDIMusic_OnChange(void)
 		midi_disabled = true;
 		if (S_MusicType() == MU_MID || S_MusicType() == MU_MID_EX)
 		{
-			if (digital_disabled)
-				S_StopMusic();
-			else
+			S_StopMusic();
+			if (!digital_disabled)
 			{
-				char mmusic[7];
-				UINT16 mflags;
-				boolean looping;
-
-				if (S_MusicInfo(mmusic, &mflags, &looping) && S_DigExists(mmusic))
-				{
-					S_StopMusic();
-					S_ChangeMusic(mmusic, mflags, looping);
-				}
+				if (Playing())
+					P_RestoreMusic(&players[consoleplayer]);
 				else
-					S_StopMusic();
+					S_ChangeMusicInternal("_clear", false);
 			}
 		}
 	}
 }
 
+void MusicPref_OnChange(void)
+{
+	if (M_CheckParm("-nomusic") || M_CheckParm("-noaudio") ||
+		M_CheckParm("-nomidimusic") || M_CheckParm("-nodigmusic"))
+		return;
+
+	if (Playing())
+		P_RestoreMusic(&players[consoleplayer]);
+	else if (S_PrefAvailable(cv_musicpref.value, "_clear"))
+		S_ChangeMusicInternal("_clear", false);
+}
+
 #ifdef HAVE_OPENMPT
 void ModFilter_OnChange(void)
 {
diff --git a/src/s_sound.h b/src/s_sound.h
index 3334fcb69d7bf18820776130c627ba948b1ecb52..35d1c3dc5e57276b0cdae86c34f8fe9cb9db362e 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -46,6 +46,7 @@ extern consvar_t cv_1upsound;
 extern consvar_t cv_gamedigimusic;
 extern consvar_t cv_gamemidimusic;
 extern consvar_t cv_gamesounds;
+extern consvar_t cv_musicpref;
 
 extern consvar_t cv_playmusicifunfocused;
 extern consvar_t cv_playsoundsifunfocused;
@@ -178,11 +179,16 @@ boolean S_MusicPaused(void);
 boolean S_MusicNotInFocus(void);
 musictype_t S_MusicType(void);
 const char *S_MusicName(void);
-boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping);
 boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi);
 #define S_DigExists(a) S_MusicExists(a, false, true)
 #define S_MIDIExists(a) S_MusicExists(a, true, false)
 
+// Returns whether the preferred format a (true = MIDI, false = Digital)
+// exists and is enabled for musicname b
+#define S_PrefAvailable(a, b) (a ? \
+	(!S_MIDIMusicDisabled() && S_MIDIExists(b)) : \
+	(!S_DigMusicDisabled() && S_DigExists(b)))
+
 //
 // Music Effects
 //
diff --git a/src/y_inter.c b/src/y_inter.c
index 58e0c4a885702496deacd774a85f8441f9e86015..d857d60dc0815e6f8eb1991c3f47887107eb6289 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -141,7 +141,6 @@ static y_data data;
 
 // graphics
 static patch_t *bgpatch = NULL;     // INTERSCR
-static patch_t *widebgpatch = NULL; // INTERSCW
 static patch_t *bgtile = NULL;      // SPECTILE/SRB2BACK
 static patch_t *interpic = NULL;    // custom picture defined in map header
 static boolean usetile;
@@ -330,7 +329,7 @@ void Y_IntermissionDrawer(void)
 		safetorender = false;
 	}
 
-	if (!usebuffer || !safetorender)
+	if (!safetorender)
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
 	if (!safetorender)
@@ -359,12 +358,11 @@ void Y_IntermissionDrawer(void)
 		else if (rendermode != render_soft && usebuffer)
 			HWR_DrawIntermissionBG();
 #endif
-		else
+		else if (bgpatch)
 		{
-			if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx == 400)
-				V_DrawScaledPatch(0, 0, V_SNAPTOLEFT, widebgpatch);
-			else if (bgpatch)
-				V_DrawScaledPatch(0, 0, 0, bgpatch);
+			fixed_t hs = vid.width  * FRACUNIT / BASEVIDWIDTH;
+			fixed_t vs = vid.height * FRACUNIT / BASEVIDHEIGHT;
+			V_DrawStretchyFixedPatch(0, 0, hs, vs, V_NOSCALEPATCH, bgpatch, NULL);
 		}
 	}
 	else if (bgtile)
@@ -1266,7 +1264,6 @@ void Y_StartIntermission(void)
 			data.coop.actnum = mapheaderinfo[gamemap-1]->actnum;
 
 			// get background patches
-			widebgpatch = W_CachePatchName("INTERSCW", PU_PATCH);
 			bgpatch = W_CachePatchName("INTERSCR", PU_PATCH);
 
 			// grab an interscreen if appropriate
@@ -2084,7 +2081,6 @@ static void Y_UnloadData(void)
 
 	// unload the background patches
 	UNLOAD(bgpatch);
-	UNLOAD(widebgpatch);
 	UNLOAD(bgtile);
 	UNLOAD(interpic);
 
@@ -2127,7 +2123,6 @@ static void Y_CleanupData(void)
 {
 	// unload the background patches
 	CLEANUP(bgpatch);
-	CLEANUP(widebgpatch);
 	CLEANUP(bgtile);
 	CLEANUP(interpic);
 
diff --git a/tools/masterserver/.gitignore b/tools/masterserver/.gitignore
index 9f45745a36fda74c3d242e2a1ccd4eb673c15999..8ae879eec144d61b8662f1b319be2d2e97dc373f 100644
--- a/tools/masterserver/.gitignore
+++ b/tools/masterserver/.gitignore
@@ -1,4 +1,4 @@
 /client
 /server
-/server.log
-/*.o
+/*.log
+/*.o
\ No newline at end of file
diff --git a/tools/masterserver/ipcs.h b/tools/masterserver/ipcs.h
index b9dc52fdf55966b448b19b631904a97f66530fa7..4e144f4a57c252d4ab27e1d46f3acbf0e505a7ba 100644
--- a/tools/masterserver/ipcs.h
+++ b/tools/masterserver/ipcs.h
@@ -140,8 +140,8 @@ typedef struct
 	char port[8];
 	char name[32];
 	INT32 room;
-	char key[32]; // Secret key for linking dedicated servers to accounts
 	char version[8]; // format is: x.yy.z (like 1.30.2 or 1.31)
+	char key[32]; // Secret key for linking dedicated servers to accounts
 } ATTRPACK msg_server_t;
 
 typedef struct
diff --git a/tools/masterserver/masterserver.sh b/tools/masterserver/masterserver.sh
index 9b1adb128c06905cfd743fc082fd43e335c095e0..fe4ba00718ad052c54da2208c5f7d96f11947ad0 100755
--- a/tools/masterserver/masterserver.sh
+++ b/tools/masterserver/masterserver.sh
@@ -5,9 +5,10 @@
 
 # Get LSB functions
 . /lib/lsb/init-functions
-. /etc/default/rcS
+#. /etc/default/rcS
 
-SRB2MS=/usr/local/bin/masterserver
+#SRB2MS=/usr/local/bin/masterserver
+SRB2MS=./server
 SRB2MS_PORT=28900
 
 # Check that the package is still installed
@@ -15,11 +16,9 @@ SRB2MS_PORT=28900
 
 case "$1" in
 	start)
-		log_begin_msg "Starting SRB2MS..."
+		log_begin_msg "Starting SRB2MS...\n"
 		umask 002
-		if start-stop-daemon --start \
-		--exec $SRB2MS \
-		-- $SRB2MS_PORT; then
+		if exec $SRB2MS $SRB2MS_PORT & then
 			log_end_msg 0
 		else
 			log_end_msg $?
@@ -27,11 +26,11 @@ case "$1" in
 	;;
 
 	stop)
-		log_begin_msg "Stopping SRB2MS..."
-		if start-stop-daemon --stop --exec $SRB2MS; then
-		log_end_msg 0
+		log_begin_msg "Stopping SRB2MS...\n"
+		if killall $SRB2MS -q & then
+			log_end_msg 0
 		else
-		log_end_msg $?
+			log_end_msg $?
 		fi
 	;;
 
@@ -40,7 +39,7 @@ case "$1" in
 	;;
 
 	*)
-	e	cho "Usage: /etc/init.d/masterserver {start|stop|restart|force-reload}"
+	echo "Usage: $0 {start|stop|restart|force-reload}"
 		exit 1
 	;;
 esac
diff --git a/tools/masterserver/server.cpp b/tools/masterserver/server.cpp
index b7ed0d6b48db8d92f9ba9668e9587f9278ed887a..d0020530179ce45ae429a50d03177a6af752947b 100644
--- a/tools/masterserver/server.cpp
+++ b/tools/masterserver/server.cpp
@@ -86,7 +86,7 @@ typedef struct
 
 //=============================================================================
 
-#define HOSTNAME "loopback"
+#define HOSTNAME "localhost"
 #define USER "srb2_ms"
 #define PASSWORD "gLRDRb7WgLRDRb7W"
 #define DATABASE "srb2_ms"
@@ -291,17 +291,17 @@ void MySQL_AddServer(const char *ip, const char *port, const char *name, const c
         char checkquery[500];
         char updatequery[5000];
         char queryp1[5000] = "INSERT INTO `ms_servers` (`name`,`ip`,`port`,`version`,`timestamp`,`room`,`key`) VALUES ('%s','%s','%s','%s','%ld','%d','%s')";
-        char checkqueryp1[500] = "SELECT room_override FROM `ms_servers` WHERE `ip` = '%s'";
+        char checkqueryp1[500] = "SELECT room_override FROM `ms_servers` WHERE `ip` = '%s' AND `port` = '%s'";
 		char updatequeryp1[5000];
 		if(firstadd)
 		{
 			logPrintf(logfile, "First add.\n");
-			strcpy(updatequeryp1, "UPDATE `ms_servers` SET `name` = '%s', `port` = '%s', `version` = '%s', timestamp = '%ld', upnow = '1', `room` = '%d', `delisted` = '0', `key` = '%s' WHERE `ip` = '%s'");
+			strcpy(updatequeryp1, "UPDATE `ms_servers` SET `name` = '%s', `port` = '%s', `version` = '%s', timestamp = '%ld', upnow = '1', `room` = '%d', `delisted` = '0', `key` = '%s' WHERE `ip` = '%s' AND `port` = '%s'");
 		}
 		else
 		{
 			logPrintf(logfile, "Update ping.\n");
-			strcpy(updatequeryp1, "UPDATE `ms_servers` SET `name` = '%s', `port` = '%s', `version` = '%s', timestamp = '%ld', upnow = '1', `room` = '%d', `key` = '%s' WHERE `ip` = '%s' AND `delisted` = '0'");
+			strcpy(updatequeryp1, "UPDATE `ms_servers` SET `name` = '%s', `port` = '%s', `version` = '%s', timestamp = '%ld', upnow = '1', `room` = '%d', `key` = '%s' WHERE `ip` = '%s' AND `port` = '%s' AND `delisted` = '0'");
 		}
         MySQL_Conn(false);
         mysql_real_escape_string(conn, escapedName, name, (unsigned long)strlen(name));
@@ -314,10 +314,10 @@ void MySQL_AddServer(const char *ip, const char *port, const char *name, const c
 			logPrintf(errorfile, "IP %s tried to use the private room %d! THIS SHOULD NOT HAPPEN\n", ip, room);
 			return;
 		}
-        sprintf(checkquery, checkqueryp1, ip);
+        sprintf(checkquery, checkqueryp1, ip, port);
         time_t timestamp;
         timestamp = time (NULL);
-        logPrintf(logfile, "Checking for existing servers in table with the same IP...\n");
+        logPrintf(logfile, "Checking for existing servers in table with the same IP and port...\n");
         logPrintf(mysqlfile, "Executing MySQL Query: %s\n", checkquery);
         if(mysql_query(conn, checkquery)) {
           logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn));
@@ -341,9 +341,9 @@ void MySQL_AddServer(const char *ip, const char *port, const char *name, const c
 			if(atoi(row[0]) != 0)
 				room = atoi(row[0]);
             mysql_free_result(res);
-            logPrintf(logfile, "Server's IP already exists, so let's just update it instead...\n");
+            logPrintf(logfile, "Server's IP and port already exists, so let's just update it instead...\n");
             logPrintf(logfile, "Updating Server Data for %s\n", ip);
-            sprintf(updatequery, updatequeryp1, escapedName, escapedPort, escapedVersion, timestamp, room, escapedKey, ip);
+            sprintf(updatequery, updatequeryp1, escapedName, escapedPort, escapedVersion, timestamp, room, escapedKey, ip, port);
             logPrintf(mysqlfile, "Executing MySQL Query: %s\n", updatequery);
             if(mysql_query(conn, updatequery)) {
                logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn));
@@ -619,10 +619,10 @@ void MySQL_ListServServers(UINT32 id, UINT32 type, const char *ip) {
 void MySQL_RemoveServer(char *ip, char *port, char *name, char *version) {
         char escapedName[255];
         char updatequery[5000];
-        char updatequeryp1[5000] = "UPDATE `ms_servers` SET upnow = '0' WHERE `ip` = '%s' AND `permanent` = '0'";
+        char updatequeryp1[5000] = "UPDATE `ms_servers` SET upnow = '0' WHERE `ip` = '%s' AND `port` = '%s' AND `permanent` = '0'";
         MySQL_Conn(false);
         mysql_real_escape_string(conn, escapedName, name, (unsigned long)strlen(name));
-        sprintf(updatequery, updatequeryp1, ip);
+        sprintf(updatequery, updatequeryp1, ip, port);
         logPrintf(mysqlfile, "Executing MySQL Query: %s\n", updatequery);
         if(mysql_query(conn, updatequery)) {
            logPrintf(errorfile, "MYSQL ERROR: %s\n", mysql_error(conn));
@@ -841,6 +841,10 @@ static void addServer(int id, char *buffer, bool firstadd)
 	info->port[sizeof (info->port)-1] = '\0';
 	info->name[sizeof (info->name)-1] = '\0';
 	info->version[sizeof (info->version)-1] = '\0';
+
+	logPrintf(logfile, "addServer(): Version = \"%s\"\n", info->version);
+	logPrintf(logfile, "addServer(): Key = \"%s\"\n", info->key);
+
 	// retrieve the true ip of the server
 	strcpy(info->ip, server_socket.getClientIP(id));
 	//strcpy(info->port, server_socket.getClientPort(id));
@@ -995,7 +999,7 @@ int main(int argc, char *argv[])
 
 	if (server_socket.listen(argv[1]) < 0)
 	{
-		fprintf(stderr, "Error while initializing the server\n");
+		fprintf(stderr, "Error while initializing the server; port being used! Try killing the other Master Server.\n");
 		exit(2);
 	}
 
diff --git a/tools/masterserver/structure.sql b/tools/masterserver/structure.sql
index 3cc2cb15bbcfa24f4bdd37968bc982bbeb5f87ca..013c223839813b45cc51454ac4031b3444f9eb60 100644
--- a/tools/masterserver/structure.sql
+++ b/tools/masterserver/structure.sql
@@ -13,8 +13,8 @@ SET time_zone = "+00:00";
 -- Database: `srb2ms`
 --
 
-CREATE DATABASE IF NOT EXISTS `srb2ms` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-USE `srb2ms`;
+CREATE DATABASE IF NOT EXISTS `srb2_ms` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
+USE `srb2_ms`;
 
 
 -- --------------------------------------------------------
@@ -25,8 +25,8 @@ USE `srb2ms`;
 
 CREATE TABLE `ms_bans` (
   `bid` int(11) DEFAULT NULL,
-  `ipstart` int(11) DEFAULT NULL,
-  `ipend` int(11) DEFAULT NULL,
+  `ipstart` int(10) unsigned DEFAULT NULL,
+  `ipend` int(10) unsigned DEFAULT NULL,
   `full_endtime` int(11) DEFAULT NULL,
   `permanent` tinyint(1) DEFAULT NULL,
   `hostonly` tinyint(1) DEFAULT NULL,
@@ -63,19 +63,19 @@ INSERT INTO `ms_rooms` (`room_id`, `title`, `motd`, `visible`, `order`, `private
 --
 
 CREATE TABLE `ms_servers` (
-  `sid` int(11) NOT NULL,
+  `sid` int(11) primary key AUTO_INCREMENT,
   `name` text COLLATE utf8mb4_unicode_ci NOT NULL,
   `ip` text COLLATE utf8mb4_unicode_ci NOT NULL,
-  `port` int(11) NOT NULL,
+  `port` int(11) NOT NULL DEFAULT 5029,
   `version` text COLLATE utf8mb4_unicode_ci NOT NULL,
-  `timestamp` int(11) NOT NULL,
-  `room` int(11) NOT NULL,
+  `timestamp` int(11) NOT NULL DEFAULT 0,
+  `room` int(11) NOT NULL DEFAULT 0,
   `key` text COLLATE utf8mb4_unicode_ci NOT NULL,
-  `room_override` int(11) NOT NULL,
-  `upnow` tinyint(1) NOT NULL,
-  `permanent` tinyint(1) NOT NULL,
-  `delisted` tinyint(1) NOT NULL,
-  `sticky` int(11) NOT NULL
+  `room_override` int(11) NOT NULL DEFAULT 0,
+  `upnow` tinyint(1) NOT NULL DEFAULT 1,
+  `permanent` tinyint(1) NOT NULL DEFAULT 0,
+  `delisted` tinyint(1) NOT NULL DEFAULT 0,
+  `sticky` int(11) NOT NULL DEFAULT 0
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 -- --------------------------------------------------------
@@ -85,16 +85,20 @@ CREATE TABLE `ms_servers` (
 --
 
 CREATE TABLE `ms_versions` (
-  `mod_id` int(11) NOT NULL,
-  `mod_version` int(11) NOT NULL
+  `mod_id` int(10) unsigned primary key AUTO_INCREMENT,
+  `mod_version` int(10) unsigned NOT NULL DEFAULT 1,
+  `mod_vstring` varchar(45) NOT NULL DEFAULT 'v1.0',
+  `mod_codebase` int(10) unsigned NOT NULL DEFAULT 205,
+  `mod_name` varchar(255) NOT NULL DEFAULT 'Default MOD Name',
+  `mod_url` text NOT NULL 
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
 
 --
 -- Dumping data for table `ms_versions`
 --
 
-INSERT INTO `ms_versions` (`mod_id`, `mod_version`) VALUES
-(12, 25);
+INSERT INTO `ms_versions` (`mod_id`, `mod_version`, `mod_vstring`, `mod_codebase`, `mod_name`, `mod_url`) VALUES
+(18, 42, 'v2.2.2', 205, 'SRB2 2.2', 'SRB2.org');
 
 -- --------------------------------------------------------
 
@@ -114,4 +118,4 @@ COMMIT;
 
 /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
 /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
-/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
\ No newline at end of file