diff --git a/cmake/Modules/FindDiscordRPC.cmake b/cmake/Modules/FindDiscordRPC.cmake
new file mode 100644
index 000000000..e71762767
--- /dev/null
+++ b/cmake/Modules/FindDiscordRPC.cmake
@@ -0,0 +1,23 @@
+include(LibFindMacros)
+
+libfind_pkg_check_modules(DISCORDRPC_PKGCONF DISCORDRPC)
+
+find_path(DISCORDRPC_INCLUDE_DIR
+ NAMES discord_rpc.h
+ PATHS
+ ${DISCORDRPC_PKGCONF_INCLUDE_DIRS}
+ "/usr/include"
+ "/usr/local/include"
+)
+
+find_library(DISCORDRPC_LIBRARY
+ NAMES discord-rpc
+ PATHS
+ ${DISCORDRPC_PKGCONF_LIBRARY_DIRS}
+ "/usr/lib"
+ "/usr/local/lib"
+)
+
+set(DISCORDRPC_PROCESS_INCLUDES DISCORDRPC_INCLUDE_DIR)
+set(DISCORDRPC_PROCESS_LIBS DISCORDRPC_LIBRARY)
+libfind_process(DISCORDRPC)
diff --git a/libs/DLL-README.txt b/libs/DLL-README.txt
index bbbecbd0e..51963baca 100644
--- a/libs/DLL-README.txt
+++ b/libs/DLL-README.txt
@@ -1,6 +1,6 @@
# SRB2Kart - Which DLLs do I need to bundle?
-Updated 12/6/2019 (v2.2)
+Updated 8/23/2020 (v1.3)
Here are the required DLLs, per build. For each architecture, copy all the binaries from these folders:
@@ -15,6 +15,7 @@ and don't forget to build r_opengl.dll for srb2dd.
* libs\dll-binaries\i686\exchndl.dll
* libs\dll-binaries\i686\libgme.dll
+* libs\dll-binaries\i686\discord-rpc.dll
* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
* libs\SDL2\i686-w64-mingw32\bin\SDL2.dll
* libs\SDL2mixerX\i686-w64-mingw32\bin\*.dll (get everything)
@@ -24,27 +25,8 @@ and don't forget to build r_opengl.dll for srb2dd.
* libs\dll-binaries\x86_64\exchndl.dll
* libs\dll-binaries\x86_64\libgme.dll
+* libs\dll-binaries\x86_64\discord-rpc.dll
* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
* libs\SDL2\x86_64-w64-mingw32\bin\SDL2.dll
-<<<<<<< HEAD
-* libs\SDL2_mixer\x86_64-w64-mingw32\bin\*.dll (get everything)
-
-## srb2kartdd, 32-bit
-
-* libs\dll-binaries\i686\exchndl.dll
-* libs\dll-binaries\i686\fmodex.dll
-* libs\dll-binaries\i686\libgme.dll
-* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
-* r_opengl.dll (build this from make)
-
-## srb2kartdd, 64-bit
-
-* libs\dll-binaries\x86_64\exchndl.dll
-* libs\dll-binaries\x86_64\fmodex.dll
-* libs\dll-binaries\x86_64\libgme.dll
-* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
-* r_opengl.dll (build this from make)
-=======
* libs\SDL2mixerX\x86_64-w64-mingw32\bin\*.dll (get everything)
* libs\libopenmpt\x86_64\bin\mingw\libopenmpt.dll
->>>>>>> srb2/next
diff --git a/libs/discord-rpc.props b/libs/discord-rpc.props
new file mode 100644
index 000000000..83c7a03f3
--- /dev/null
+++ b/libs/discord-rpc.props
@@ -0,0 +1,16 @@
+
+
+
+
+ $(SolutionDir)libs\discord-rpc\win32-dynamic\lib;$(LibraryPath)
+ $(SolutionDir)libs\discord-rpc\win32-dynamic\lib;$(IncludePath)
+ $(SolutionDir)libs\discord-rpc\win64-dynamic\lib;$(LibraryPath)
+ $(SolutionDir)libs\discord-rpc\win64-dynamic\lib;$(IncludePath)
+
+
+
+ discord-rpc.dll.a;%(AdditionalDependencies)
+
+
+
+
\ No newline at end of file
diff --git a/libs/discord-rpc/win32-dynamic/include/discord_register.h b/libs/discord-rpc/win32-dynamic/include/discord_register.h
new file mode 100644
index 000000000..16fb42f32
--- /dev/null
+++ b/libs/discord-rpc/win32-dynamic/include/discord_register.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#if defined(DISCORD_DYNAMIC_LIB)
+#if defined(_WIN32)
+#if defined(DISCORD_BUILDING_SDK)
+#define DISCORD_EXPORT __declspec(dllexport)
+#else
+#define DISCORD_EXPORT __declspec(dllimport)
+#endif
+#else
+#define DISCORD_EXPORT __attribute__((visibility("default")))
+#endif
+#else
+#define DISCORD_EXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
+DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libs/discord-rpc/win32-dynamic/include/discord_rpc.h b/libs/discord-rpc/win32-dynamic/include/discord_rpc.h
new file mode 100644
index 000000000..3e1441e05
--- /dev/null
+++ b/libs/discord-rpc/win32-dynamic/include/discord_rpc.h
@@ -0,0 +1,87 @@
+#pragma once
+#include
+
+// clang-format off
+
+#if defined(DISCORD_DYNAMIC_LIB)
+# if defined(_WIN32)
+# if defined(DISCORD_BUILDING_SDK)
+# define DISCORD_EXPORT __declspec(dllexport)
+# else
+# define DISCORD_EXPORT __declspec(dllimport)
+# endif
+# else
+# define DISCORD_EXPORT __attribute__((visibility("default")))
+# endif
+#else
+# define DISCORD_EXPORT
+#endif
+
+// clang-format on
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DiscordRichPresence {
+ const char* state; /* max 128 bytes */
+ const char* details; /* max 128 bytes */
+ int64_t startTimestamp;
+ int64_t endTimestamp;
+ const char* largeImageKey; /* max 32 bytes */
+ const char* largeImageText; /* max 128 bytes */
+ const char* smallImageKey; /* max 32 bytes */
+ const char* smallImageText; /* max 128 bytes */
+ const char* partyId; /* max 128 bytes */
+ int partySize;
+ int partyMax;
+ const char* matchSecret; /* max 128 bytes */
+ const char* joinSecret; /* max 128 bytes */
+ const char* spectateSecret; /* max 128 bytes */
+ int8_t instance;
+} DiscordRichPresence;
+
+typedef struct DiscordUser {
+ const char* userId;
+ const char* username;
+ const char* discriminator;
+ const char* avatar;
+} DiscordUser;
+
+typedef struct DiscordEventHandlers {
+ void (*ready)(const DiscordUser* request);
+ void (*disconnected)(int errorCode, const char* message);
+ void (*errored)(int errorCode, const char* message);
+ void (*joinGame)(const char* joinSecret);
+ void (*spectateGame)(const char* spectateSecret);
+ void (*joinRequest)(const DiscordUser* request);
+} DiscordEventHandlers;
+
+#define DISCORD_REPLY_NO 0
+#define DISCORD_REPLY_YES 1
+#define DISCORD_REPLY_IGNORE 2
+
+DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
+ DiscordEventHandlers* handlers,
+ int autoRegister,
+ const char* optionalSteamId);
+DISCORD_EXPORT void Discord_Shutdown(void);
+
+/* checks for incoming messages, dispatches callbacks */
+DISCORD_EXPORT void Discord_RunCallbacks(void);
+
+/* If you disable the lib starting its own io thread, you'll need to call this from your own */
+#ifdef DISCORD_DISABLE_IO_THREAD
+DISCORD_EXPORT void Discord_UpdateConnection(void);
+#endif
+
+DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
+DISCORD_EXPORT void Discord_ClearPresence(void);
+
+DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
+
+DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
diff --git a/libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib b/libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib
new file mode 100644
index 000000000..d8b6689f3
Binary files /dev/null and b/libs/discord-rpc/win32-dynamic/lib/discord-rpc.lib differ
diff --git a/libs/discord-rpc/win64-dynamic/include/discord_register.h b/libs/discord-rpc/win64-dynamic/include/discord_register.h
new file mode 100644
index 000000000..16fb42f32
--- /dev/null
+++ b/libs/discord-rpc/win64-dynamic/include/discord_register.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#if defined(DISCORD_DYNAMIC_LIB)
+#if defined(_WIN32)
+#if defined(DISCORD_BUILDING_SDK)
+#define DISCORD_EXPORT __declspec(dllexport)
+#else
+#define DISCORD_EXPORT __declspec(dllimport)
+#endif
+#else
+#define DISCORD_EXPORT __attribute__((visibility("default")))
+#endif
+#else
+#define DISCORD_EXPORT
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
+DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libs/discord-rpc/win64-dynamic/include/discord_rpc.h b/libs/discord-rpc/win64-dynamic/include/discord_rpc.h
new file mode 100644
index 000000000..3e1441e05
--- /dev/null
+++ b/libs/discord-rpc/win64-dynamic/include/discord_rpc.h
@@ -0,0 +1,87 @@
+#pragma once
+#include
+
+// clang-format off
+
+#if defined(DISCORD_DYNAMIC_LIB)
+# if defined(_WIN32)
+# if defined(DISCORD_BUILDING_SDK)
+# define DISCORD_EXPORT __declspec(dllexport)
+# else
+# define DISCORD_EXPORT __declspec(dllimport)
+# endif
+# else
+# define DISCORD_EXPORT __attribute__((visibility("default")))
+# endif
+#else
+# define DISCORD_EXPORT
+#endif
+
+// clang-format on
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DiscordRichPresence {
+ const char* state; /* max 128 bytes */
+ const char* details; /* max 128 bytes */
+ int64_t startTimestamp;
+ int64_t endTimestamp;
+ const char* largeImageKey; /* max 32 bytes */
+ const char* largeImageText; /* max 128 bytes */
+ const char* smallImageKey; /* max 32 bytes */
+ const char* smallImageText; /* max 128 bytes */
+ const char* partyId; /* max 128 bytes */
+ int partySize;
+ int partyMax;
+ const char* matchSecret; /* max 128 bytes */
+ const char* joinSecret; /* max 128 bytes */
+ const char* spectateSecret; /* max 128 bytes */
+ int8_t instance;
+} DiscordRichPresence;
+
+typedef struct DiscordUser {
+ const char* userId;
+ const char* username;
+ const char* discriminator;
+ const char* avatar;
+} DiscordUser;
+
+typedef struct DiscordEventHandlers {
+ void (*ready)(const DiscordUser* request);
+ void (*disconnected)(int errorCode, const char* message);
+ void (*errored)(int errorCode, const char* message);
+ void (*joinGame)(const char* joinSecret);
+ void (*spectateGame)(const char* spectateSecret);
+ void (*joinRequest)(const DiscordUser* request);
+} DiscordEventHandlers;
+
+#define DISCORD_REPLY_NO 0
+#define DISCORD_REPLY_YES 1
+#define DISCORD_REPLY_IGNORE 2
+
+DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
+ DiscordEventHandlers* handlers,
+ int autoRegister,
+ const char* optionalSteamId);
+DISCORD_EXPORT void Discord_Shutdown(void);
+
+/* checks for incoming messages, dispatches callbacks */
+DISCORD_EXPORT void Discord_RunCallbacks(void);
+
+/* If you disable the lib starting its own io thread, you'll need to call this from your own */
+#ifdef DISCORD_DISABLE_IO_THREAD
+DISCORD_EXPORT void Discord_UpdateConnection(void);
+#endif
+
+DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
+DISCORD_EXPORT void Discord_ClearPresence(void);
+
+DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
+
+DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
diff --git a/libs/discord-rpc/win64-dynamic/lib/discord-rpc.lib b/libs/discord-rpc/win64-dynamic/lib/discord-rpc.lib
new file mode 100644
index 000000000..fcd009d82
Binary files /dev/null and b/libs/discord-rpc/win64-dynamic/lib/discord-rpc.lib differ
diff --git a/libs/dll-binaries/i686/discord-rpc.dll b/libs/dll-binaries/i686/discord-rpc.dll
new file mode 100644
index 000000000..88c7d0cef
Binary files /dev/null and b/libs/dll-binaries/i686/discord-rpc.dll differ
diff --git a/libs/dll-binaries/x86_64/discord-rpc.dll b/libs/dll-binaries/x86_64/discord-rpc.dll
new file mode 100644
index 000000000..8493c5490
Binary files /dev/null and b/libs/dll-binaries/x86_64/discord-rpc.dll differ
diff --git a/libs/drmingw/include/exchndl.h b/libs/drmingw/include/exchndl.h
new file mode 100644
index 000000000..37dafb88e
--- /dev/null
+++ b/libs/drmingw/include/exchndl.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2015 Jose Fonseca
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#pragma once
+
+
+#include
+
+
+// Set the unhandled exception handler.
+// Must be called when exchndll.dll is statically loaded (as opposed to loaded
+// dynamically via LoadLibrary)
+EXTERN_C VOID APIENTRY
+ExcHndlInit(void);
+
+
+// Override the report file name.
+//
+// Default is prog_name.RPT, in the same directory as the main executable.
+//
+// You can also pass "-" for stderr.
+EXTERN_C BOOL APIENTRY
+ExcHndlSetLogFileNameA(const char *szLogFileName);
diff --git a/libs/drmingw/lib/win32/libexchndl.a b/libs/drmingw/lib/win32/libexchndl.a
new file mode 100644
index 000000000..cd462a0df
Binary files /dev/null and b/libs/drmingw/lib/win32/libexchndl.a differ
diff --git a/libs/drmingw/lib/win32/libmgwhelp.a b/libs/drmingw/lib/win32/libmgwhelp.a
new file mode 100644
index 000000000..330cb52fe
Binary files /dev/null and b/libs/drmingw/lib/win32/libmgwhelp.a differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b04068609..87c4b6a65 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -103,7 +103,6 @@ set(SRB2_CORE_HEADERS
m_swap.h
md5.h
mserv.h
- http-mserv.h
p5prof.h
s_sound.h
screen.h
@@ -253,6 +252,8 @@ set(SRB2_CONFIG_HAVE_ZLIB ON CACHE BOOL
"Enable zlib support.")
set(SRB2_CONFIG_HAVE_GME ON CACHE BOOL
"Enable GME support.")
+set(SRB2_CONFIG_HAVE_DISCORDRPC OFF CACHE BOOL
+ "Enable Discord rich presence support.")
set(SRB2_CONFIG_HAVE_CURL ON CACHE BOOL
"Enable cURL support, used for downloading files via HTTP.")
set(SRB2_CONFIG_HAVE_OPENMPT ON CACHE BOOL
@@ -369,7 +370,7 @@ if(${SRB2_CONFIG_HAVE_GME})
if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
set(GME_FOUND ON)
set(GME_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/gme/include)
- if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
set(GME_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/gme/win64 -lgme")
else() # 32-bit
set(GME_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/gme/win32 -lgme")
@@ -429,6 +430,32 @@ if(${SRB2_CONFIG_HAVE_MIXERX})
endif()
endif()
+if(${SRB2_CONFIG_HAVE_DISCORDRPC})
+ if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ set(DISCORDRPC_FOUND ON)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ set(DISCORDRPC_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/discord-rpc/win64-dynamic/include)
+ set(DISCORDRPC_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/discord-rpc/win64-dynamic/lib -ldiscord-rpc")
+ else() # 32-bit
+ set(DISCORDRPC_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/discord-rpc/win32-dynamic/include)
+ set(DISCORDRPC_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/discord-rpc/win32-dynamic/lib -ldiscord-rpc")
+ endif()
+ else()
+ find_package(DiscordRPC)
+ endif()
+ if(${DISCORDRPC_FOUND})
+ set(SRB2_HAVE_DISCORDRPC ON)
+ add_definitions(-DHAVE_DISCORDRPC)
+ set(SRB2_DISCORDRPC_SOURCES discord.c)
+ set(SRB2_DISCORDRPC_HEADERS discord.h)
+ prepend_sources(SRB2_DISCORDRPC_SOURCES)
+ prepend_sources(SRB2_DISCORDRPC_HEADERS)
+ source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES} ${SRB2_DISCORDRPC_HEADERS})
+ else()
+ message(WARNING "You have specified that Discord Rich Presence is available but it was not found.")
+ endif()
+endif()
+
if(${SRB2_CONFIG_HAVE_ZLIB})
if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
set(ZLIB_FOUND ON)
@@ -481,7 +508,7 @@ endif()
if(${SRB2_CONFIG_HAVE_CURL})
if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
set(CURL_FOUND ON)
- set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl)
+ set(CURL_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/curl/include)
if(${SRB2_SYSTEM_BITS} EQUAL 64)
set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib64 -lcurl")
else() # 32-bit
diff --git a/src/Makefile b/src/Makefile
index 67bb8e605..6342e937f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -57,10 +57,10 @@
# Compile a profile version, add 'PROFILEMODE=1'
# Compile a debug version, add 'DEBUGMODE=1'
# Compile for the testers group (they don't get to play unless we're watching *wink*), add 'TESTERS=1'
-# Compile with extra warnings, add 'WARNINGMODE=1'
+# Compile with less warnings, add 'RELAXWARNINGS=1'
+# Generate compiler errors for most compiler warnings, add 'ERRORMODE=1'
# Compile without NASM's tmap.nas, add 'NOASM=1'
# Compile without 3D hardware support, add 'NOHW=1'
-# Compile without 3D sound support, add 'NOHS=1'
# Compile with GDBstubs, add 'RDB=1'
# Compile without PNG, add 'NOPNG=1'
# Compile without zlib, add 'NOZLIB=1'
@@ -81,6 +81,58 @@
#
#############################################################################
+ALL_SYSTEMS=\
+ PANDORA\
+ LINUX64\
+ MINGW64\
+ HAIKU\
+ DUMMY\
+ DJGPPDOS\
+ MINGW\
+ UNIX\
+ LINUX\
+ SOLARIS\
+ FREEBSD\
+ MACOSX\
+ SDL\
+
+# check for user specified system
+ifeq (,$(filter $(ALL_SYSTEMS),$(.VARIABLES)))
+ifeq ($(OS),Windows_NT) # all windows are Windows_NT...
+
+ $(info Detected a Windows system, compiling for 32-bit MinGW SDL2...)
+
+ # go for a 32-bit sdl mingw exe by default
+ MINGW=1
+ SDL=1
+ WINDOWSHELL=1
+
+else # if you on the *nix
+
+ system:=$(shell uname -s)
+
+ ifeq ($(system),Linux)
+ new_system=LINUX
+ else
+
+ $(error \
+ Could not automatically detect your system,\
+ try specifying a system manually)
+
+ endif
+
+ ifeq ($(shell getconf LONG_BIT),64)
+ system+=64-bit
+ new_system:=$(new_system)64
+ endif
+
+ $(info Detected $(system) ($(new_system))...)
+ $(new_system)=1
+
+endif
+endif
+
+
# SRB2 data files
D_DIR?=../bin/Resources
D_FILES=$(D_DIR)/main.kart \
@@ -124,7 +176,6 @@ NOPNG=1
NOZLIB=1
NONET=1
NOHW=1
-NOHS=1
NOASM=1
NOIPX=1
EXENAME?=srb2dummy
@@ -146,7 +197,6 @@ endif
ifdef PANDORA
NONX86=1
NOHW=1
-NOHS=1
endif
ifndef NOOPENMPT
@@ -228,13 +278,6 @@ else
$(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o
endif
-ifdef NOHS
- OPTS+=-DNOHS
-else
- OPTS+=-DHW3SOUND
- OBJS+=$(OBJDIR)/hw3sound.o
-endif
-
OPTS += -DCOMPVERSION
ifndef NONX86
@@ -348,6 +391,12 @@ CFLAGS+=-DHAVE_MINIUPNPC
endif
endif
+ifdef HAVE_DISCORDRPC
+LIBS+=-ldiscord-rpc
+CFLAGS+=-DHAVE_DISCORDRPC
+OBJS+=$(OBJDIR)/discord.o
+endif
+
include blua/Makefile.cfg
ifdef NOMD5
@@ -780,19 +829,6 @@ $(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \
$(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@
endif
-ifndef NOHS
-$(OBJDIR)/s_ds3d.o: hardware/s_ds3d/s_ds3d.c hardware/hw3dsdrv.h \
- hardware/hw_dll.h
- $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_ds3d.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_ds3d/s_ds3d.c
-
-$(OBJDIR)/s_fmod.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \
- hardware/hw_dll.h
- $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_fmod.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_fmod/s_fmod.c
-
-$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \
- hardware/hw_dll.h
- $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_openal.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_openal/s_openal.c
-endif
endif
endif
diff --git a/src/Makefile.cfg b/src/Makefile.cfg
index bf98dab38..3e011be5a 100644
--- a/src/Makefile.cfg
+++ b/src/Makefile.cfg
@@ -210,10 +210,7 @@ WFLAGS=-Wall
ifndef GCC295
#WFLAGS+=-Wno-packed
endif
-ifdef ERRORMODE
-WARNINGMODE=1
-endif
-ifdef WARNINGMODE
+ifndef RELAXWARNINGS
WFLAGS+=-W
#WFLAGS+=-Wno-sign-compare
ifndef GCC295
diff --git a/src/blua/lvm.c b/src/blua/lvm.c
index b654613f4..46a015c1e 100644
--- a/src/blua/lvm.c
+++ b/src/blua/lvm.c
@@ -322,8 +322,8 @@ static void Arith (lua_State *L, StkId ra, TValue *rb,
case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;
case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;
case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;
- case TM_DIV: if (nc == 0) { lua_pushliteral(L, "divide by zero error"); lua_error(L); } else setnvalue(ra, luai_numdiv(nb, nc)); break;
- case TM_MOD: if (nc == 0) { lua_pushliteral(L, "modulo by zero error"); lua_error(L); } else setnvalue(ra, luai_nummod(nb, nc)); break;
+ case TM_DIV: if (nc == 0) { luaG_runerror(L, "divide by zero error"); } else setnvalue(ra, luai_numdiv(nb, nc)); break;
+ case TM_MOD: if (nc == 0) { luaG_runerror(L, "modulo by zero error"); } else setnvalue(ra, luai_nummod(nb, nc)); break;
case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;
case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;
case TM_AND: setnvalue(ra, luai_numand(nb, nc)); break;
@@ -492,8 +492,7 @@ void luaV_execute (lua_State *L, int nexeccalls) {
if (ttisnumber(rb) && ttisnumber(rc)) {
lua_Number nb = nvalue(rb), nc = nvalue(rc);
if (nc == 0) {
- lua_pushliteral(L, "divide by zero error");
- lua_error(L);
+ luaG_runerror(L, "divide by zero error");
}
else
setnvalue(ra, luai_numdiv(nb, nc));
@@ -508,8 +507,7 @@ void luaV_execute (lua_State *L, int nexeccalls) {
if (ttisnumber(rb) && ttisnumber(rc)) {
lua_Number nb = nvalue(rb), nc = nvalue(rc);
if (nc == 0) {
- lua_pushliteral(L, "modulo by zero error");
- lua_error(L);
+ luaG_runerror(L, "modulo by zero error");
}
else
setnvalue(ra, luai_nummod(nb, nc));
diff --git a/src/config.h.in b/src/config.h.in
index b42ebc5b6..1636a6731 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -29,7 +29,7 @@
/* Manually defined asset hashes for non-CMake builds
* Last updated 2019 / 01 / 18 - Kart v1.0.2 - Main assets
- * Last updated 2020 / 05 / 09 - Kart v1.2 - patch.kart
+ * Last updated 2020 / 08 / 30 - Kart v1.3 - patch.kart
*/
#define ASSET_HASH_MAIN_KART "00000000000000000000000000000000"
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 657e95681..bbf115124 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -53,6 +53,7 @@
#include "k_bot.h"
#include "k_grandprix.h"
#include "doomstat.h"
+#include "s_sound.h" // sfx_syfail
#ifndef NONET
// cl loading screen
@@ -60,6 +61,10 @@
#include "f_finale.h"
#endif
+#ifdef HAVE_DISCORDRPC
+#include "discord.h"
+#endif
+
//
// NETWORKING
//
@@ -153,6 +158,9 @@ char connectedservername[MAXSERVERNAME];
/// \todo WORK!
boolean acceptnewnode = true;
+boolean serverisfull = false; //lets us be aware if the server was full after we check files, but before downloading, so we can ask if the user still wants to download or not
+tic_t firstconnectattempttime = 0;
+
// engine
// Must be a power of two
@@ -1101,13 +1109,16 @@ static INT16 Consistancy(void);
typedef enum
{
CL_SEARCHING,
+ CL_CHECKFILES,
CL_DOWNLOADFILES,
CL_ASKJOIN,
+ CL_LOADFILES,
CL_WAITJOINRESPONSE,
CL_DOWNLOADSAVEGAME,
CL_CONNECTED,
CL_ABORTED,
CL_ASKFULLFILELIST,
+ CL_CONFIRMCONNECT,
#ifdef HAVE_CURL
CL_PREPAREHTTPFILES,
CL_DOWNLOADHTTPFILES,
@@ -1173,11 +1184,7 @@ static inline void CL_DrawConnectionStatus(void)
if (!menuactive) // menu already draws its own fade
V_DrawFadeScreen(0xFF00, 16); // force default
- // Draw the bottom box.
- M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1);
- V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort");
-
- if (cl_mode != CL_DOWNLOADFILES
+ if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES
#ifdef HAVE_CURL
&& cl_mode != CL_DOWNLOADHTTPFILES
#endif
@@ -1188,9 +1195,12 @@ static inline void CL_DrawConnectionStatus(void)
// 15 pal entries total.
const char *cltext;
- if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1))
- for (i = 0; i < 16; ++i)
- V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15));
+ // Draw bottom box
+ M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort");
+
+ for (i = 0; i < 16; ++i)
+ V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
switch (cl_mode)
{
@@ -1220,11 +1230,22 @@ static inline void CL_DrawConnectionStatus(void)
cltext = M_GetText("Waiting to download game state...");
break;
case CL_ASKFULLFILELIST:
- cltext = M_GetText("This server has a LOT of files!");
+ case CL_CHECKFILES:
+ cltext = M_GetText("Checking server addon list ...");
+ break;
+ case CL_CONFIRMCONNECT:
+ cltext = "";
+ break;
+ case CL_LOADFILES:
+ cltext = M_GetText("Loading server addons...");
break;
case CL_ASKJOIN:
case CL_WAITJOINRESPONSE:
- cltext = M_GetText("Requesting to join...");
+ if (serverisfull)
+ cltext = M_GetText("Server full, waiting for a slot...");
+ else
+ cltext = M_GetText("Requesting to join...");
+
break;
#ifdef HAVE_CURL
case CL_PREPAREHTTPFILES:
@@ -1239,19 +1260,48 @@ static inline void CL_DrawConnectionStatus(void)
}
else
{
- if (lastfilenum != -1)
+ if (cl_mode == CL_LOADFILES)
+ {
+ INT32 totalfileslength;
+ INT32 loadcompletednum = 0;
+ INT32 i;
+
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort");
+
+ //ima just count files here
+ for (i = 0; i < fileneedednum; i++)
+ if (fileneeded[i].status == FS_OPEN)
+ loadcompletednum++;
+
+ // Loading progress
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, "Loading server addons...");
+ totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256);
+ M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va(" %2u/%2u Files",loadcompletednum,fileneedednum));
+ }
+ else if (lastfilenum != -1)
{
INT32 dldlength;
+ INT32 totalfileslength;
+ UINT32 totaldldsize;
static char tempname[28];
fileneeded_t *file = &fileneeded[lastfilenum];
char *filename = file->filename;
+ // Draw the bottom box.
+ M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-58-8, 32, 1);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-14, V_YELLOWMAP, "Press ESC to abort");
+
Net_GetNetStat();
dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256);
if (dldlength > 256)
dldlength = 256;
- V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111);
- V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, dldlength, 8, 96);
+
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, 256, 8, 111);
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, dldlength, 8, 96);
memset(tempname, 0, sizeof(tempname));
// offset filename to just the name only part
@@ -1269,16 +1319,52 @@ static inline void CL_DrawConnectionStatus(void)
strncpy(tempname, filename, sizeof(tempname)-1);
}
- V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-58-22, V_YELLOWMAP,
va(M_GetText("Downloading \"%s\""), tempname));
- V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
+ V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,file->totalsize>>10));
- V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE,
+ V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-58, V_20TRANS|V_MONOSPACE,
va("%3.1fK/s ", ((double)getbps)/1024));
+
+ // Download progress
+
+ if (fileneeded[lastfilenum].currentsize != fileneeded[lastfilenum].totalsize)
+ totaldldsize = downloadcompletedsize+fileneeded[lastfilenum].currentsize; //Add in single file progress download if applicable
+ else
+ totaldldsize = downloadcompletedsize;
+
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-14, V_YELLOWMAP, "Overall Download Progress");
+ totalfileslength = (INT32)((totaldldsize/(double)totalfilesrequestedsize) * 256);
+ M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, totalfileslength, 8, 160);
+
+ if (totalfilesrequestedsize>>20 >= 100) //display in MB if over 100MB
+ V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va(" %4uM/%4uM",totaldldsize>>20,totalfilesrequestedsize>>20));
+ else
+ V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va(" %4uK/%4uK",totaldldsize>>10,totalfilesrequestedsize>>10));
+
+ V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va("%2u/%2u Files ",downloadcompletednum,totalfilesrequestednum));
}
else
- V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP,
+ {
+ INT32 i, animtime = ((ccstime / 4) & 15) + 16;
+ UINT8 palstart = (cl_mode == CL_SEARCHING) ? 128 : 160;
+ // 15 pal entries total.
+
+ //Draw bottom box
+ M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort");
+
+ for (i = 0; i < 16; ++i)
+ V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
+
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP,
M_GetText("Waiting to download files..."));
+ }
}
}
@@ -1362,13 +1448,75 @@ static INT32 FindRejoinerNum(SINT8 node)
return -1;
}
+static void
+CopyCaretColors (char *p, const char *s, int n)
+{
+ char *t;
+ int m;
+ int c;
+ if (!n)
+ return;
+ while (( t = strchr(s, '^') ))
+ {
+ m = ( t - s );
+
+ if (m >= n)
+ {
+ memcpy(p, s, n);
+ return;
+ }
+ else
+ memcpy(p, s, m);
+
+ p += m;
+ n -= m;
+ s += m;
+
+ if (!n)
+ return;
+
+ if (s[1])
+ {
+ c = toupper(s[1]);
+ if (isdigit(c))
+ c = 0x80 + ( c - '0' );
+ else if (c >= 'A' && c <= 'F')
+ c = 0x80 + ( c - 'A' );
+ else
+ c = 0;
+
+ if (c)
+ {
+ *p++ = c;
+ n--;
+
+ if (!n)
+ return;
+ }
+ else
+ {
+ if (n < 2)
+ break;
+
+ memcpy(p, s, 2);
+
+ p += 2;
+ n -= 2;
+ }
+
+ s += 2;
+ }
+ else
+ break;
+ }
+ strncpy(p, s, n);
+}
+
static void SV_SendServerInfo(INT32 node, tic_t servertime)
{
UINT8 *p;
-#ifdef HAVE_CURL
size_t mirror_length;
const char *httpurl = cv_httpsource.string;
-#endif
netbuffer->packettype = PT_SERVERINFO;
netbuffer->u.serverinfo._255 = 255;
@@ -1403,8 +1551,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
(dedicated ? SV_DEDICATED : 0)
);
-
- strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
+ CopyCaretColors(netbuffer->u.serverinfo.servername, cv_servername.string,
MAXSERVERNAME);
strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
@@ -1428,8 +1575,8 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
"%s",
mapheaderinfo[gamemap-1]->lvlttl) < 0)
{
- // If there's an encoding error, send UNKNOWN, we accept that the above may be truncated
- strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", sizeof netbuffer->u.serverinfo.maptitle);
+ // If there's an encoding error, send "Unknown", we accept that the above may be truncated
+ strncpy(netbuffer->u.serverinfo.maptitle, "Unknown", sizeof netbuffer->u.serverinfo.maptitle);
}
}
else
@@ -1439,19 +1586,18 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
"%s %s",
mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl) < 0)
{
- // If there's an encoding error, send UNKNOWN, we accept that the above may be truncated
- strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", sizeof netbuffer->u.serverinfo.maptitle);
+ // If there's an encoding error, send "Unknown", we accept that the above may be truncated
+ strncpy(netbuffer->u.serverinfo.maptitle, "Unknown", sizeof netbuffer->u.serverinfo.maptitle);
}
}
}
else
- strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", sizeof netbuffer->u.serverinfo.maptitle);
+ strncpy(netbuffer->u.serverinfo.maptitle, "Unknown", sizeof netbuffer->u.serverinfo.maptitle);
netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
memset(netbuffer->u.serverinfo.httpsource, 0, MAX_MIRROR_LENGTH);
-#ifdef HAVE_CURL
mirror_length = strlen(httpurl);
if (mirror_length > MAX_MIRROR_LENGTH)
mirror_length = MAX_MIRROR_LENGTH;
@@ -1461,7 +1607,6 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
strncpy(netbuffer->u.serverinfo.httpsource, "", mirror_length);
netbuffer->u.serverinfo.httpsource[MAX_MIRROR_LENGTH-1] = '\0';
-#endif
p = PutFileNeeded(0);
@@ -1602,6 +1747,10 @@ static boolean SV_SendServerConfig(INT32 node)
netbuffer->u.servercfg.playerisbot[i] = players[i].bot;
}
+ netbuffer->u.servercfg.maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value));
+ netbuffer->u.servercfg.allownewplayer = cv_allownewplayer.value;
+ netbuffer->u.servercfg.discordinvites = (boolean)cv_discordinvites.value;
+
memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
op = p = netbuffer->u.servercfg.varlengthinputs;
@@ -1801,7 +1950,7 @@ static void CL_LoadReceivedSavegame(void)
if (strlen(mapheaderinfo[gamemap-1]->zonttl) > 0)
CON_LogMessage(va(" %s", mapheaderinfo[gamemap-1]->zonttl));
else if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
- CON_LogMessage(M_GetText(" ZONE"));
+ CON_LogMessage(M_GetText(" Zone"));
if (mapheaderinfo[gamemap-1]->actnum > 0)
CON_LogMessage(va(" %d", mapheaderinfo[gamemap-1]->actnum));
}
@@ -1905,57 +2054,30 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
M_SortServerList();
}
-#ifdef HAVE_THREADS
-struct Fetch_servers_ctx
+void CL_UpdateServerList (void)
{
- int room;
- int id;
-};
+ SL_ClearServerList(0);
-static void
-Fetch_servers_thread (struct Fetch_servers_ctx *ctx)
-{
- msg_server_t *server_list;
-
- server_list = GetShortServersList(ctx->room, ctx->id);
-
- if (server_list)
+ if (!netgame && I_NetOpenSocket)
{
- I_lock_mutex(&ms_QueryId_mutex);
+ if (I_NetOpenSocket())
{
- if (ctx->id != ms_QueryId)
- {
- free(server_list);
- server_list = NULL;
- }
- }
- I_unlock_mutex(ms_QueryId_mutex);
-
- if (server_list)
- {
- I_lock_mutex(&m_menu_mutex);
- {
- if (m_waiting_mode == M_WAITING_SERVERS)
- m_waiting_mode = M_NOT_WAITING;
- }
- I_unlock_mutex(m_menu_mutex);
-
- I_lock_mutex(&ms_ServerList_mutex);
- {
- ms_ServerList = server_list;
- }
- I_unlock_mutex(ms_ServerList_mutex);
+ netgame = true;
+ multiplayer = true;
}
}
- free(ctx);
+ // search for local servers
+ if (netgame)
+ SendAskInfo(BROADCASTADDR);
}
-#endif/*HAVE_THREADS*/
void CL_QueryServerList (msg_server_t *server_list)
{
INT32 i;
+ CL_UpdateServerList();
+
for (i = 0; server_list[i].header.buffer[0]; i++)
{
// Make sure MS version matches our own, to
@@ -1984,67 +2106,58 @@ void CL_QueryServerList (msg_server_t *server_list)
}
}
}
-
-void CL_UpdateServerList(boolean internetsearch, INT32 room)
-{
-#ifdef HAVE_THREADS
- struct Fetch_servers_ctx *ctx;
-#endif
-
- SL_ClearServerList(0);
-
- if (!netgame && I_NetOpenSocket)
- {
- if (I_NetOpenSocket())
- {
- netgame = true;
- multiplayer = true;
- }
- }
-
- // search for local servers
- if (netgame)
- SendAskInfo(BROADCASTADDR);
-
- if (internetsearch)
- {
-#ifdef HAVE_THREADS
- ctx = malloc(sizeof *ctx);
-
- /* This called from M_Refresh so I don't use a mutex */
- m_waiting_mode = M_WAITING_SERVERS;
-
- I_lock_mutex(&ms_QueryId_mutex);
- {
- ctx->id = ms_QueryId;
- }
- I_unlock_mutex(ms_QueryId_mutex);
-
- ctx->room = room;
-
- I_spawn_thread("fetch-servers", (I_thread_fn)Fetch_servers_thread, ctx);
-#else
- msg_server_t *server_list;
-
- server_list = GetShortServersList(room, 0);
-
- if (server_list)
- {
- CL_QueryServerList(server_list);
- free(server_list);
- }
-#endif
- }
-}
-
#endif // ifndef NONET
+static void M_ConfirmConnect(event_t *ev)
+{
+#ifndef NONET
+ if (ev->type == ev_keydown)
+ {
+ if (ev->data1 == ' ' || ev->data1 == 'y' || ev->data1 == KEY_ENTER || ev->data1 == gamecontrol[0][gc_accelerate][0] || ev->data1 == gamecontrol[0][gc_accelerate][1])
+ {
+ if (totalfilesrequestednum > 0)
+ {
+#ifdef HAVE_CURL
+ if (http_source[0] == '\0' || curl_failedwebdownload)
+#endif
+ {
+ if (CL_SendFileRequest())
+ {
+ cl_mode = CL_DOWNLOADFILES;
+ }
+ }
+#ifdef HAVE_CURL
+ else
+ cl_mode = CL_PREPAREHTTPFILES;
+#endif
+ }
+ else
+ cl_mode = CL_LOADFILES;
+
+ M_ClearMenus(true);
+ }
+ else if (ev->data1 == 'n' || ev->data1 == KEY_ESCAPE|| ev->data1 == gamecontrol[0][gc_brake][0] || ev->data1 == gamecontrol[0][gc_brake][1])
+ {
+ cl_mode = CL_ABORTED;
+ M_ClearMenus(true);
+ }
+ }
+#else
+ (void)ev;
+#endif
+}
+
static boolean CL_FinishedFileList(void)
{
INT32 i;
- CONS_Printf(M_GetText("Checking files...\n"));
+ char *downloadsize = NULL;
+ //CONS_Printf(M_GetText("Checking files...\n"));
i = CL_CheckFiles();
- if (i == 3) // too many files
+ if (i == 4) // still checking ...
+ {
+ return true;
+ }
+ else if (i == 3) // too many files
{
D_QuitNetGame();
CL_Reset();
@@ -2073,7 +2186,21 @@ static boolean CL_FinishedFileList(void)
return false;
}
else if (i == 1)
- cl_mode = CL_ASKJOIN;
+ {
+ if (serverisfull)
+ {
+ M_StartMessage(M_GetText(
+ "This server is full!\n"
+ "\n"
+ "You may load server addons (if any), and wait for a slot.\n"
+ "\n"
+ "Press ACCEL to continue or BRAKE to cancel.\n\n"
+ ), M_ConfirmConnect, MM_EVENTHANDLER);
+ cl_mode = CL_CONFIRMCONNECT;
+ }
+ else
+ cl_mode = CL_LOADFILES;
+ }
else
{
// must download something
@@ -2098,14 +2225,61 @@ static boolean CL_FinishedFileList(void)
), NULL, MM_NOTHING);
return false;
}
+ }
- if (CL_SendFileRequest())
- cl_mode = CL_DOWNLOADFILES;
+#ifdef HAVE_CURL
+ if (!curl_failedwebdownload)
+#endif
+ {
+#ifndef NONET
+ downloadcompletednum = 0;
+ downloadcompletedsize = 0;
+ totalfilesrequestednum = 0;
+ totalfilesrequestedsize = 0;
+#endif
+
+ for (i = 0; i < fileneedednum; i++)
+ if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)
+ {
+#ifndef NONET
+ totalfilesrequestednum++;
+ totalfilesrequestedsize += fileneeded[i].totalsize;
+#endif
+ }
+
+#ifndef NONET
+ if (totalfilesrequestedsize>>20 >= 100)
+ downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20));
+ else
+ downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10));
+#endif
+
+ if (serverisfull)
+ M_StartMessage(va(M_GetText(
+ "This server is full!\n"
+ "Download of %s additional content is required to join.\n"
+ "\n"
+ "You may download, load server addons, and wait for a slot.\n"
+ "\n"
+ "Press ACCEL to continue or BRAKE to cancel.\n\n"
+ ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER);
+ else
+ M_StartMessage(va(M_GetText(
+ "Download of %s additional content is required to join.\n"
+ "\n"
+ "Press ACCEL to continue or BRAKE to cancel.\n\n"
+ ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER);
+
+ Z_Free(downloadsize);
+ cl_mode = CL_CONFIRMCONNECT;
}
#ifdef HAVE_CURL
else
{
- cl_mode = CL_PREPAREHTTPFILES;
+ if (CL_SendFileRequest())
+ {
+ cl_mode = CL_DOWNLOADFILES;
+ }
}
#endif
}
@@ -2145,16 +2319,7 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
// Quit here rather than downloading files and being refused later.
if (serverlist[i].info.refusereason)
{
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
- if (serverlist[i].info.refusereason == 1)
- M_StartMessage(M_GetText("The server is not accepting\njoins for the moment.\n\nPress ESC\n"), NULL, MM_NOTHING);
- else if (serverlist[i].info.refusereason == 2)
- M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING);
- else
- M_StartMessage(M_GetText("You can't join.\nI don't know why,\nbut you can't join.\n\nPress ESC\n"), NULL, MM_NOTHING);
- return false;
+ serverisfull = true;
}
if (client)
@@ -2168,7 +2333,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
if (serverlist[i].info.httpsource[0])
CONS_Printf("We received a http url from the server, however it will not be used as this build lacks curl support (%s)\n", serverlist[i].info.httpsource);
#endif
-
D_ParseFileneeded(serverlist[i].info.fileneedednum, serverlist[i].info.fileneeded, 0);
if (serverlist[i].info.kartvars & SV_LOTSOFADDONS)
{
@@ -2177,8 +2341,7 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
return true;
}
- if (!CL_FinishedFileList())
- return false;
+ cl_mode = CL_CHECKFILES;
}
else
cl_mode = CL_ASKJOIN; // files need not be checked for the server.
@@ -2187,13 +2350,12 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
}
// Ask the info to the server (askinfo packet)
- if (*asksent + NEWTICRATE < I_GetTime())
+ if (I_GetTime() >= *asksent)
{
SendAskInfo(servernode);
- *asksent = I_GetTime();
+ *asksent = I_GetTime() + NEWTICRATE;
}
#else
- (void)viams;
(void)asksent;
// No netgames, so we skip this state.
cl_mode = CL_ASKJOIN;
@@ -2216,7 +2378,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
{
boolean waitmore;
INT32 i;
-
+
#ifdef NONET
(void)tmpsave;
#endif
@@ -2230,28 +2392,30 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
case CL_ASKFULLFILELIST:
if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved
- {
- if (!CL_FinishedFileList())
- return false;
- }
- else if (fileneedednum != cl_lastcheckedfilecount || *asksent + NEWTICRATE < I_GetTime())
+ cl_mode = CL_CHECKFILES;
+ else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent)
{
if (CL_AskFileList(fileneedednum))
{
cl_lastcheckedfilecount = fileneedednum;
- *asksent = I_GetTime();
+ *asksent = I_GetTime() + NEWTICRATE;
}
}
break;
-
+ case CL_CHECKFILES:
+ if (!CL_FinishedFileList())
+ return false;
+ break;
#ifdef HAVE_CURL
case CL_PREPAREHTTPFILES:
if (http_source[0])
{
for (i = 0; i < fileneedednum; i++)
if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)
+ {
curl_transfers++;
-
+ }
+
cl_mode = CL_DOWNLOADHTTPFILES;
}
break;
@@ -2275,19 +2439,13 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
if (curl_failedwebdownload && !curl_transfers)
{
- if (!CL_FinishedFileList())
- break;
-
CONS_Printf("One or more files failed to download, falling back to internal downloader\n");
- if (CL_SendFileRequest())
- {
- cl_mode = CL_DOWNLOADFILES;
- break;
- }
+ cl_mode = CL_CHECKFILES;
+ break;
}
if (!curl_transfers)
- cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now
+ cl_mode = CL_LOADFILES;
break;
#endif
@@ -2303,22 +2461,48 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
if (waitmore)
break; // exit the case
- cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now
- /* FALLTHRU */
-
+ cl_mode = CL_LOADFILES;
+ break;
+ case CL_LOADFILES:
+ if (CL_LoadServerFiles())
+ {
+ *asksent = 0; //This ensure the first join ask is right away
+ firstconnectattempttime = I_GetTime();
+ cl_mode = CL_ASKJOIN;
+ }
+ break;
case CL_ASKJOIN:
- CL_LoadServerFiles();
-#ifndef NONET
+ if (firstconnectattempttime + NEWTICRATE*300 < I_GetTime() && !server)
+ {
+ CONS_Printf(M_GetText("5 minute wait time exceeded.\n"));
+ CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+ M_StartMessage(M_GetText(
+ "5 minute wait time exceeded.\n"
+ "You may retry connection.\n"
+ "\n"
+ "Press ESC\n"
+ ), NULL, MM_NOTHING);
+ return false;
+ }
// prepare structures to save the file
// WARNING: this can be useless in case of server not in GS_LEVEL
// but since the network layer doesn't provide ordered packets...
CL_PrepareDownloadSaveGame(tmpsave);
-#endif
- if (CL_SendJoin())
+ if (I_GetTime() >= *asksent && CL_SendJoin())
+ {
+ *asksent = I_GetTime() + NEWTICRATE*3;
cl_mode = CL_WAITJOINRESPONSE;
+ }
+ break;
+ case CL_WAITJOINRESPONSE:
+ if (I_GetTime() >= *asksent)
+ {
+ cl_mode = CL_ASKJOIN;
+ }
break;
-
-#ifndef NONET
case CL_DOWNLOADSAVEGAME:
// At this state, the first (and only) needed file is the gamestate
if (fileneeded[0].status == FS_FOUND)
@@ -2326,13 +2510,12 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
// Gamestate is now handled within CL_LoadReceivedSavegame()
CL_LoadReceivedSavegame();
cl_mode = CL_CONNECTED;
+ break;
} // don't break case continue to CL_CONNECTED
else
break;
-#endif
-
- case CL_WAITJOINRESPONSE:
case CL_CONNECTED:
+ case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect
default:
break;
@@ -2353,7 +2536,10 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
G_MapEventsToControls(&events[eventtail]);
- if (gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1])
+ if (cl_mode == CL_CONFIRMCONNECT)
+ D_ProcessEvents(); //needed for menu system to receive inputs
+
+ if ((gamekeydown[KEY_ESCAPE] || gamekeydown[KEY_JOY1+1]) || cl_mode == CL_ABORTED)
{
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
@@ -2384,6 +2570,13 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
F_TitleScreenDrawer();
}
CL_DrawConnectionStatus();
+#ifdef HAVE_THREADS
+ I_lock_mutex(&m_menu_mutex);
+#endif
+ M_Drawer(); //Needed for drawing messageboxes on the connection screen
+#ifdef HAVE_THREADS
+ I_unlock_mutex(m_menu_mutex);
+#endif
I_UpdateNoVsync(); // page flip or blit buffer
if (moviemode)
M_SaveFrame();
@@ -2447,9 +2640,10 @@ static void CL_ConnectToServer(void)
ClearAdminPlayers();
K_ClearClientPowerLevels();
pnumnodes = 1;
- oldtic = I_GetTime() - 1;
+ oldtic = 0;
#ifndef NONET
- asksent = (tic_t) - TICRATE;
+ asksent = 0;
+ firstconnectattempttime = I_GetTime();
i = SL_SearchServer(servernode);
@@ -2814,6 +3008,8 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
// Reset the name
sprintf(player_names[playernum], "Player %d", playernum+1);
+ player_name_changes[playernum] = 0;
+
if (IsPlayerAdmin(playernum))
{
RemoveAdminPlayer(playernum); // don't stay admin after you're gone
@@ -2859,6 +3055,14 @@ void CL_Reset(void)
fileneedednum = 0;
memset(fileneeded, 0, sizeof(fileneeded));
+#ifndef NONET
+ totalfilesrequestednum = 0;
+ totalfilesrequestedsize = 0;
+#endif
+ firstconnectattempttime = 0;
+ serverisfull = false;
+ connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack
+
#ifdef HAVE_CURL
curl_failedwebdownload = false;
curl_transfers = 0;
@@ -3203,6 +3407,11 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
#endif
}
+ if (msg == KICK_MSG_PLAYER_QUIT)
+ S_StartSound(NULL, sfx_leave); // intended leave
+ else
+ S_StartSound(NULL, sfx_syfail); // he he he
+
switch (msg)
{
case KICK_MSG_GO_AWAY:
@@ -3366,18 +3575,26 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
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 };
+static void Joinable_OnChange(void);
+
+consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|CV_CALL, CV_OnOff, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
#ifdef VANILLAJOINNEXTROUND
consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
#endif
+
static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}};
-consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE|CV_CALL, maxplayers_cons_t, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
consvar_t cv_joindelay = {"joindelay", "10", CV_SAVE, joindelay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}};
consvar_t cv_rejointimeout = {"rejointimeout", "Off", CV_SAVE|CV_FLOAT, rejointimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+// Here for dedicated servers
+static CV_PossibleValue_t discordinvites_cons_t[] = {{0, "Admins Only"}, {1, "Everyone"}, {0, NULL}};
+consvar_t cv_discordinvites = {"discordinvites", "Everyone", CV_SAVE|CV_CALL, discordinvites_cons_t, Joinable_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}};
consvar_t cv_resynchattempts = {"resynchattempts", "5", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
@@ -3396,6 +3613,24 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum);
static void Got_RemovePlayer(UINT8 **p, INT32 playernum);
static void Got_AddBot(UINT8 **p, INT32 playernum);
+static void Joinable_OnChange(void)
+{
+ UINT8 buf[3];
+ UINT8 *p = buf;
+ UINT8 maxplayer;
+
+ if (!server)
+ return;
+
+ maxplayer = (UINT8)(min((dedicated ? MAXPLAYERS-1 : MAXPLAYERS), cv_maxplayers.value));
+
+ WRITEUINT8(p, maxplayer);
+ WRITEUINT8(p, cv_allownewplayer.value);
+ WRITEUINT8(p, cv_discordinvites.value);
+
+ SendNetXCmd(XD_DISCORD, &buf, 3);
+}
+
// called one time at init
void D_ClientServerInit(void)
{
@@ -3487,6 +3722,7 @@ void SV_ResetServer(void)
}
memset(splitscreen_partied, 0, sizeof splitscreen_partied);
+ memset(player_name_changes, 0, sizeof player_name_changes);
mynode = 0;
cl_packetmissed = false;
@@ -3553,8 +3789,10 @@ void D_QuitNetGame(void)
for (i = 0; i < MAXNETNODES; i++)
if (nodeingame[i])
HSendPacket(i, true, 0, 0);
- if (serverrunning && ms_RoomId > 0)
+#ifdef MASTERSERVER
+ if (serverrunning && cv_advertise.value)
UnregisterServer();
+#endif
}
else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode])
{
@@ -3719,6 +3957,9 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)"));
strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum));
+ if (node != mynode)
+ S_StartSound(NULL, sfx_join);
+
// Merge join notification + IP to avoid clogging console/chat
if (server && cv_showjoinaddress.value && I_GetNodeAddress)
{
@@ -3735,6 +3976,10 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
if (!rejoined)
LUAh_PlayerJoin(newplayernum);
+
+#ifdef HAVE_DISCORDRPC
+ DRPC_UpdatePresence();
+#endif
}
// Xcmd XD_REMOVEPLAYER
@@ -3757,6 +4002,10 @@ static void Got_RemovePlayer(UINT8 **p, INT32 playernum)
reason = READUINT8(*p);
CL_RemovePlayer(pnum, reason);
+
+#ifdef HAVE_DISCORDRPC
+ DRPC_UpdatePresence();
+#endif
}
// Xcmd XD_ADDBOT
@@ -3950,8 +4199,10 @@ boolean SV_SpawnServer(void)
if (netgame && I_NetOpenSocket)
{
I_NetOpenSocket();
- if (ms_RoomId > 0)
+#ifdef MASTERSERVER
+ if (cv_advertise.value)
RegisterServer();
+#endif
}
// non dedicated server just connect to itself
@@ -3981,7 +4232,7 @@ void SV_StopServer(void)
D_Clearticcmd(i);
consoleplayer = 0;
- cl_mode = CL_SEARCHING;
+ cl_mode = CL_ABORTED;
maketic = gametic+1;
neededtic = maketic;
serverrunning = false;
@@ -4015,7 +4266,7 @@ static void SV_SendRefuse(INT32 node, const char *reason)
strcpy(netbuffer->u.serverrefuse.reason, reason);
netbuffer->packettype = PT_SERVERREFUSE;
- HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
+ HSendPacket(node, false, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
Net_CloseConnection(node);
}
@@ -4187,6 +4438,7 @@ static void HandleTimeout(SINT8 node)
*/
static void HandleServerInfo(SINT8 node)
{
+ char servername[MAXSERVERNAME];
// compute ping in ms
const tic_t ticnow = I_GetTime();
const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time);
@@ -4197,6 +4449,8 @@ static void HandleServerInfo(SINT8 node)
[sizeof netbuffer->u.serverinfo.application - 1] = '\0';
netbuffer->u.serverinfo.gametypename
[sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0';
+ memcpy(servername, netbuffer->u.serverinfo.servername, MAXSERVERNAME);
+ CopyCaretColors(netbuffer->u.serverinfo.servername, servername, MAXSERVERNAME);
SL_InsertServer(&netbuffer->u.serverinfo, node);
}
@@ -4309,13 +4563,24 @@ static void HandlePacketFromAwayNode(SINT8 node)
if (!reason)
I_Error("Out of memory!\n");
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
+ if (strstr(reason, "Maximum players reached"))
+ {
+ serverisfull = true;
+ //Special timeout for when refusing due to player cap. The client will wait 3 seconds between join requests when waiting for a slot, so we need this to be much longer
+ //We set it back to the value of cv_nettimeout.value in CL_Reset
+ connectiontimeout = NEWTICRATE*7;
+ cl_mode = CL_ASKJOIN;
+ free(reason);
+ break;
+ }
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
reason), NULL, MM_NOTHING);
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+
free(reason);
// Will be reset by caller. Signals refusal.
@@ -4335,7 +4600,7 @@ static void HandlePacketFromAwayNode(SINT8 node)
}
SERVERONLY
/// \note how would this happen? and is it doing the right thing if it does?
- if (cl_mode != CL_WAITJOINRESPONSE)
+ if (!(cl_mode == CL_WAITJOINRESPONSE || cl_mode == CL_ASKJOIN))
break;
if (client)
@@ -4369,6 +4634,12 @@ static void HandlePacketFromAwayNode(SINT8 node)
memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
}
+#ifdef HAVE_DISCORDRPC
+ discordInfo.maxPlayers = netbuffer->u.servercfg.maxplayer;
+ discordInfo.joinsAllowed = netbuffer->u.servercfg.allownewplayer;
+ discordInfo.everyoneCanInvite = netbuffer->u.servercfg.discordinvites;
+#endif
+
nodeingame[(UINT8)servernode] = true;
serverplayer = netbuffer->u.servercfg.serverplayer;
doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
@@ -5768,7 +6039,9 @@ void NetKeepAlive(void)
GetPackets();
+#ifdef MASTERSERVER
MasterClient_Ticker();
+#endif
if (client)
{
@@ -5825,7 +6098,9 @@ void NetUpdate(void)
// client send the command after a receive of the server
// the server send before because in single player is beter
+#ifdef MASTERSERVER
MasterClient_Ticker(); // Acking the Master Server
+#endif
if (client)
{
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 4a3c5baa6..2f1caa82e 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -367,6 +367,11 @@ typedef struct
char server_context[8]; // Unique context id, generated at server startup.
+ // Discord info (always defined for net compatibility)
+ UINT8 maxplayer;
+ boolean allownewplayer;
+ boolean discordinvites;
+
UINT8 varlengthinputs[0]; // Playernames and netvars
} ATTRPACK serverconfig_pak;
@@ -592,6 +597,10 @@ typedef enum
} kickreason_t;
+/* the max number of name changes in some time period */
+#define MAXNAMECHANGES (5)
+#define NAMECHANGERATE (60*TICRATE)
+
extern boolean server;
extern boolean serverrunning;
#define client (!server)
@@ -619,6 +628,8 @@ extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;
extern consvar_t cv_joinnextround;
#endif
+extern consvar_t cv_discordinvites;
+
// Used in d_net, the only dependence
tic_t ExpandTics(INT32 low, tic_t basetic);
void D_ClientServerInit(void);
@@ -643,7 +654,7 @@ void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum);
void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
void CL_QueryServerList(msg_server_t *list);
-void CL_UpdateServerList(boolean internetsearch, INT32 room);
+void CL_UpdateServerList(void);
// Is there a game running
boolean Playing(void);
diff --git a/src/d_main.c b/src/d_main.c
index e3c334d92..bc46fc0a3 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -59,7 +59,6 @@
#include "m_cheat.h"
#include "y_inter.h"
#include "p_local.h" // chasecam
-#include "mserv.h" // ms_RoomId
#include "m_misc.h" // screenshot functionality
#include "dehacked.h" // Dehacked list test
#include "m_cond.h" // condition initialization
@@ -96,6 +95,10 @@
int VERSION;
int SUBVERSION;
+#ifdef HAVE_DISCORDRPC
+#include "discord.h"
+#endif
+
// platform independant focus loss
UINT8 window_notinfocus = false;
@@ -901,6 +904,13 @@ void D_SRB2Loop(void)
#endif
LUA_Step();
+
+#ifdef HAVE_DISCORDRPC
+ if (! dedicated)
+ {
+ Discord_RunCallbacks();
+ }
+#endif
}
}
@@ -1078,9 +1088,23 @@ static void ChangeDirForUrlHandler(void)
// Identify the SRB2 version, and IWAD file to use.
// ==========================================================================
+static boolean AddIWAD(void)
+{
+ char * path = va(pandf,srb2path,"main.kart");
+
+ if (FIL_ReadFileOK(path))
+ {
+ D_AddFile(path, startupiwads);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
static void IdentifyVersion(void)
{
- char *mainresource;
const char *srb2waddir = NULL;
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
@@ -1098,36 +1122,19 @@ static void IdentifyVersion(void)
if (getcwd(srb2path, 256) != NULL)
srb2waddir = srb2path;
else
- {
srb2waddir = ".";
- }
}
-#if defined (macintosh) && !defined (HAVE_SDL)
- // cwd is always "/" when app is dbl-clicked
- if (!stricmp(srb2waddir, "/"))
- srb2waddir = I_GetWadDir();
-#endif
- // Commercial.
- mainresource = malloc(strlen(srb2waddir)+1+9+1);
- if (mainresource == NULL)
- I_Error("No more free memory to look in %s", srb2waddir);
- else
- sprintf(mainresource, pandf, srb2waddir, "main.kart");
+ // Load the IWAD
+ if (! AddIWAD())
+ {
+ I_Error("MAIN.KART not found! Expected in %s\n", srb2waddir);
+ }
// will be overwritten in case of -cdrom or unix/win home
snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir);
configfile[sizeof configfile - 1] = '\0';
- // Load the IWAD
- if (mainresource != NULL && FIL_ReadFileOK(mainresource))
- D_AddFile(mainresource, startupiwads);
- else
- I_Error("main.kart not found! Expected in %s, ss file: %s \n", srb2waddir, mainresource);
-
- if (mainresource)
- free(mainresource);
-
// if you change the ordering of this or add/remove a file, be sure to update the md5
// checking in D_SRB2Main
@@ -1662,17 +1669,6 @@ void D_SRB2Main(void)
CONS_Printf("ST_Init(): Init status bar.\n");
ST_Init();
- if (M_CheckParm("-room"))
- {
- if (!M_IsNextParm())
- I_Error("usage: -room \nCheck the Master Server's webpage for room ID numbers.\n");
- ms_RoomId = atoi(M_GetNextParm());
-
-#ifdef UPDATE_ALERT
- GetMODVersion_Console();
-#endif
- }
-
// Set up splitscreen players before joining!
if (!dedicated && (M_CheckParm("-splitscreen") && M_IsNextParm()))
{
@@ -1890,6 +1886,13 @@ void D_SRB2Main(void)
if (!P_LoadLevel(false))
I_Quit(); // fail so reset game stuff
}
+
+#ifdef HAVE_DISCORDRPC
+ if (! dedicated)
+ {
+ DRPC_Init();
+ }
+#endif
}
const char *D_Home(void)
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 65a9db6f5..a93e86ac0 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -64,6 +64,10 @@
#define CV_RESTRICT 0
#endif
+#ifdef HAVE_DISCORDRPC
+#include "discord.h"
+#endif
+
// ------
// protos
// ------
@@ -89,6 +93,7 @@ static void Got_RandomSeed(UINT8 **cp, INT32 playernum);
static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum);
static void Got_Teamchange(UINT8 **cp, INT32 playernum);
static void Got_Clearscores(UINT8 **cp, INT32 playernum);
+static void Got_DiscordInfo(UINT8 **cp, INT32 playernum);
static void PointLimit_OnChange(void);
static void TimeLimit_OnChange(void);
@@ -748,10 +753,13 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_showviewpointtext);
#ifdef SEENAMES
- CV_RegisterVar(&cv_allowseenames);
+ CV_RegisterVar(&cv_allowseenames);
#endif
CV_RegisterVar(&cv_dummyconsvar);
+
+ CV_RegisterVar(&cv_discordinvites);
+ RegisterNetXCmd(XD_DISCORD, Got_DiscordInfo);
}
// =========================================================================
@@ -1038,6 +1046,12 @@ void D_RegisterClientCommands(void)
#ifdef LUA_ALLOW_BYTECODE
COM_AddCommand("dumplua", Command_Dumplua_f);
#endif
+
+#ifdef HAVE_DISCORDRPC
+ CV_RegisterVar(&cv_discordrp);
+ CV_RegisterVar(&cv_discordstreamer);
+ CV_RegisterVar(&cv_discordasks);
+#endif
}
/** Checks if a name (as received from another player) is okay.
@@ -1255,6 +1269,8 @@ static void SetPlayerName(INT32 playernum, char *newname)
if (netgame)
HU_AddChatText(va("\x82*%s renamed to %s", player_names[playernum], newname), false);
+ player_name_changes[playernum]++;
+
strcpy(player_names[playernum], newname);
demo_extradata[playernum] |= DXD_NAME;
}
@@ -1455,7 +1471,12 @@ static void SendNameAndColor(UINT8 n)
snacpending[n]++;
// Don't change name if muted
- if (cv_mute.value && !(server || IsPlayerAdmin(playernum)))
+ if (player_name_changes[playernum] >= MAXNAMECHANGES)
+ {
+ CV_StealthSet(&cv_playername[n], player_names[playernum]);
+ HU_AddChatText("\x85*You must wait to change your name again", false);
+ }
+ else if (cv_mute.value && !(server || IsPlayerAdmin(playernum)))
CV_StealthSet(&cv_playername[n], player_names[playernum]);
else // Cleanup name if changing it
CleanupPlayerName(playernum, cv_playername[n].zstring);
@@ -1491,6 +1512,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
UINT16 color, followercolor;
UINT8 skin;
SINT8 follower;
+ SINT8 localplayer = -1;
UINT8 i;
#ifdef PARANOIA
@@ -1509,7 +1531,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
I_Error("snacpending[%d] negative!", i);
#endif
- // i is now player screen id
+ localplayer = i;
break;
}
}
@@ -1590,8 +1612,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
const INT32 forcedskin = cv_forceskin.value;
SetPlayerSkinByNum(playernum, forcedskin);
- if (playernum == g_localplayers[i])
- CV_StealthSet(&cv_skin[i], skins[forcedskin].name);
+ if (localplayer != -1)
+ CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name);
}
else
SetPlayerSkinByNum(playernum, skin);
@@ -1602,6 +1624,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
// set follower
SetFollower(playernum, follower);
+
+#ifdef HAVE_DISCORDRPC
+ if (playernum == consoleplayer)
+ DRPC_UpdatePresence();
+#endif
}
void SendWeaponPref(UINT8 n)
@@ -2865,6 +2892,10 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
if (demo.recording) // Okay, level loaded, character spawned and skinned,
G_BeginRecording(); // I AM NOW READY TO RECORD.
demo.deferstart = true;
+
+#ifdef HAVE_DISCORDRPC
+ DRPC_UpdatePresence();
+#endif
}
static void Command_Pause(void)
@@ -3799,7 +3830,7 @@ static void Command_RunSOC(void)
static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
{
char filename[256];
- filestatus_t ncs = FS_NOTFOUND;
+ filestatus_t ncs = FS_NOTCHECKED;
if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
@@ -3937,7 +3968,7 @@ static void Command_Addfile(void)
static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
{
char filename[241];
- filestatus_t ncs = FS_NOTFOUND;
+ filestatus_t ncs = FS_NOTCHECKED;
UINT8 md5sum[16];
boolean kick = false;
boolean toomany = false;
@@ -3996,7 +4027,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
{
char filename[241];
- filestatus_t ncs = FS_NOTFOUND;
+ filestatus_t ncs = FS_NOTCHECKED;
UINT8 md5sum[16];
READSTRINGN(*cp, filename, 240);
@@ -4289,6 +4320,10 @@ static void TimeLimit_OnChange(void)
}
else if (netgame || multiplayer)
CONS_Printf(M_GetText("Time limit disabled\n"));
+
+#ifdef HAVE_DISCORDRPC
+ DRPC_UpdatePresence();
+#endif
}
/** Adjusts certain settings to match a changed gametype.
@@ -5603,3 +5638,26 @@ static void KartEliminateLast_OnChange(void)
P_CheckRacers();
}
+
+void Got_DiscordInfo(UINT8 **p, INT32 playernum)
+{
+ if (playernum != serverplayer /*&& !IsPlayerAdmin(playernum)*/)
+ {
+ // protect against hacked/buggy client
+ CONS_Alert(CONS_WARNING, M_GetText("Illegal Discord info command received from %s\n"), player_names[playernum]);
+ if (server)
+ SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY);
+ return;
+ }
+
+ // Don't do anything with the information if we don't have Discord RP support
+#ifdef HAVE_DISCORDRPC
+ discordInfo.maxPlayers = READUINT8(*p);
+ discordInfo.joinsAllowed = (boolean)READUINT8(*p);
+ discordInfo.everyoneCanInvite = (boolean)READUINT8(*p);
+
+ DRPC_UpdatePresence();
+#else
+ (*p) += 3;
+#endif
+}
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 30a6e845b..e7c7ad206 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -161,6 +161,7 @@ typedef enum
XD_CANCELPARTYINVITE, // 31
XD_GIVEITEM, // 32
XD_ADDBOT, // 33
+ XD_DISCORD, // 34
MAXNETXCMD
} netxcmd_t;
diff --git a/src/d_netfil.c b/src/d_netfil.c
index 66b0ff650..5ca99508a 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -118,6 +118,10 @@ static pauseddownload_t *pauseddownload = NULL;
#ifndef NONET
// for cl loading screen
INT32 lastfilenum = -1;
+INT32 downloadcompletednum = 0;
+UINT32 downloadcompletedsize = 0;
+INT32 totalfilesrequestednum = 0;
+UINT32 totalfilesrequestedsize = 0;
#endif
#ifdef HAVE_CURL
@@ -156,7 +160,7 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
char wadfilename[MAX_WADPATH] = "";
UINT8 filestatus;
- for (i = mainwads; i < numwadfiles; i++)
+ for (i = mainwads+1; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad
{
// If it has only music/sound lumps, don't put it in the list
if (!wadfiles[i]->important)
@@ -222,7 +226,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi
p = (UINT8 *)fileneededstr;
for (i = firstfile; i < fileneedednum; i++)
{
- fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
+ fileneeded[i].status = FS_NOTCHECKED; // We haven't even started looking for the file yet
fileneeded[i].justdownloaded = false;
filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].willsend = (UINT8)(filestatus >> 4);
@@ -416,15 +420,17 @@ boolean PT_RequestFile(INT32 node)
* \return 0 if some files are missing
* 1 if all files exist
* 2 if some already loaded files are not requested or are in a different order
+ * 3 too many files, over WADLIMIT
+ * 4 still checking, continuing next tic
*
*/
INT32 CL_CheckFiles(void)
{
INT32 i, j;
char wadfilename[MAX_WADPATH];
- INT32 ret = 1;
size_t packetsize = 0;
- size_t filestoget = 0;
+ size_t filestoload = 0;
+ boolean downloadrequired = false;
// if (M_CheckParm("-nofiles"))
// return 1;
@@ -441,7 +447,7 @@ INT32 CL_CheckFiles(void)
if (modifiedgame)
{
CONS_Debug(DBG_NETPLAY, "game is modified; only doing basic checks\n");
- for (i = 0, j = mainwads; i < fileneedednum || j < numwadfiles;)
+ for (i = 0, j = mainwads+1; i < fileneedednum || j < numwadfiles;)
{
if (j < numwadfiles && !wadfiles[j]->important)
{
@@ -470,10 +476,19 @@ INT32 CL_CheckFiles(void)
for (i = 0; i < fileneedednum; i++)
{
+ if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_FALLBACK)
+ downloadrequired = true;
+
+ if (fileneeded[i].status == FS_FOUND || fileneeded[i].status == FS_NOTFOUND)
+ filestoload++;
+
+ if (fileneeded[i].status != FS_NOTCHECKED) //since we're running this over multiple tics now, its possible for us to come across files checked in previous tics
+ continue;
+
CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
// Check in already loaded files
- for (j = mainwads; wadfiles[j]; j++)
+ for (j = mainwads+1; wadfiles[j]; j++)
{
nameonly(strcpy(wadfilename, wadfiles[j]->filename));
if (!stricmp(wadfilename, fileneeded[i].filename) &&
@@ -481,36 +496,35 @@ INT32 CL_CheckFiles(void)
{
CONS_Debug(DBG_NETPLAY, "already loaded\n");
fileneeded[i].status = FS_OPEN;
- break;
+ return 4;
}
}
- if (fileneeded[i].status != FS_NOTFOUND)
- continue;
packetsize += nameonlylength(fileneeded[i].filename) + 22;
- if (mainwads+filestoget >= MAX_WADFILES)
- return 3;
-
- filestoget++;
-
fileneeded[i].status = findfile(fileneeded[i].filename, fileneeded[i].md5sum, true);
CONS_Debug(DBG_NETPLAY, "found %d\n", fileneeded[i].status);
- if (fileneeded[i].status != FS_FOUND)
- ret = 0;
+ return 4;
}
- return ret;
+
+ //now making it here means we've checked the entire list and no FS_NOTCHECKED files remain
+ if (numwadfiles+filestoload > MAX_WADFILES)
+ return 3;
+ else if (downloadrequired)
+ return 0; //some stuff is FS_NOTFOUND, needs download
+ else
+ return 1; //everything is FS_OPEN or FS_FOUND, proceed to loading
}
// Load it now
-void CL_LoadServerFiles(void)
+boolean CL_LoadServerFiles(void)
{
INT32 i;
// if (M_CheckParm("-nofiles"))
// return;
- for (i = 1; i < fileneedednum; i++)
+ for (i = 0; i < fileneedednum; i++)
{
if (fileneeded[i].status == FS_OPEN)
continue; // Already loaded
@@ -519,6 +533,7 @@ void CL_LoadServerFiles(void)
P_AddWadFile(fileneeded[i].filename);
G_SetGameModified(true, false);
fileneeded[i].status = FS_OPEN;
+ return false;
}
else if (fileneeded[i].status == FS_MD5SUMBAD)
I_Error("Wrong version of file %s", fileneeded[i].filename);
@@ -544,6 +559,7 @@ void CL_LoadServerFiles(void)
fileneeded[i].status, s);
}
}
+ return true;
}
void AddLuaFileTransfer(const char *filename, const char *mode)
@@ -1374,6 +1390,11 @@ void PT_FileFragment(void)
netbuffer->packettype = PT_HASLUAFILE;
HSendPacket(servernode, true, 0, 0);
}
+
+#ifndef NONET
+ downloadcompletednum++;
+ downloadcompletedsize += file->totalsize;
+#endif
}
}
else // Already received
@@ -1732,6 +1753,8 @@ void CURLGetFile(void)
{
nameonly(curl_realname);
CONS_Printf(M_GetText("Finished downloading %s\n"), curl_realname);
+ downloadcompletednum++;
+ downloadcompletedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
fclose(curl_curfile->file);
}
diff --git a/src/d_netfil.h b/src/d_netfil.h
index b7f80bdc6..31f9bc507 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -27,6 +27,7 @@ typedef enum
typedef enum
{
+ FS_NOTCHECKED,
FS_NOTFOUND,
FS_FOUND,
FS_REQUESTED,
@@ -61,6 +62,10 @@ extern char downloaddir[512];
#ifndef NONET
extern INT32 lastfilenum;
+extern INT32 downloadcompletednum;
+extern UINT32 downloadcompletedsize;
+extern INT32 totalfilesrequestednum;
+extern UINT32 totalfilesrequestedsize;
#endif
#ifdef HAVE_CURL
@@ -74,7 +79,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi
void CL_PrepareDownloadSaveGame(const char *tmpsave);
INT32 CL_CheckFiles(void);
-void CL_LoadServerFiles(void);
+boolean CL_LoadServerFiles(void);
void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemethod,
UINT8 fileid);
diff --git a/src/dehacked.c b/src/dehacked.c
index 6925b02f1..392bfd5df 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -10362,7 +10362,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_TWINKLECARTAMBIENCE",
"MT_EXPLODINGBARREL",
"MT_MERRYHORSE",
- "MT_ARIDTOAD",
"MT_BLUEFRUIT",
"MT_ORANGEFRUIT",
"MT_REDFRUIT",
@@ -10373,6 +10372,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BOOSTPROMPT",
"MT_BOOSTOFF",
"MT_BOOSTON",
+ "MT_ARIDTOAD",
"MT_LIZARDMAN",
"MT_LIONMAN",
@@ -12689,8 +12689,6 @@ static inline int lib_getenum(lua_State *L)
if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word);
// DYNAMIC variables too!!
- // Try not to add anything that would break netgames or timeattack replays here.
- // You know, like consoleplayer, displayplayer, secondarydisplayplayer, or gametime.
return LUA_PushGlobals(L, word);
}
diff --git a/src/discord.c b/src/discord.c
new file mode 100644
index 000000000..fdbdcb415
--- /dev/null
+++ b/src/discord.c
@@ -0,0 +1,712 @@
+// SONIC ROBO BLAST 2 KART
+//-----------------------------------------------------------------------------
+// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour.
+// Copyright (C) 2018-2020 by Kart Krew.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file discord.h
+/// \brief Discord Rich Presence handling
+
+#ifdef HAVE_DISCORDRPC
+
+#ifdef HAVE_CURL
+#include
+#endif // HAVE_CURL
+
+#include "i_system.h"
+#include "d_clisrv.h"
+#include "d_netcmd.h"
+#include "i_net.h"
+#include "g_game.h"
+#include "p_tick.h"
+#include "m_menu.h" // gametype_cons_t
+#include "r_things.h" // skins
+#include "mserv.h" // cv_advertise
+#include "z_zone.h"
+#include "byteptr.h"
+
+#include "discord.h"
+#include "doomdef.h"
+
+// Feel free to provide your own, if you care enough to create another Discord app for this :P
+#define DISCORD_APPID "503531144395096085"
+
+// length of IP strings
+#define IP_SIZE 21
+
+consvar_t cv_discordrp = {"discordrp", "On", CV_SAVE|CV_CALL, CV_OnOff, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_discordstreamer = {"discordstreamer", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_discordasks = {"discordasks", "Yes", CV_SAVE|CV_CALL, CV_YesNo, DRPC_UpdatePresence, 0, NULL, NULL, 0, 0, NULL};
+
+struct discordInfo_s discordInfo;
+
+discordRequest_t *discordRequestList = NULL;
+
+#ifdef HAVE_CURL
+struct SelfIPbuffer
+{
+ CURL *curl;
+ char *pointer;
+ size_t length;
+};
+
+static char self_ip[IP_SIZE];
+#endif // HAVE_CURL
+
+/*--------------------------------------------------
+ static char *DRPC_XORIPString(const char *input)
+
+ Simple XOR encryption/decryption. Not complex or
+ very secretive because we aren't sending anything
+ that isn't easily accessible via our Master Server anyway.
+--------------------------------------------------*/
+static char *DRPC_XORIPString(const char *input)
+{
+ const UINT8 xor[IP_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
+ char *output = malloc(sizeof(char) * (IP_SIZE+1));
+ UINT8 i;
+
+ for (i = 0; i < IP_SIZE; i++)
+ {
+ char xorinput;
+
+ if (!input[i])
+ break;
+
+ xorinput = input[i] ^ xor[i];
+
+ if (xorinput < 32 || xorinput > 126)
+ {
+ xorinput = input[i];
+ }
+
+ output[i] = xorinput;
+ }
+
+ output[i] = '\0';
+
+ return output;
+}
+
+/*--------------------------------------------------
+ static void DRPC_HandleReady(const DiscordUser *user)
+
+ Callback function, ran when the game connects to Discord.
+
+ Input Arguments:-
+ user - Struct containing Discord user info.
+
+ Return:-
+ None
+--------------------------------------------------*/
+static void DRPC_HandleReady(const DiscordUser *user)
+{
+ if (cv_discordstreamer.value)
+ {
+ CONS_Printf("Discord: connected to %s\n", user->username);
+ }
+ else
+ {
+ CONS_Printf("Discord: connected to %s#%s (%s)\n", user->username, user->discriminator, user->userId);
+ }
+}
+
+/*--------------------------------------------------
+ static void DRPC_HandleDisconnect(int err, const char *msg)
+
+ Callback function, ran when disconnecting from Discord.
+
+ Input Arguments:-
+ err - Error type
+ msg - Error message
+
+ Return:-
+ None
+--------------------------------------------------*/
+static void DRPC_HandleDisconnect(int err, const char *msg)
+{
+ CONS_Printf("Discord: disconnected (%d: %s)\n", err, msg);
+}
+
+/*--------------------------------------------------
+ static void DRPC_HandleError(int err, const char *msg)
+
+ Callback function, ran when Discord outputs an error.
+
+ Input Arguments:-
+ err - Error type
+ msg - Error message
+
+ Return:-
+ None
+--------------------------------------------------*/
+static void DRPC_HandleError(int err, const char *msg)
+{
+ CONS_Alert(CONS_WARNING, "Discord error (%d: %s)\n", err, msg);
+}
+
+/*--------------------------------------------------
+ static void DRPC_HandleJoin(const char *secret)
+
+ Callback function, ran when Discord wants to
+ connect a player to the game via a channel invite
+ or a join request.
+
+ Input Arguments:-
+ secret - Value that links you to the server.
+
+ Return:-
+ None
+--------------------------------------------------*/
+static void DRPC_HandleJoin(const char *secret)
+{
+ char *ip = DRPC_XORIPString(secret);
+ CONS_Printf("Connecting to %s via Discord\n", ip);
+ COM_BufAddText(va("connect \"%s\"\n", ip));
+ free(ip);
+}
+
+/*--------------------------------------------------
+ static boolean DRPC_InvitesAreAllowed(void)
+
+ Determines whenever or not invites or
+ ask to join requests are allowed.
+
+ Input Arguments:-
+ None
+
+ Return:-
+ true if invites are allowed, false otherwise.
+--------------------------------------------------*/
+static boolean DRPC_InvitesAreAllowed(void)
+{
+ if (!Playing())
+ {
+ // We're not playing, so we should not be getting invites.
+ return false;
+ }
+
+ if (cv_discordasks.value == 0)
+ {
+ // Client has the CVar set to off, so never allow invites from this client.
+ return false;
+ }
+
+ if (discordInfo.joinsAllowed == true)
+ {
+ if (discordInfo.everyoneCanInvite == true)
+ {
+ // Everyone's allowed!
+ return true;
+ }
+ else if (consoleplayer == serverplayer || IsPlayerAdmin(consoleplayer))
+ {
+ // Only admins are allowed!
+ return true;
+ }
+ }
+
+ // Did not pass any of the checks
+ return false;
+}
+
+/*--------------------------------------------------
+ static void DRPC_HandleJoinRequest(const DiscordUser *requestUser)
+
+ Callback function, ran when Discord wants to
+ ask the player if another Discord user can join
+ or not.
+
+ Input Arguments:-
+ requestUser - DiscordUser struct for the user trying to connect.
+
+ Return:-
+ None
+--------------------------------------------------*/
+static void DRPC_HandleJoinRequest(const DiscordUser *requestUser)
+{
+ discordRequest_t *append = discordRequestList;
+ discordRequest_t *newRequest;
+
+ if (DRPC_InvitesAreAllowed() == false)
+ {
+ // Something weird happened if this occurred...
+ Discord_Respond(requestUser->userId, DISCORD_REPLY_IGNORE);
+ return;
+ }
+
+ newRequest = Z_Calloc(sizeof(discordRequest_t), PU_STATIC, NULL);
+
+ newRequest->username = Z_Calloc(344, PU_STATIC, NULL);
+ snprintf(newRequest->username, 344, "%s", requestUser->username);
+
+ newRequest->discriminator = Z_Calloc(8, PU_STATIC, NULL);
+ snprintf(newRequest->discriminator, 8, "%s", requestUser->discriminator);
+
+ newRequest->userID = Z_Calloc(32, PU_STATIC, NULL);
+ snprintf(newRequest->userID, 32, "%s", requestUser->userId);
+
+ if (append != NULL)
+ {
+ discordRequest_t *prev = NULL;
+
+ while (append != NULL)
+ {
+ // CHECK FOR DUPES!! Ignore any that already exist from the same user.
+ if (!strcmp(newRequest->userID, append->userID))
+ {
+ Discord_Respond(newRequest->userID, DISCORD_REPLY_IGNORE);
+ DRPC_RemoveRequest(newRequest);
+ return;
+ }
+
+ prev = append;
+ append = append->next;
+ }
+
+ newRequest->prev = prev;
+ prev->next = newRequest;
+ }
+ else
+ {
+ discordRequestList = newRequest;
+ M_RefreshPauseMenu();
+ }
+
+ // Made it to the end, request was valid, so play the request sound :)
+ S_StartSound(NULL, sfx_requst);
+}
+
+/*--------------------------------------------------
+ void DRPC_RemoveRequest(discordRequest_t *removeRequest)
+
+ See header file for description.
+--------------------------------------------------*/
+void DRPC_RemoveRequest(discordRequest_t *removeRequest)
+{
+ if (removeRequest->prev != NULL)
+ {
+ removeRequest->prev->next = removeRequest->next;
+ }
+
+ if (removeRequest->next != NULL)
+ {
+ removeRequest->next->prev = removeRequest->prev;
+
+ if (removeRequest == discordRequestList)
+ {
+ discordRequestList = removeRequest->next;
+ }
+ }
+ else
+ {
+ if (removeRequest == discordRequestList)
+ {
+ discordRequestList = NULL;
+ }
+ }
+
+ Z_Free(removeRequest->username);
+ Z_Free(removeRequest->userID);
+ Z_Free(removeRequest);
+}
+
+/*--------------------------------------------------
+ void DRPC_Init(void)
+
+ See header file for description.
+--------------------------------------------------*/
+void DRPC_Init(void)
+{
+ DiscordEventHandlers handlers;
+ memset(&handlers, 0, sizeof(handlers));
+
+ handlers.ready = DRPC_HandleReady;
+ handlers.disconnected = DRPC_HandleDisconnect;
+ handlers.errored = DRPC_HandleError;
+ handlers.joinGame = DRPC_HandleJoin;
+ handlers.joinRequest = DRPC_HandleJoinRequest;
+
+ Discord_Initialize(DISCORD_APPID, &handlers, 1, NULL);
+ I_AddExitFunc(Discord_Shutdown);
+ DRPC_UpdatePresence();
+}
+
+#ifdef HAVE_CURL
+/*--------------------------------------------------
+ static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata)
+
+ Writing function for use with curl. Only intended to be used with simple text.
+
+ Input Arguments:-
+ s - Data to write
+ size - Always 1.
+ n - Length of data
+ userdata - Passed in from CURLOPT_WRITEDATA, intended to be SelfIPbuffer
+
+ Return:-
+ Number of bytes wrote in this pass.
+--------------------------------------------------*/
+static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata)
+{
+ struct SelfIPbuffer *buffer;
+ size_t newlength;
+
+ buffer = userdata;
+
+ newlength = buffer->length + size*n;
+ buffer->pointer = realloc(buffer->pointer, newlength+1);
+
+ memcpy(buffer->pointer + buffer->length, s, size*n);
+
+ buffer->pointer[newlength] = '\0';
+ buffer->length = newlength;
+
+ return size*n;
+}
+#endif // HAVE_CURL
+
+/*--------------------------------------------------
+ static const char *DRPC_GetServerIP(void)
+
+ Retrieves the IP address of the server that you're
+ connected to. Will attempt to use curl for getting your
+ own IP address, if it's not yours.
+--------------------------------------------------*/
+static const char *DRPC_GetServerIP(void)
+{
+ const char *address;
+
+ // If you're connected
+ if (I_GetNodeAddress && (address = I_GetNodeAddress(servernode)) != NULL)
+ {
+ if (strcmp(address, "self"))
+ {
+ // We're not the server, so we could successfully get the IP!
+ // No need to do anything else :)
+ return address;
+ }
+ }
+
+#ifdef HAVE_CURL
+ // This is a little bit goofy, but
+ // there's practically no good way to get your own public IP address,
+ // so we've gotta break out curl for this :V
+ if (!self_ip[0])
+ {
+ CURL *curl;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+ curl = curl_easy_init();
+
+ if (curl)
+ {
+ // The API to get your public IP address from.
+ // Picked because it's stupid simple and it's been up for a long time.
+ const char *api = "http://ip4only.me/api/";
+
+ struct SelfIPbuffer buffer;
+ CURLcode success;
+
+ buffer.length = 0;
+ buffer.pointer = malloc(buffer.length+1);
+ buffer.pointer[0] = '\0';
+
+ curl_easy_setopt(curl, CURLOPT_URL, api);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DRPC_WriteServerIP);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
+
+ success = curl_easy_perform(curl);
+
+ if (success == CURLE_OK)
+ {
+ char *tmp;
+ tmp = strtok(buffer.pointer, ",");
+
+ if (!strcmp(tmp, "IPv4")) // ensure correct type of IP
+ {
+ tmp = strtok(NULL, ",");
+ strncpy(self_ip, tmp, IP_SIZE); // Yay, we have the IP :)
+ }
+ }
+
+ free(buffer.pointer);
+ curl_easy_cleanup(curl);
+ }
+
+ curl_global_cleanup();
+ }
+
+ if (self_ip[0])
+ return self_ip;
+ else
+#endif // HAVE_CURL
+ return NULL; // Could not get your IP for whatever reason, so we cannot do Discord invites
+}
+
+/*--------------------------------------------------
+ void DRPC_EmptyRequests(void)
+
+ Empties the request list. Any existing requests
+ will get an ignore reply.
+--------------------------------------------------*/
+static void DRPC_EmptyRequests(void)
+{
+ while (discordRequestList != NULL)
+ {
+ Discord_Respond(discordRequestList->userID, DISCORD_REPLY_IGNORE);
+ DRPC_RemoveRequest(discordRequestList);
+ }
+}
+
+/*--------------------------------------------------
+ void DRPC_UpdatePresence(void)
+
+ See header file for description.
+--------------------------------------------------*/
+void DRPC_UpdatePresence(void)
+{
+ char detailstr[48+1];
+
+ char mapimg[8+1];
+ char mapname[5+21+21+2+1];
+
+ char charimg[4+SKINNAMESIZE+1];
+ char charname[11+SKINNAMESIZE+1];
+
+ boolean joinSecretSet = false;
+
+ DiscordRichPresence discordPresence;
+ memset(&discordPresence, 0, sizeof(discordPresence));
+
+ if (dedicated)
+ {
+ return;
+ }
+
+ if (!cv_discordrp.value)
+ {
+ // User doesn't want to show their game information, so update with empty presence.
+ // This just shows that they're playing SRB2Kart. (If that's too much, then they should disable game activity :V)
+ DRPC_EmptyRequests();
+ Discord_UpdatePresence(&discordPresence);
+ return;
+ }
+
+#ifdef DEVELOP
+ // This way, we can use the invite feature in-dev, but not have snoopers seeing any potential secrets! :P
+ discordPresence.largeImageKey = "miscdevelop";
+ discordPresence.largeImageText = "No peeking!";
+ discordPresence.state = "Testing the game";
+
+ DRPC_EmptyRequests();
+ Discord_UpdatePresence(&discordPresence);
+ return;
+#endif // DEVELOP
+
+ // Server info
+ if (netgame)
+ {
+ if (cv_advertise.value)
+ {
+ discordPresence.state = "Public";
+ }
+ else
+ {
+ discordPresence.state = "Private";
+ }
+
+ discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field!
+ discordPresence.partySize = D_NumPlayers(); // Players in server
+ discordPresence.partyMax = discordInfo.maxPlayers; // Max players
+
+ if (DRPC_InvitesAreAllowed() == true)
+ {
+ const char *join;
+
+ // Grab the host's IP for joining.
+ if ((join = DRPC_GetServerIP()) != NULL)
+ {
+ char *xorjoin = DRPC_XORIPString(join);
+ discordPresence.joinSecret = xorjoin;
+ free(xorjoin);
+
+ joinSecretSet = true;
+ }
+ }
+ }
+ else
+ {
+ // Reset discord info if you're not in a place that uses it!
+ // Important for if you join a server that compiled without HAVE_DISCORDRPC,
+ // so that you don't ever end up using bad information from another server.
+ memset(&discordInfo, 0, sizeof(discordInfo));
+
+ // Offline info
+ if (Playing())
+ discordPresence.state = "Offline";
+ else if (demo.playback && !demo.title)
+ discordPresence.state = "Watching Replay";
+ else
+ discordPresence.state = "Menu";
+ }
+
+ // Gametype info
+ if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) && Playing())
+ {
+ if (modeattacking)
+ discordPresence.details = "Time Attack";
+ else
+ {
+ snprintf(detailstr, 48, "%s%s%s",
+ gametype_cons_t[gametype].strvalue,
+ (gametype == GT_RACE) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "",
+ (encoremode == true) ? " | Encore" : ""
+ );
+ discordPresence.details = detailstr;
+ }
+ }
+
+ if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info
+ && !(demo.playback && demo.title))
+ {
+ if ((gamemap >= 1 && gamemap <= 60) // supported race maps
+ || (gamemap >= 136 && gamemap <= 164)) // supported battle maps
+ {
+ snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap));
+ strlwr(mapimg);
+ discordPresence.largeImageKey = mapimg; // Map image
+ }
+ else if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU)
+ {
+ // Hell map, use the method that got you here :P
+ discordPresence.largeImageKey = "miscdice";
+ }
+ else
+ {
+ // This is probably a custom map!
+ discordPresence.largeImageKey = "mapcustom";
+ }
+
+ if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU)
+ {
+ // Hell map, hide the name
+ discordPresence.largeImageText = "Map: ???";
+ }
+ else
+ {
+ // Map name on tool tip
+ snprintf(mapname, 48, "Map: %s", G_BuildMapTitle(gamemap));
+ discordPresence.largeImageText = mapname;
+ }
+
+ if (gamestate == GS_LEVEL && Playing())
+ {
+ const time_t currentTime = time(NULL);
+ const time_t mapTimeStart = currentTime - ((leveltime + (modeattacking ? starttime : 0)) / TICRATE);
+
+ discordPresence.startTimestamp = mapTimeStart;
+
+ if (timelimitintics > 0)
+ {
+ const time_t mapTimeEnd = mapTimeStart + ((timelimitintics + starttime + 1) / TICRATE);
+ discordPresence.endTimestamp = mapTimeEnd;
+ }
+ }
+ }
+ else if (gamestate == GS_VOTING)
+ {
+ discordPresence.largeImageKey = ((gametype == GT_BATTLE) ? "miscredplanet" : "miscblueplanet");
+ discordPresence.largeImageText = "Voting";
+ }
+ else
+ {
+ discordPresence.largeImageKey = "misctitle";
+ discordPresence.largeImageText = "Title Screen";
+ }
+
+ // Character info
+ if (Playing() && playeringame[consoleplayer] && !players[consoleplayer].spectator)
+ {
+ // Supported skin names
+ static const char *supportedSkins[] = {
+ // base game
+ "sonic",
+ "tails",
+ "knuckles",
+ "eggman",
+ "metalsonic",
+ // bonus chars
+ "flicky",
+ "motobug",
+ "amy",
+ "mighty",
+ "ray",
+ "espio",
+ "vector",
+ "chao",
+ "gamma",
+ "chaos",
+ "shadow",
+ "rouge",
+ "herochao",
+ "darkchao",
+ "cream",
+ "omega",
+ "blaze",
+ "silver",
+ "wonderboy",
+ "arle",
+ "nights",
+ "sakura",
+ "ulala",
+ "beat",
+ "vyse",
+ "aiai",
+ "kiryu",
+ "aigis",
+ "miku",
+ "doom",
+ NULL
+ };
+
+ boolean customChar = true;
+ UINT8 checkSkin = 0;
+
+ // Character image
+ while (supportedSkins[checkSkin] != NULL)
+ {
+ if (!strcmp(skins[players[consoleplayer].skin].name, supportedSkins[checkSkin]))
+ {
+ snprintf(charimg, 21, "char%s", supportedSkins[checkSkin]);
+ discordPresence.smallImageKey = charimg;
+ customChar = false;
+ break;
+ }
+
+ checkSkin++;
+ }
+
+ if (customChar == true)
+ {
+ // Use the custom character icon!
+ discordPresence.smallImageKey = "charcustom";
+ }
+
+ snprintf(charname, 28, "Character: %s", skins[players[consoleplayer].skin].realname);
+ discordPresence.smallImageText = charname; // Character name
+ }
+
+ if (joinSecretSet == false)
+ {
+ // Not able to join? Flush the request list, if it exists.
+ DRPC_EmptyRequests();
+ }
+
+ Discord_UpdatePresence(&discordPresence);
+}
+
+#endif // HAVE_DISCORDRPC
diff --git a/src/discord.h b/src/discord.h
new file mode 100644
index 000000000..a6bb1134a
--- /dev/null
+++ b/src/discord.h
@@ -0,0 +1,80 @@
+// SONIC ROBO BLAST 2 KART
+//-----------------------------------------------------------------------------
+// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour.
+// Copyright (C) 2018-2020 by Kart Krew.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file discord.h
+/// \brief Discord Rich Presence handling
+
+#ifndef __DISCORD__
+#define __DISCORD__
+
+#ifdef HAVE_DISCORDRPC
+
+#include "discord_rpc.h"
+
+extern consvar_t cv_discordrp;
+extern consvar_t cv_discordstreamer;
+extern consvar_t cv_discordasks;
+
+extern struct discordInfo_s {
+ UINT8 maxPlayers;
+ boolean joinsAllowed;
+ boolean everyoneCanInvite;
+} discordInfo;
+
+typedef struct discordRequest_s {
+ char *username; // Discord user name.
+ char *discriminator; // Discord discriminator (The little hashtag thing after the username). Separated for a "hide discriminators" cvar.
+ char *userID; // The ID of the Discord user, gets used with Discord_Respond()
+
+ // HAHAHA, no.
+ // *Maybe* if it was only PNG I would boot up curl just to get AND convert this to Doom GFX,
+ // but it can *also* be a JEPG, WebP, or GIF :)
+ // Hey, wanna add ImageMagick as a dependency? :dying:
+ //patch_t *avatar;
+
+ struct discordRequest_s *next; // Next request in the list.
+ struct discordRequest_s *prev; // Previous request in the list. Not used normally, but just in case something funky happens, this should repair the list.
+} discordRequest_t;
+
+extern discordRequest_t *discordRequestList;
+
+
+/*--------------------------------------------------
+ void DRPC_RemoveRequest(void);
+
+ Removes an invite from the list.
+--------------------------------------------------*/
+
+void DRPC_RemoveRequest(discordRequest_t *removeRequest);
+
+
+/*--------------------------------------------------
+ void DRPC_Init(void);
+
+ Initalizes Discord Rich Presence by linking the Application ID
+ and setting the callback functions.
+--------------------------------------------------*/
+
+void DRPC_Init(void);
+
+
+/*--------------------------------------------------
+ void DRPC_UpdatePresence(void);
+
+ Updates what is displayed by Rich Presence on the user's profile.
+ Should be called whenever something that is displayed is
+ changed in-game.
+--------------------------------------------------*/
+
+void DRPC_UpdatePresence(void);
+
+
+#endif // HAVE_DISCORDRPC
+
+#endif // __DISCORD__
diff --git a/src/doomdata.h b/src/doomdata.h
index fde0a7d8e..ed01f42c6 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -220,4 +220,11 @@ typedef struct
#define NUMMAPS 1035
+/* slope thing types */
+enum
+{
+ FLOOR_SLOPE_THING = 777,
+ CEILING_SLOPE_THING = 778,
+};
+
#endif // __DOOMDATA__
diff --git a/src/doomdef.h b/src/doomdef.h
index f0347c716..b1960b507 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -29,7 +29,6 @@
// Use Mixer interface?
#ifdef HAVE_MIXER
#define SOUND SOUND_MIXER
- #define NOHS // No HW3SOUND
#ifdef HW3SOUND
#undef HW3SOUND
#endif
@@ -45,7 +44,6 @@
// Use FMOD?
#ifdef HAVE_FMOD
#define SOUND SOUND_FMOD
- #define NOHS // No HW3SOUND
#ifdef HW3SOUND
#undef HW3SOUND
#endif
@@ -62,10 +60,6 @@
#if !defined (HWRENDER) && !defined (NOHW)
#define HWRENDER
#endif
-// judgecutor: 3D sound support
-#if !defined(HW3SOUND) && !defined (NOHS)
-#define HW3SOUND
-#endif
#endif
#ifdef _WIN32
@@ -165,32 +159,13 @@ extern char logfilename[1024];
// Please change to apply to your modification (we don't want everyone asking where your mod is on SRB2.org!).
#define UPDATE_ALERT_STRING \
"A new update is available for SRB2Kart.\n"\
-"Please visit mb.srb2.org to download it.\n"\
+"Please visit kartkrew.org to download it.\n"\
"\n"\
"You are using version: %s\n"\
"The newest version is: %s\n"\
"\n"\
-"This update is required for online\n"\
-"play using the Master Server.\n"\
-"You will not be able to connect to\n"\
-"the Master Server until you update to\n"\
-"the newest version of the game.\n"\
-"\n"\
"(Press a key)\n"
-// The string used in the I_Error alert upon trying to host through command line parameters.
-// Generally less filled with newlines, since Windows gives you lots more room to work with.
-#define UPDATE_ALERT_STRING_CONSOLE \
-"A new update is available for SRB2Kart.\n"\
-"Please visit mb.srb2.org to download it.\n"\
-"\n"\
-"You are using version: %s\n"\
-"The newest version is: %s\n"\
-"\n"\
-"This update is required for online play using the Master Server.\n"\
-"You will not be able to connect to the Master Server\n"\
-"until you update to the newest version of the game.\n"
-
// For future use, the codebase is the version of SRB2 that the modification is based on,
// and should not be changed unless you have merged changes between versions of SRB2
// (such as 2.0.4 to 2.0.5, etc) into your working copy.
@@ -683,4 +658,10 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
/// MIDI support is really shitty -- we don't use it anyway, so lets throw it behind a define
#define NO_MIDI
+#if defined (HAVE_CURL) && ! defined (NONET)
+#define MASTERSERVER
+#else
+#undef UPDATE_ALERT
+#endif
+
#endif // __DOOMDEF__
diff --git a/src/g_game.c b/src/g_game.c
index b15013ded..187b7af23 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -58,6 +58,10 @@
#include "k_grandprix.h"
#include "doomstat.h"
+#ifdef HAVE_DISCORDRPC
+#include "discord.h"
+#endif
+
gameaction_t gameaction;
gamestate_t gamestate = GS_NULL;
UINT8 ultimatemode = false;
@@ -471,6 +475,7 @@ player_t *seenplayer; // player we're aiming at right now
// now automatically allocated in D_RegisterClientCommands
// so that it doesn't have to be updated depending on the value of MAXPLAYERS
char player_names[MAXPLAYERS][MAXPLAYERNAME+1];
+INT32 player_name_changes[MAXPLAYERS];
// Allocation for time and nights data
void G_AllocMainRecordData(INT16 i)
@@ -1957,6 +1962,11 @@ void G_Ticker(boolean run)
if (camtoggledelay[i])
camtoggledelay[i]--;
}
+
+ if (gametic % NAMECHANGERATE == 0)
+ {
+ memset(player_name_changes, 0, sizeof player_name_changes);
+ }
}
}
@@ -4744,6 +4754,9 @@ INT32 G_FindMapByNameOrCode(const char *mapname, char **realmapnamep)
void G_SetGamestate(gamestate_t newstate)
{
gamestate = newstate;
+#ifdef HAVE_DISCORDRPC
+ DRPC_UpdatePresence();
+#endif
}
/* These functions handle the exitgame flag. Before, when the user
diff --git a/src/g_game.h b/src/g_game.h
index 13376cd20..8407171df 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -27,7 +27,8 @@ extern char customversionstring[32];
#ifdef SEENAMES
extern player_t *seenplayer;
#endif
-extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1];
+extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1];
+extern INT32 player_name_changes[MAXPLAYERS];
extern player_t players[MAXPLAYERS];
extern boolean playeringame[MAXPLAYERS];
diff --git a/src/http-mserv.c b/src/http-mserv.c
index 47c2b5b0f..27a4e23f1 100644
--- a/src/http-mserv.c
+++ b/src/http-mserv.c
@@ -11,10 +11,12 @@
/*
Documentation available here.
-
+
*/
+#ifdef HAVE_CURL
#include
+#endif
#include "doomdef.h"
#include "d_clisrv.h"
@@ -49,6 +51,8 @@ consvar_t cv_masterserver_token = {
NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */
};
+#ifdef MASTERSERVER
+
static int hms_started;
static char *hms_api;
@@ -137,7 +141,7 @@ HMS_connect (const char *format, ...)
if (cv_masterserver_token.string[0])
{
quack_token = curl_easy_escape(curl, cv_masterserver_token.string, 0);
- token_length = ( sizeof "?token="-1 )+ strlen(quack_token);
+ token_length = ( sizeof "&token="-1 )+ strlen(quack_token);
}
else
{
@@ -152,7 +156,9 @@ HMS_connect (const char *format, ...)
seek = strlen(hms_api) + 1;/* + '/' */
va_start (ap, format);
- url = malloc(seek + vsnprintf(0, 0, format, ap) + token_length + 1);
+ url = malloc(seek + vsnprintf(0, 0, format, ap) +
+ sizeof "?v=2" - 1 +
+ token_length + 1);
va_end (ap);
sprintf(url, "%s/", hms_api);
@@ -165,8 +171,11 @@ HMS_connect (const char *format, ...)
seek += vsprintf(&url[seek], format, ap);
va_end (ap);
+ strcpy(&url[seek], "?v=2");
+ seek += sizeof "?v=2" - 1;
+
if (quack_token)
- sprintf(&url[seek], "?token=%s", quack_token);
+ sprintf(&url[seek], "&token=%s", quack_token);
CONS_Printf("HMS: connecting '%s'...\n", url);
@@ -254,117 +263,6 @@ HMS_end (struct HMS_buffer *buffer)
free(buffer);
}
-int
-HMS_fetch_rooms (int joining, int query_id)
-{
- struct HMS_buffer *hms;
- int ok;
-
- int doing_shit;
-
- char *id;
- char *title;
- char *room_motd;
-
- int id_no;
-
- char *p;
- char *end;
-
- int i;
-
- (void)query_id;
-
- hms = HMS_connect("rooms");
-
- if (! hms)
- return 0;
-
- if (HMS_do(hms))
- {
- doing_shit = 1;
-
- p = hms->buffer;
-
- for (i = 0; i < NUM_LIST_ROOMS && ( end = strstr(p, "\n\n\n") );)
- {
- *end = '\0';
-
- id = strtok(p, "\n");
- title = strtok(0, "\n");
- room_motd = strtok(0, "");
-
- if (id && title && room_motd)
- {
- id_no = atoi(id);
-
- /*
- Don't show the 'All' room if hosting. And it's a hack like this
- because I'm way too lazy to add another feature to the MS.
- */
- if (joining || id_no != 0)
- {
-#ifdef HAVE_THREADS
- I_lock_mutex(&ms_QueryId_mutex);
- {
- if (query_id != ms_QueryId)
- doing_shit = 0;
- }
- I_unlock_mutex(ms_QueryId_mutex);
-
- if (! doing_shit)
- break;
-#endif
-
- room_list[i].header.buffer[0] = 1;
-
- room_list[i].id = id_no;
- strlcpy(room_list[i].name, title, sizeof room_list[i].name);
- strlcpy(room_list[i].motd, room_motd, sizeof room_list[i].motd);
-
- i++;
- }
-
- p = ( end + 3 );/* skip the three linefeeds */
- }
- else
- break;
- }
-
- if (doing_shit)
- room_list[i].header.buffer[0] = 0;
-
- ok = 1;
-
- if (doing_shit)
- {
-#ifdef HAVE_THREADS
- I_lock_mutex(&m_menu_mutex);
-#endif
- {
- for (i = 0; room_list[i].header.buffer[0]; i++)
- {
- if(*room_list[i].name != '\0')
- {
- MP_RoomMenu[i+1].text = room_list[i].name;
- roomIds[i] = room_list[i].id;
- MP_RoomMenu[i+1].status = IT_STRING|IT_CALL;
- }
- }
- }
-#ifdef HAVE_THREADS
- I_unlock_mutex(m_menu_mutex);
-#endif
- }
- }
- else
- ok = 0;
-
- HMS_end(hms);
-
- return ok;
-}
-
int
HMS_register (void)
{
@@ -373,28 +271,26 @@ HMS_register (void)
char post[256];
- char *title;
+ char *contact;
- hms = HMS_connect("rooms/%d/register", ms_RoomId);
+ hms = HMS_connect(
+ "games/%s/%d/servers/register", SRB2APPLICATION, MODVERSION);
if (! hms)
return 0;
- title = curl_easy_escape(hms->curl, cv_servername.string, 0);
+ contact = curl_easy_escape(hms->curl, cv_server_contact.string, 0);
snprintf(post, sizeof post,
"port=%d&"
- "title=%s&"
- "version=%s",
+ "contact=%s",
current_port,
- title,
-
- SRB2VERSION
+ contact
);
- curl_free(title);
+ curl_free(contact);
curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post);
@@ -468,19 +364,13 @@ HMS_list_servers (void)
{
struct HMS_buffer *hms;
- char *p;
-
- hms = HMS_connect("servers");
+ hms = HMS_connect("games/%s/%d/servers", SRB2APPLICATION, MODVERSION);
if (! hms)
return;
if (HMS_do(hms))
{
- p = &hms->buffer[strlen(hms->buffer)];
- while (*--p == '\n')
- ;
-
CONS_Printf("%s\n", hms->buffer);
}
@@ -488,35 +378,24 @@ HMS_list_servers (void)
}
msg_server_t *
-HMS_fetch_servers (msg_server_t *list, int room_number, int query_id)
+HMS_fetch_servers (msg_server_t *list, int query_id)
{
struct HMS_buffer *hms;
int doing_shit;
- char local_version[9];
-
- char *room;
-
char *address;
char *port;
- char *title;
- char *version;
+ char *contact;
char *end;
- char *section_end;
char *p;
int i;
(void)query_id;
- if (room_number > 0)
- {
- hms = HMS_connect("rooms/%d/servers", room_number);
- }
- else
- hms = HMS_connect("servers");
+ hms = HMS_connect("games/%s/%d/servers", SRB2APPLICATION, MODVERSION);
if (! hms)
return NULL;
@@ -525,80 +404,51 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id)
{
doing_shit = 1;
- snprintf(local_version, sizeof local_version,
- "%s",
- SRB2VERSION
- );
-
p = hms->buffer;
i = 0;
- do
+ while (i < MAXSERVERLIST && ( end = strchr(p, '\n') ))
{
- section_end = strstr(p, "\n\n");
+ *end = '\0';
- room = strtok(p, "\n");
+ address = strtok(p, " ");
+ port = strtok(0, " ");
+ contact = strtok(0, "");
- p = strtok(0, "");
-
- if (! p)
- break;
-
- while (i < MAXSERVERLIST && ( end = strchr(p, '\n') ))
+ if (address && port)
{
- *end = '\0';
-
- address = strtok(p, " ");
- port = strtok(0, " ");
- title = strtok(0, " ");
- version = strtok(0, "");
-
- if (address && port && title && version)
- {
#ifdef HAVE_THREADS
- I_lock_mutex(&ms_QueryId_mutex);
- {
- if (query_id != ms_QueryId)
- doing_shit = 0;
- }
- I_unlock_mutex(ms_QueryId_mutex);
+ I_lock_mutex(&ms_QueryId_mutex);
+ {
+ if (query_id != ms_QueryId)
+ doing_shit = 0;
+ }
+ I_unlock_mutex(ms_QueryId_mutex);
- if (! doing_shit)
- break;
+ if (! doing_shit)
+ break;
#endif
- if (strcmp(version, local_version) == 0)
- {
- strlcpy(list[i].ip, address, sizeof list[i].ip);
- strlcpy(list[i].port, port, sizeof list[i].port);
- strlcpy(list[i].name, title, sizeof list[i].name);
- strlcpy(list[i].version, version, sizeof list[i].version);
+ strlcpy(list[i].ip, address, sizeof list[i].ip);
+ strlcpy(list[i].port, port, sizeof list[i].port);
- list[i].room = atoi(room);
-
- list[i].header.buffer[0] = 1;
-
- i++;
- }
-
- if (end == section_end)/* end of list for this room */
- break;
- else
- p = ( end + 1 );/* skip server delimiter */
- }
- else
+ if (contact)
{
- section_end = 0;/* malformed so quit the parsing */
- break;
+ strlcpy(list[i].contact, contact, sizeof list[i].contact);
}
+
+ list[i].header.buffer[0] = 1;
+
+ i++;
+
+ p = ( end + 1 );/* skip server delimiter */
}
-
- if (! doing_shit)
+ else
+ {
+ /* malformed so quit the parsing */
break;
-
- p = ( section_end + 2 );
+ }
}
- while (section_end) ;
if (doing_shit)
list[i].header.buffer[0] = 0;
@@ -620,7 +470,7 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size)
char *version;
char *version_name;
- hms = HMS_connect("versions/%d", MODID);
+ hms = HMS_connect("games/%s/version", SRB2APPLICATION);
if (! hms)
return 0;
@@ -649,6 +499,19 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size)
return ok;
}
+static char *
+Strip_trailing_slashes (char *api)
+{
+ char * p = &api[strlen(api)];
+
+ while (*--p == '/')
+ ;
+
+ p[1] = '\0';
+
+ return api;
+}
+
void
HMS_set_api (char *api)
{
@@ -657,17 +520,21 @@ HMS_set_api (char *api)
#endif
{
free(hms_api);
- hms_api = api;
+ hms_api = Strip_trailing_slashes(api);
}
#ifdef HAVE_THREADS
I_unlock_mutex(hms_api_mutex);
#endif
}
+#endif/*MASTERSERVER*/
+
static void
MasterServer_Debug_OnChange (void)
{
+#ifdef MASTERSERVER
/* TODO: change to 'latest-log.txt' for log files revision. */
if (cv_masterserver_debug.value)
CONS_Printf("Master server debug messages will appear in log.txt\n");
+#endif
}
diff --git a/src/k_kart.c b/src/k_kart.c
index 3dd61fa8c..4352ce5c5 100644
--- a/src/k_kart.c
+++ b/src/k_kart.c
@@ -306,35 +306,35 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
/*Jawz x2*/ { 0, 0, 1, 2, 0, 0, 0, 0 } // Jawz x2
};
-static INT32 K_KartItemOddsBattle[NUMKARTRESULTS-1][6] =
+static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
{
- //P-Odds 0 1 2 3 4 5
- /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker
- /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker
- /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility
- /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana
- /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor
- /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut
- /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz
- /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine
- /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog
- /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb
- /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow
- /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink
- /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield
- /*Bubble Shield*/ { 0, 0, 0, 0, 0, 0 }, // Bubble Shield
- /*Flame Shield*/ { 0, 0, 0, 0, 0, 0 }, // Flame Shield
- /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro
- /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring
- /*Super Ring*/ { 0, 0, 0, 0, 0, 0 }, // Super Ring
- /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
- /*Sneaker x2*/ { 0, 0, 0, 0, 0, 0 }, // Sneaker x2
- /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3
- /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3
- /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10
- /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3
- /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4
- /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2
+ //P-Odds 0 1
+ /*Sneaker*/ { 2, 1 }, // Sneaker
+ /*Rocket Sneaker*/ { 0, 0 }, // Rocket Sneaker
+ /*Invincibility*/ { 2, 1 }, // Invincibility
+ /*Banana*/ { 1, 0 }, // Banana
+ /*Eggman Monitor*/ { 1, 0 }, // Eggman Monitor
+ /*Orbinaut*/ { 8, 0 }, // Orbinaut
+ /*Jawz*/ { 8, 1 }, // Jawz
+ /*Mine*/ { 4, 1 }, // Mine
+ /*Ballhog*/ { 2, 1 }, // Ballhog
+ /*Self-Propelled Bomb*/ { 0, 0 }, // Self-Propelled Bomb
+ /*Grow*/ { 2, 1 }, // Grow
+ /*Shrink*/ { 0, 0 }, // Shrink
+ /*Thunder Shield*/ { 0, 0 }, // Thunder Shield
+ /*Bubble Shield*/ { 0, 0 }, // Bubble Shield
+ /*Flame Shield*/ { 0, 0 }, // Flame Shield
+ /*Hyudoro*/ { 2, 0 }, // Hyudoro
+ /*Pogo Spring*/ { 2, 0 }, // Pogo Spring
+ /*Super Ring*/ { 0, 0 }, // Super Ring
+ /*Kitchen Sink*/ { 0, 0 }, // Kitchen Sink
+ /*Sneaker x2*/ { 0, 0 }, // Sneaker x2
+ /*Sneaker x3*/ { 0, 1 }, // Sneaker x3
+ /*Banana x3*/ { 1, 0 }, // Banana x3
+ /*Banana x10*/ { 0, 1 }, // Banana x10
+ /*Orbinaut x3*/ { 2, 0 }, // Orbinaut x3
+ /*Orbinaut x4*/ { 1, 1 }, // Orbinaut x4
+ /*Jawz x2*/ { 2, 1 } // Jawz x2
};
#define DISTVAR (2048) // Magic number distance for use with item roulette tiers
@@ -620,19 +620,20 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush,
UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush)
{
UINT8 i;
- UINT8 n = 0;
UINT8 useodds = 0;
UINT8 disttable[14];
- UINT8 totallen = 0;
UINT8 distlen = 0;
boolean oddsvalid[8];
+ // Unused now, oops :V
+ (void)bestbumper;
+
for (i = 0; i < 8; i++)
{
UINT8 j;
boolean available = false;
- if (gametype == GT_BATTLE && i > 5)
+ if (gametype == GT_BATTLE && i > 1)
{
oddsvalid[i] = false;
break;
@@ -653,30 +654,24 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
#define SETUPDISTTABLE(odds, num) \
if (oddsvalid[odds]) \
for (i = num; i; --i) \
- disttable[distlen++] = odds; \
- totallen += num;
+ disttable[distlen++] = odds;
if (gametype == GT_BATTLE) // Battle Mode
{
- SETUPDISTTABLE(0,1);
- SETUPDISTTABLE(1,1);
- SETUPDISTTABLE(2,1);
- SETUPDISTTABLE(3,1);
- SETUPDISTTABLE(4,1);
-
- if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items
- useodds = 5;
+ if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[1] == true)
+ {
+ // 1 is the extreme odds of player-controlled "Karma" items
+ useodds = 1;
+ }
else
{
- SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc
- if (K_IsPlayerWanted(player))
- wantedpos++;
- if (wantedpos > 4) // Don't run off into karma items
- wantedpos = 4;
- if (wantedpos < 0) // Don't go below somehow
- wantedpos = 0;
- n = (wantedpos * distlen) / totallen;
- useodds = disttable[n];
+ useodds = 0;
+
+ if (oddsvalid[0] == false && oddsvalid[1] == true)
+ {
+ // try to use karma odds as a fallback
+ useodds = 1;
+ }
}
}
else
@@ -1240,7 +1235,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid)
}
}
-/** \brief Checks that the player is on an offroad subsector for realsies
+/** \brief Checks that the player is on an offroad subsector for realsies. Also accounts for line riding to prevent cheese.
\param mo player mobj object
@@ -1248,18 +1243,64 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid)
*/
static UINT8 K_CheckOffroadCollide(mobj_t *mo)
{
- UINT8 i;
+ // Check for sectors in touching_sectorlist
+ UINT8 i; // special type iter
+ msecnode_t *node; // touching_sectorlist iter
+ sector_t *s; // main sector shortcut
+ sector_t *s2; // FOF sector shortcut
+ ffloor_t *rover; // FOF
+
+ fixed_t flr;
+ fixed_t cel; // floor & ceiling for height checks to make sure we're touching the offroad sector.
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
- for (i = 2; i < 5; i++)
+ for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
- if (P_MobjTouchingSectorSpecial(mo, 1, i, true))
- return i-1;
- }
+ if (!node->m_sector)
+ break; // shouldn't happen.
- return 0;
+ s = node->m_sector;
+ // 1: Check for the main sector, make sure we're on the floor of that sector and see if we can apply offroad.
+ // Make arbitrary Z checks because we want to check for 1 sector in particular, we don't want to affect the player if the offroad sector is way below them and they're lineriding a normal sector above.
+
+ flr = P_MobjFloorZ(mo, s, s, mo->x, mo->y, NULL, false, true);
+ cel = P_MobjCeilingZ(mo, s, s, mo->x, mo->y, NULL, true, true); // get Z coords of both floors and ceilings for this sector (this accounts for slopes properly.)
+ // NOTE: we don't use P_GetZAt with our x/y directly because the mobj won't have the same height because of its hitbox on the slope. Complex garbage but tldr it doesn't work.
+
+ if ( ((s->flags & SF_FLIPSPECIAL_FLOOR) && mo->z == flr) // floor check
+ || ((mo->eflags & MFE_VERTICALFLIP && (s->flags & SF_FLIPSPECIAL_CEILING) && (mo->z + mo->height) == cel)) ) // ceiling check.
+
+ for (i = 2; i < 5; i++) // check for sector special
+
+ if (GETSECSPECIAL(s->special, 1) == i)
+ return i-1; // return offroad type
+
+ // 2: If we're here, we haven't found anything. So let's try looking for FOFs in the sectors using the same logic.
+ for (rover = s->ffloors; rover; rover = rover->next)
+ {
+ if (!(rover->flags & FF_EXISTS)) // This FOF doesn't exist anymore.
+ continue;
+
+ s2 = §ors[rover->secnum]; // makes things easier for us
+
+ flr = P_GetFOFBottomZ(mo, s, rover, mo->x, mo->y, NULL);
+ cel = P_GetFOFTopZ(mo, s, rover, mo->x, mo->y, NULL); // Z coords for fof top/bottom.
+
+ // we will do essentially the same checks as above instead of bothering with top/bottom height of the FOF.
+ // Reminder that an FOF's floor is its bottom, silly!
+ if ( ((s2->flags & SF_FLIPSPECIAL_FLOOR) && mo->z == cel) // "floor" check
+ || ((s2->flags & SF_FLIPSPECIAL_CEILING) && (mo->z + mo->height) == flr) ) // "ceiling" check.
+
+ for (i = 2; i < 5; i++) // check for sector special
+
+ if (GETSECSPECIAL(s2->special, 1) == i)
+ return i-1; // return offroad type
+
+ }
+ }
+ return 0; // couldn't find any offroad
}
/** \brief Updates the Player's offroad value once per frame
@@ -3680,13 +3721,13 @@ void K_PuntMine(mobj_t *thismine, mobj_t *punter)
mine->flags2 = thismine->flags2;
mine->floorz = thismine->floorz;
mine->ceilingz = thismine->ceilingz;
-
+
//Since we aren't using P_KillMobj, we need to clean up the hnext reference
{
P_SetTarget(&thismine->target->hnext, NULL); //target is the player who owns the mine
thismine->target->player->kartstuff[k_bananadrag] = 0;
thismine->target->player->kartstuff[k_itemheld] = 0;
-
+
if (--thismine->target->player->kartstuff[k_itemamount] <= 0)
thismine->target->player->kartstuff[k_itemtype] = KITEM_NONE;
}
@@ -4340,7 +4381,7 @@ void K_DropRocketSneaker(player_t *player)
flingangle = -(ANG60);
else
flingangle = ANG60;
-
+
S_StartSound(shoe, shoe->info->deathsound);
P_SetObjectMomZ(shoe, 8*FRACUNIT, false);
P_InstaThrust(shoe, R_PointToAngle2(shoe->target->x, shoe->target->y, shoe->x, shoe->y)+flingangle, 16*FRACUNIT);
@@ -4944,14 +4985,22 @@ player_t *K_FindJawzTarget(mobj_t *actor, player_t *source)
static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd)
{
const INT32 numsnds = 13;
+
+ const fixed_t closedist = 160*FRACUNIT;
+ const fixed_t fardist = 1536*FRACUNIT;
+
+ const UINT8 dampenval = 48; // 255 * 48 = close enough to FRACUNIT/6
+
INT32 class, s, w; // engine class number
+
UINT8 volume = 255;
- fixed_t volumedampen = 0;
+ fixed_t volumedampen = FRACUNIT;
+
INT32 targetsnd = 0;
INT32 i;
- s = (player->kartspeed-1)/3;
- w = (player->kartweight-1)/3;
+ s = (player->kartspeed - 1) / 3;
+ w = (player->kartweight - 1) / 3;
#define LOCKSTAT(stat) \
if (stat < 0) { stat = 0; } \
@@ -4960,83 +5009,119 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd)
LOCKSTAT(w);
#undef LOCKSTAT
- class = s+(3*w);
+ class = s + (3*w);
- // Silence the engines
if (leveltime < 8 || player->spectator)
{
- player->karthud[khud_enginesnd] = 0; // Reset sound number
+ // Silence the engines, and reset sound number while we're at it.
+ player->karthud[khud_enginesnd] = 0;
return;
}
#if 0
if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct!
#else
- if (leveltime % 8) // .25 seconds of wait time between engine sounds
+ if (leveltime % 8)
#endif
+ {
+ // .25 seconds of wait time between each engine sound playback
return;
+ }
if (player->respawn.state == RESPAWNST_DROP) // Dropdashing
+ {
+ // Dropdashing
targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0);
- else if (K_PlayerEBrake(player) == true) // Spindashing
+ }
+ else if (K_PlayerEBrake(player) == true)
+ {
+ // Spindashing
targetsnd = ((cmd->buttons & BT_DRIFT) ? 12 : 0);
+ }
else
- targetsnd = (((6*K_GetForwardMove(player))/25) + ((player->speed / mapobjectscale)/5))/2;
+ {
+ // Average out the value of forwardmove and the speed that you're moving at.
+ targetsnd = (((6 * cmd->forwardmove) / 25) + ((player->speed / mapobjectscale) / 5)) / 2;
+ }
- if (targetsnd < 0)
- targetsnd = 0;
- if (targetsnd > 12)
- targetsnd = 12;
+ if (targetsnd < 0) { targetsnd = 0; }
+ if (targetsnd > 12) { targetsnd = 12; }
- if (player->karthud[khud_enginesnd] < targetsnd)
- player->karthud[khud_enginesnd]++;
- if (player->karthud[khud_enginesnd] > targetsnd)
- player->karthud[khud_enginesnd]--;
+ if (player->karthud[khud_enginesnd] < targetsnd) { player->karthud[khud_enginesnd]++; }
+ if (player->karthud[khud_enginesnd] > targetsnd) { player->karthud[khud_enginesnd]--; }
- if (player->karthud[khud_enginesnd] < 0)
- player->karthud[khud_enginesnd] = 0;
- if (player->karthud[khud_enginesnd] > 12)
- player->karthud[khud_enginesnd] = 12;
+ if (player->karthud[khud_enginesnd] < 0) { player->karthud[khud_enginesnd] = 0; }
+ if (player->karthud[khud_enginesnd] > 12) { player->karthud[khud_enginesnd] = 12; }
+
+ // This code calculates how many players (and thus, how many engine sounds) are within ear shot,
+ // and rebalances the volume of your engine sound based on how far away they are.
+
+ // This results in multiple things:
+ // - When on your own, you will hear your own engine sound extremely clearly.
+ // - When you were alone but someone is gaining on you, yours will go quiet, and you can hear theirs more clearly.
+ // - When around tons of people, engine sounds will try to rebalance to not be as obnoxious.
for (i = 0; i < MAXPLAYERS; i++)
{
UINT8 thisvol = 0;
fixed_t dist;
- if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting)
- continue;
-
- if (P_IsDisplayPlayer(&players[i]))
+ if (!playeringame[i] || !players[i].mo)
{
- volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time.
+ // This player doesn't exist.
continue;
}
- dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x,
- player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2;
+ if (players[i].spectator)
+ {
+ // This player isn't playing an engine sound.
+ continue;
+ }
+
+ if (P_IsDisplayPlayer(&players[i]))
+ {
+ // Don't dampen yourself!
+ continue;
+ }
+
+ dist = P_AproxDistance(
+ P_AproxDistance(
+ player->mo->x - players[i].mo->x,
+ player->mo->y - players[i].mo->y),
+ player->mo->z - players[i].mo->z) / 2;
dist = FixedDiv(dist, mapobjectscale);
- if (dist > 1536< fardist)
+ {
+ // ENEMY OUT OF RANGE !
continue;
- else if (dist < 160<>FRACBITS)) / (((1536<>(FRACBITS+4));
+ {
+ thisvol = (15 * ((closedist - dist) / FRACUNIT)) / ((fardist - closedist) >> (FRACBITS+4));
+ }
- if (thisvol == 0)
- continue;
-
- volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT
+ volumedampen += (thisvol * dampenval);
}
if (volumedampen > FRACUNIT)
- volume = FixedDiv(volume<>FRACBITS;
+ {
+ volume = FixedDiv(volume * FRACUNIT, volumedampen) / FRACUNIT;
+ }
- if (volume <= 0) // Might as well
+ if (volume <= 0)
+ {
+ // Don't need to play the sound at all.
return;
+ }
- S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->karthud[khud_enginesnd]) + (class*numsnds), volume);
+ S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->karthud[khud_enginesnd]) + (class * numsnds), volume);
}
static void K_UpdateInvincibilitySounds(player_t *player)
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index a6005946f..9ddbd868e 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -3624,41 +3624,6 @@ static int lib_kGetItemPatch(lua_State *L)
return 1;
}
-// sets the remaining time before players blow up
-static int lib_kSetRaceCountdown(lua_State *L)
-{
- tic_t c = (tic_t)luaL_checkinteger(L, 1);
- racecountdown = c;
- return 0;
-}
-
-// sets the remaining time before the race ends after everyone finishes
-static int lib_kSetExitCountdown(lua_State *L)
-{
- tic_t c = (tic_t)luaL_checkinteger(L, 1);
- NOHUD
- exitcountdown = c;
- return 0;
-}
-
-// Sets the item cooldown before another shrink / SPB can be rolled
-static int lib_kSetIndirectItemCountdown(lua_State *L)
-{
- tic_t c = (tic_t)luaL_checkinteger(L, 1);
- NOHUD
- indirectitemcooldown = c;
- return 0;
-}
-
-// Sets the item cooldown before another shrink / SPB can be rolled
-static int lib_kSetHyuCountdown(lua_State *L)
-{
- tic_t c = (tic_t)luaL_checkinteger(L, 1);
- NOHUD
- hyubgone = c;
- return 0;
-}
-
static luaL_Reg lib[] = {
{"print", lib_print},
{"chatprint", lib_chatprint},
@@ -3920,10 +3885,6 @@ static luaL_Reg lib[] = {
{"K_GetKartAccel",lib_kGetKartAccel},
{"K_GetKartFlashing",lib_kGetKartFlashing},
{"K_GetItemPatch",lib_kGetItemPatch},
- {"K_SetRaceCountdown",lib_kSetRaceCountdown},
- {"K_SetExitCountdown",lib_kSetExitCountdown},
- {"K_SetIndirectItemCooldown",lib_kSetIndirectItemCountdown},
- {"K_SetHyudoroCooldown",lib_kSetHyuCountdown},
{NULL, NULL}
};
diff --git a/src/lua_script.c b/src/lua_script.c
index b709bc0e7..c8d518d66 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -370,7 +370,11 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"exitcountdown")) {
lua_pushinteger(L, exitcountdown); // This name is pretty dumb. Hence why we'll prefer more descriptive names at least in Lua...
return 1;
+ } else if (fastcmp(word,"replayplayback")) {
+ lua_pushboolean(L, demo.playback);
+ return 1;
}
+
return 0;
}
@@ -381,6 +385,15 @@ int LUA_WriteGlobals(lua_State *L, const char *word)
redscore = (UINT32)luaL_checkinteger(L, 2);
else if (fastcmp(word, "bluescore"))
bluescore = (UINT32)luaL_checkinteger(L, 2);
+ // SRB2Kart
+ else if (fastcmp(word,"racecountdown"))
+ racecountdown = (tic_t)luaL_checkinteger(L, 2);
+ else if (fastcmp(word,"exitcountdown"))
+ exitcountdown = (tic_t)luaL_checkinteger(L, 2);
+ else if (fastcmp(word,"indirectitemcooldown"))
+ indirectitemcooldown = (tic_t)luaL_checkinteger(L, 2);
+ else if (fastcmp(word,"hyubgone"))
+ hyubgone = (tic_t)luaL_checkinteger(L, 2);
else
return 0;
diff --git a/src/m_menu.c b/src/m_menu.c
index 1330469ce..62225330d 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -85,6 +85,11 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
//int vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
#endif
+#ifdef HAVE_DISCORDRPC
+//#include "discord_rpc.h"
+#include "discord.h"
+#endif
+
#define SKULLXOFF -32
#define LINEHEIGHT 16
#define STRINGHEIGHT 8
@@ -186,7 +191,6 @@ static void M_StopMessage(INT32 choice);
#ifndef NONET
static void M_HandleServerPage(INT32 choice);
-static void M_RoomMenu(INT32 choice);
#endif
// Prototyping is fun, innit?
@@ -194,9 +198,18 @@ static void M_RoomMenu(INT32 choice);
// NEEDED FUNCTION PROTOTYPES GO HERE
// ==========================================================================
+void M_SetWaitingMode(int mode);
+int M_GetWaitingMode(void);
+
// the haxor message menu
menu_t MessageDef;
+#ifdef HAVE_DISCORDRPC
+menu_t MISC_DiscordRequestsDef;
+static void M_HandleDiscordRequests(INT32 choice);
+static void M_DrawDiscordRequests(void);
+#endif
+
menu_t SPauseDef;
#define lsheadingheight 16
@@ -263,7 +276,6 @@ static void M_ConnectMenu(INT32 choice);
static void M_ConnectMenuModChecks(INT32 choice);
static void M_Refresh(INT32 choice);
static void M_Connect(INT32 choice);
-static void M_ChooseRoom(INT32 choice);
#endif
#ifndef TESTERS
static void M_StartOfflineServerMenu(INT32 choice);
@@ -306,6 +318,9 @@ menu_t OP_SoundOptionsDef;
//Misc
menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef;
+#ifdef HAVE_DISCORDRPC
+menu_t OP_DiscordOptionsDef;
+#endif
menu_t OP_HUDOptionsDef, OP_ChatOptionsDef;
menu_t OP_GameOptionsDef, OP_ServerOptionsDef;
#ifndef NONET
@@ -368,7 +383,6 @@ static void M_DrawMonitorToggles(void);
static void M_DrawMPMainMenu(void);
#ifndef NONET
static void M_DrawConnectMenu(void);
-static void M_DrawRoomMenu(void);
#endif
static void M_DrawJoystick(void);
static void M_DrawSetupMultiPlayerMenu(void);
@@ -614,6 +628,10 @@ static menuitem_t MPauseMenu[] =
{IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16},
{IT_STRING | IT_CALL, NULL, "Switch Map..." , M_MapChange, 24},
+#ifdef HAVE_DISCORDRPC
+ {IT_STRING | IT_SUBMENU, NULL, "Ask To Join Requests...", &MISC_DiscordRequestsDef, 24},
+#endif
+
{IT_CALL | IT_STRING, NULL, "Continue", M_SelectableClearMenus, 40},
{IT_CALL | IT_STRING, NULL, "P1 Setup...", M_SetupMultiPlayer, 48}, // splitscreen
{IT_CALL | IT_STRING, NULL, "P2 Setup...", M_SetupMultiPlayer2, 56}, // splitscreen
@@ -637,6 +655,9 @@ typedef enum
mpause_addons = 0,
mpause_scramble,
mpause_switchmap,
+#ifdef HAVE_DISCORDRPC
+ mpause_discordrequests,
+#endif
mpause_continue,
mpause_psetupsplit,
@@ -687,6 +708,13 @@ typedef enum
spause_quit
} spause_e;
+#ifdef HAVE_DISCORDRPC
+static menuitem_t MISC_DiscordRequestsMenu[] =
+{
+ {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleDiscordRequests, 0},
+};
+#endif
+
// -----------------
// Misc menu options
// -----------------
@@ -1020,7 +1048,7 @@ static menuitem_t MP_MainMenu[] =
static menuitem_t MP_ServerMenu[] =
{
{IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 10},
- {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20},
+ {IT_STRING|IT_CVAR, NULL, "Advertise", &cv_advertise, 20},
{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30},
{IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68},
@@ -1051,54 +1079,30 @@ static menuitem_t MP_PlayerSetupMenu[] =
#ifndef NONET
static menuitem_t MP_ConnectMenu[] =
{
- {IT_STRING | IT_CALL, NULL, "Room...", M_RoomMenu, 4},
- {IT_STRING | IT_CVAR, NULL, "Sort By", &cv_serversort, 12},
- {IT_STRING | IT_KEYHANDLER, NULL, "Page", M_HandleServerPage, 20},
- {IT_STRING | IT_CALL, NULL, "Refresh", M_Refresh, 28},
+ {IT_STRING | IT_CVAR, NULL, "Sort By", &cv_serversort, 4},
+ {IT_STRING | IT_KEYHANDLER, NULL, "Page", M_HandleServerPage, 12},
+ {IT_STRING | IT_CALL, NULL, "Refresh", M_Refresh, 20},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 48-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 60-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 72-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 84-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 96-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 108-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 120-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 132-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 144-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 156-4},
- {IT_STRING | IT_SPACE, NULL, "", M_Connect, 168-4},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 36},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 48},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 60},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 72},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 84},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 96},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 108},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 120},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 132},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 144},
+ {IT_STRING | IT_SPACE, NULL, "", M_Connect, 156},
};
enum
{
- mp_connect_room,
mp_connect_sort,
mp_connect_page,
mp_connect_refresh,
FIRSTSERVERLINE
};
-
-menuitem_t MP_RoomMenu[] =
-{
- {IT_STRING | IT_CALL, NULL, "", M_ChooseRoom, 9},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 18},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 27},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 36},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 45},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 54},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 63},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 72},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 81},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 90},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 99},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 108},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 117},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 126},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 135},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 144},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 153},
- {IT_DISABLED, NULL, "", M_ChooseRoom, 162},
-};
#endif
// ------------------------------------
@@ -1344,11 +1348,17 @@ static menuitem_t OP_SoundOptionsMenu[] =
static menuitem_t OP_DataOptionsMenu[] =
{
+
{IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10},
{IT_STRING | IT_CALL, NULL, "Addon Options...", M_AddonsOptions, 20},
{IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 30},
+#ifdef HAVE_DISCORDRPC
+ {IT_STRING | IT_SUBMENU, NULL, "Discord Options...", &OP_DiscordOptionsDef, 40},
+ {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 60},
+#else
{IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50},
+#endif
};
static menuitem_t OP_ScreenshotOptionsMenu[] =
@@ -1397,7 +1407,7 @@ static menuitem_t OP_AddonsOptionsMenu[] =
{IT_HEADER, NULL, "Menu", NULL, 0},
{IT_STRING|IT_CVAR, NULL, "Location", &cv_addons_option, 10},
{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_addons_folder, 20},
- {IT_STRING|IT_CVAR, NULL, "Identify addons via", &cv_addons_md5, 48},
+ {IT_STRING|IT_CVAR, NULL, "Identify addons via", &cv_addons_md5, 48},
{IT_STRING|IT_CVAR, NULL, "Show unsupported file types", &cv_addons_showall, 58},
{IT_HEADER, NULL, "Search", NULL, 76},
@@ -1410,6 +1420,19 @@ enum
op_addons_folder = 2,
};
+#ifdef HAVE_DISCORDRPC
+static menuitem_t OP_DiscordOptionsMenu[] =
+{
+ {IT_STRING | IT_CVAR, NULL, "Rich Presence", &cv_discordrp, 10},
+
+ {IT_HEADER, NULL, "Rich Presence Settings", NULL, 30},
+ {IT_STRING | IT_CVAR, NULL, "Streamer Mode", &cv_discordstreamer, 40},
+
+ {IT_STRING | IT_CVAR, NULL, "Allow Ask To Join", &cv_discordasks, 60},
+ {IT_STRING | IT_CVAR, NULL, "Allow Invites", &cv_discordinvites, 70},
+};
+#endif
+
static menuitem_t OP_HUDOptionsMenu[] =
{
@@ -1658,6 +1681,20 @@ menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72);
menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72);
menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72);
+#ifdef HAVE_DISCORDRPC
+menu_t MISC_DiscordRequestsDef = {
+ MN_NONE,
+ NULL,
+ sizeof (MISC_DiscordRequestsMenu)/sizeof (menuitem_t),
+ &MPauseDef,
+ MISC_DiscordRequestsMenu,
+ M_DrawDiscordRequests,
+ 0, 0,
+ 0,
+ NULL
+};
+#endif
+
// Misc Main Menu
menu_t MISC_ScrambleTeamDef = DEFAULTMENUSTYLE(MN_NONE, NULL, MISC_ScrambleTeamMenu, &MPauseDef, 27, 40);
menu_t MISC_ChangeTeamDef = DEFAULTMENUSTYLE(MN_NONE, NULL, MISC_ChangeTeamMenu, &MPauseDef, 27, 40);
@@ -1909,18 +1946,6 @@ menu_t MP_ConnectDef =
0,
M_CancelConnect
};
-menu_t MP_RoomDef =
-{
- MN_NONE,
- "M_MULTI",
- sizeof (MP_RoomMenu)/sizeof (menuitem_t),
- &MP_ConnectDef,
- MP_RoomMenu,
- M_DrawRoomMenu,
- 27, 32,
- 0,
- NULL
-};
#endif
menu_t MP_PlayerSetupDef =
{
@@ -2050,6 +2075,9 @@ menu_t OP_OpenGLOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_VIDEO", OP_OpenGLOptio
menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30);
menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_SCSHOT", OP_ScreenshotOptionsMenu, &OP_DataOptionsDef, 30, 30);
menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE(MN_NONE, "M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30);
+#ifdef HAVE_DISCORDRPC
+menu_t OP_DiscordOptionsDef = DEFAULTMENUSTYLE(MN_NONE, NULL, OP_DiscordOptionsMenu, &OP_DataOptionsDef, 30, 30);
+#endif
menu_t OP_EraseDataDef = DEFAULTMENUSTYLE(MN_NONE, "M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 30, 30);
// ==========================================================================
@@ -3262,12 +3290,18 @@ void M_StartControlPanel(void)
MPauseMenu[mpause_psetup].status = IT_DISABLED;
MISC_ChangeTeamMenu[0].status = IT_DISABLED;
MISC_ChangeSpectateMenu[0].status = IT_DISABLED;
+
// Reset these in case splitscreen messes things up
+ MPauseMenu[mpause_addons].alphaKey = 8;
+ MPauseMenu[mpause_scramble].alphaKey = 8;
+ MPauseMenu[mpause_switchmap].alphaKey = 24;
+
MPauseMenu[mpause_switchteam].alphaKey = 48;
MPauseMenu[mpause_switchspectate].alphaKey = 48;
MPauseMenu[mpause_options].alphaKey = 64;
MPauseMenu[mpause_title].alphaKey = 80;
MPauseMenu[mpause_quit].alphaKey = 88;
+
Dummymenuplayer_OnChange();
if ((server || IsPlayerAdmin(consoleplayer)))
@@ -3339,6 +3373,19 @@ void M_StartControlPanel(void)
MPauseMenu[mpause_spectate].status = IT_GRAYEDOUT;
}
+#ifdef HAVE_DISCORDRPC
+ {
+ UINT8 i;
+
+ for (i = 0; i < mpause_discordrequests; i++)
+ MPauseMenu[i].alphaKey -= 8;
+
+ MPauseMenu[mpause_discordrequests].alphaKey = MPauseMenu[i].alphaKey;
+
+ M_RefreshPauseMenu();
+ }
+#endif
+
currentMenu = &MPauseDef;
itemOn = mpause_continue;
}
@@ -3379,30 +3426,6 @@ void M_SetupNextMenu(menu_t *menudef)
{
INT16 i;
-#ifdef HAVE_THREADS
- if (currentMenu == &MP_RoomDef || currentMenu == &MP_ConnectDef)
- {
- I_lock_mutex(&ms_QueryId_mutex);
- {
- ms_QueryId++;
- }
- I_unlock_mutex(ms_QueryId_mutex);
- }
-
- if (currentMenu == &MP_ConnectDef)
- {
- I_lock_mutex(&ms_ServerList_mutex);
- {
- if (ms_ServerList)
- {
- free(ms_ServerList);
- ms_ServerList = NULL;
- }
- }
- I_unlock_mutex(ms_ServerList_mutex);
- }
-#endif/*HAVE_THREADS*/
-
if (currentMenu->quitroutine)
{
// If you're going from a menu to itself, why are you running the quitroutine? You're not quitting it! -SH
@@ -3471,7 +3494,7 @@ void M_Ticker(void)
setmodeneeded = vidm_previousmode + 1;
}
-#ifdef HAVE_THREADS
+#if defined (MASTERSERVER) && defined (HAVE_THREADS)
I_lock_mutex(&ms_ServerList_mutex);
{
if (ms_ServerList)
@@ -4176,6 +4199,25 @@ static void M_DrawPauseMenu(void)
}
#endif
+#ifdef HAVE_DISCORDRPC
+ // kind of hackily baked in here
+ if (currentMenu == &MPauseDef && discordRequestList != NULL)
+ {
+ const tic_t freq = TICRATE/2;
+
+ if ((leveltime % freq) >= freq/2)
+ {
+ V_DrawFixedPatch(204 * FRACUNIT,
+ (currentMenu->y + MPauseMenu[mpause_discordrequests].alphaKey - 1) * FRACUNIT,
+ FRACUNIT,
+ 0,
+ W_CachePatchName("K_REQUE2", PU_CACHE),
+ NULL
+ );
+ }
+ }
+#endif
+
M_DrawGenericMenu();
}
@@ -6307,7 +6349,12 @@ static void M_Options(INT32 choice)
OP_MainMenu[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits
+
+#ifdef HAVE_DISCORDRPC
+ OP_DataOptionsMenu[4].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data
+#else
OP_DataOptionsMenu[3].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data
+#endif
OP_GameOptionsMenu[3].status =
(M_SecretUnlocked(SECRET_ENCORE)) ? (IT_CVAR|IT_STRING) : IT_SECRET; // cv_kartencore
@@ -6348,6 +6395,20 @@ static void M_SelectableClearMenus(INT32 choice)
M_ClearMenus(true);
}
+void M_RefreshPauseMenu(void)
+{
+#ifdef HAVE_DISCORDRPC
+ if (discordRequestList != NULL)
+ {
+ MPauseMenu[mpause_discordrequests].status = IT_STRING | IT_SUBMENU;
+ }
+ else
+ {
+ MPauseMenu[mpause_discordrequests].status = IT_GRAYEDOUT;
+ }
+#endif
+}
+
// ======
// CHEATS
// ======
@@ -7542,7 +7603,7 @@ static void M_DrawStatsMaps(int location)
else
V_DrawString(20, y, 0, va("%s %s %d",
mapheaderinfo[mnum]->lvlttl,
- (mapheaderinfo[mnum]->zonttl[0] ? mapheaderinfo[mnum]->zonttl : "ZONE"),
+ (mapheaderinfo[mnum]->zonttl[0] ? mapheaderinfo[mnum]->zonttl : "Zone"),
mapheaderinfo[mnum]->actnum));
y += 8;
@@ -8319,7 +8380,118 @@ static void M_EndGame(INT32 choice)
// Connect Menu
//===========================================================================
-#define SERVERHEADERHEIGHT 44
+void
+M_SetWaitingMode (int mode)
+{
+#ifdef HAVE_THREADS
+ I_lock_mutex(&m_menu_mutex);
+#endif
+ {
+ m_waiting_mode = mode;
+ }
+#ifdef HAVE_THREADS
+ I_unlock_mutex(m_menu_mutex);
+#endif
+}
+
+int
+M_GetWaitingMode (void)
+{
+ int mode;
+
+#ifdef HAVE_THREADS
+ I_lock_mutex(&m_menu_mutex);
+#endif
+ {
+ mode = m_waiting_mode;
+ }
+#ifdef HAVE_THREADS
+ I_unlock_mutex(m_menu_mutex);
+#endif
+
+ return mode;
+}
+
+#ifdef MASTERSERVER
+#ifdef HAVE_THREADS
+static void
+Spawn_masterserver_thread (const char *name, void (*thread)(int*))
+{
+ int *id = malloc(sizeof *id);
+
+ I_lock_mutex(&ms_QueryId_mutex);
+ {
+ *id = ms_QueryId;
+ }
+ I_unlock_mutex(ms_QueryId_mutex);
+
+ I_spawn_thread(name, (I_thread_fn)thread, id);
+}
+
+static int
+Same_instance (int id)
+{
+ int okay;
+
+ I_lock_mutex(&ms_QueryId_mutex);
+ {
+ okay = ( id == ms_QueryId );
+ }
+ I_unlock_mutex(ms_QueryId_mutex);
+
+ return okay;
+}
+#endif/*HAVE_THREADS*/
+
+static void
+Fetch_servers_thread (int *id)
+{
+ msg_server_t * server_list;
+
+ (void)id;
+
+ M_SetWaitingMode(M_WAITING_SERVERS);
+
+#ifdef HAVE_THREADS
+ server_list = GetShortServersList(*id);
+#else
+ server_list = GetShortServersList(0);
+#endif
+
+ if (server_list)
+ {
+#ifdef HAVE_THREADS
+ if (Same_instance(*id))
+#endif
+ {
+ M_SetWaitingMode(M_NOT_WAITING);
+
+#ifdef HAVE_THREADS
+ I_lock_mutex(&ms_ServerList_mutex);
+ {
+ ms_ServerList = server_list;
+ }
+ I_unlock_mutex(ms_ServerList_mutex);
+#else
+ CL_QueryServerList(server_list);
+ free(server_list);
+#endif
+ }
+#ifdef HAVE_THREADS
+ else
+ {
+ free(server_list);
+ }
+#endif
+ }
+
+#ifdef HAVE_THREADS
+ free(id);
+#endif
+}
+#endif/*MASTERSERVER*/
+
+#define SERVERHEADERHEIGHT 36
#define SERVERLINEHEIGHT 12
#define S_LINEY(n) currentMenu->y + SERVERHEADERHEIGHT + (n * SERVERLINEHEIGHT)
@@ -8391,77 +8563,18 @@ static void M_Refresh(INT32 choice)
if (rendermode == render_soft)
I_FinishUpdate(); // page flip or blit buffer
- // note: this is the one case where 0 is a valid room number
- // because it corresponds to "All"
- CL_UpdateServerList(!(ms_RoomId < 0), ms_RoomId);
-
// first page of servers
serverlistpage = 0;
-}
-static INT32 menuRoomIndex = 0;
-
-static void M_DrawRoomMenu(void)
-{
- static int frame = -12;
- int dot_frame;
- char text[4];
-
- const char *rmotd;
- const char *waiting_message;
-
- int dots;
-
- if (m_waiting_mode)
- {
- dot_frame = frame / 4;
- dots = dot_frame + 3;
-
- strcpy(text, " ");
-
- if (dots > 0)
- {
- if (dot_frame < 0)
- dot_frame = 0;
-
- strncpy(&text[dot_frame], "...", min(dots, 3 - dot_frame));
- }
-
- if (++frame == 12)
- frame = -12;
-
- currentMenu->menuitems[0].text = text;
- }
-
- // use generic drawer for cursor, items and title
- M_DrawGenericMenu();
-
- V_DrawString(currentMenu->x - 16, currentMenu->y, highlightflags, M_GetText("Select a room"));
-
- if (m_waiting_mode == M_NOT_WAITING)
- {
- M_DrawTextBox(144, 24, 20, 20);
-
- if (itemOn == 0)
- rmotd = M_GetText("Don't connect to the Master Server.");
- else
- rmotd = room_list[itemOn-1].motd;
-
- rmotd = V_WordWrap(0, 20*8, 0, rmotd);
- V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd);
- }
-
- if (m_waiting_mode)
- {
- // Display a little "please wait" message.
- M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3);
- if (m_waiting_mode == M_WAITING_VERSION)
- waiting_message = "Checking for updates...";
- else
- waiting_message = "Fetching room info...";
- V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, waiting_message);
- V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait.");
- }
+#ifdef MASTERSERVER
+#ifdef HAVE_THREADS
+ Spawn_masterserver_thread("fetch-servers", Fetch_servers_thread);
+#else/*HAVE_THREADS*/
+ Fetch_servers_thread(NULL);
+#endif/*HAVE_THREADS*/
+#else/*MASTERSERVER*/
+ CL_UpdateServerList();
+#endif/*MASTERSERVER*/
}
static void M_DrawConnectMenu(void)
@@ -8471,6 +8584,7 @@ static void M_DrawConnectMenu(void)
//const char *spd = "";
const char *pwr = "----";
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
+ int waiting;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
MP_ConnectMenu[i].status = IT_STRING | IT_SPACE;
@@ -8478,20 +8592,12 @@ static void M_DrawConnectMenu(void)
if (!numPages)
numPages = 1;
- // Room name
- if (ms_RoomId < 0)
- V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey,
- highlightflags, (itemOn == mp_connect_room) ? "