From 9bfc82a14b95638bb51aaf6d8cbf7bd840178575 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sun, 27 Mar 2022 20:14:54 -0500 Subject: [PATCH 01/89] Prevent comptime.* from failing compilation --- src/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index c1aa35742..e24d301bc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -357,9 +357,9 @@ $(dbg).txt : $(dbg) # this really updates comptime.h comptime.c :: ifdef WINDOWSHELL - $(.)..\comptime.bat . + -$(.)..\comptime.bat . else - $(.)../comptime.sh . + -$(.)../comptime.sh . endif # I wish I could make dependencies out of rc files :( From a614865d3592ef7aed3c0c2d570aea8c6e508ea3 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sun, 27 Mar 2022 21:16:07 -0500 Subject: [PATCH 02/89] Make comptime.sh conform to POSIX and less redundant, among other improvements - Shebang now directly calls /bin/sh - Improved indenting within the comprevision() function - Quoted several strings to prevent possible argument splitting - Replaced archaic backtick command statements with more descriptive `"$(...)"` statements - Replaced direct references to `test` with more readable, but equivalent `[ ... ]` statements - Replaced the ${str:0:8} bashism with an equivalent statement using POSIX cut - Moved redundant definitions of the comptime.h body to a single function that takes arguments --- comptime.sh | 54 ++++++++++++++++++++++------------------------------- 1 file changed, 22 insertions(+), 32 deletions(-) diff --git a/comptime.sh b/comptime.sh index d5ef7271a..a619f6a1a 100755 --- a/comptime.sh +++ b/comptime.sh @@ -1,54 +1,44 @@ -#!/bin/bash -e +#!/bin/sh path="." if [ x"$1" != x ]; then path="$1" fi -versiongit() { - gitbranch=`git rev-parse --abbrev-ref HEAD` - gitversion=`git rev-parse HEAD` - cat < $path/comptime.h +version() { + cat < "$path/comptime.h" // Do not edit! This file was autogenerated // by the $0 script with git // -const char* compbranch = "$gitbranch"; -const char* comprevision = "${gitversion:0:8}"; +const char* compbranch = "$1"; +const char* comprevision = "$2"; EOF -exit 0 +} + +versiongit() { + gitbranch="$(git rev-parse --abbrev-ref HEAD)" + gitversion="$(git rev-parse HEAD | cut -c -8)" + version "$gitbranch" "$gitversion"; + exit 0 } versionsvn() { - svnrevision=`svnversion -n $1` - cat < $path/comptime.h - -// Do not edit! This file was autogenerated -// by the $0 script with subversion -// -const char* compbranch = "Subversion"; -const char* comprevision = "r$svnrevision"; -EOF -exit 0 + svnrevision="$(svnversion -n $1)" + version "Subversion" "r$svnrevision"; + exit 0 } versionfake() { - cat < $path/comptime.h - -// Do not edit! This file was autogenerated -// by the $0 script with an unknown or nonexist SCM -// -const char* compbranch = "Unknown"; -const char* comprevision = "illegal"; -EOF + version "Unknown" "illegal"; } compversion() { -touch $path/comptime.c -versionfake -test -d $path/.svn && versionsvn -test -d $path/../.git && versiongit -exit 1 + touch "$path/comptime.c" + versionfake + [ -d "$path/.svn" ] && versionsvn + [ -d "$path/../.git" ] && versiongit + exit 1 } -test -f $path/comptime.c && compversion +[ -f "$path/comptime.c" ] && compversion exit 2 From b7711b2b97ea33b8f8ceb2e5fde791da725dd067 Mon Sep 17 00:00:00 2001 From: GoldenTails Date: Sun, 27 Mar 2022 21:23:32 -0500 Subject: [PATCH 03/89] Pass argument list directly to functions that use them; quote arguments when used. --- comptime.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/comptime.sh b/comptime.sh index a619f6a1a..ce771f631 100755 --- a/comptime.sh +++ b/comptime.sh @@ -23,7 +23,7 @@ versiongit() { } versionsvn() { - svnrevision="$(svnversion -n $1)" + svnrevision="$(svnversion -n "$1")" version "Subversion" "r$svnrevision"; exit 0 } @@ -35,10 +35,10 @@ versionfake() { compversion() { touch "$path/comptime.c" versionfake - [ -d "$path/.svn" ] && versionsvn + [ -d "$path/.svn" ] && versionsvn "$@" [ -d "$path/../.git" ] && versiongit exit 1 } -[ -f "$path/comptime.c" ] && compversion +[ -f "$path/comptime.c" ] && compversion "$@" exit 2 From 9277870fa2b62aa7984a9a95f9bfe8d94c9268f5 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sat, 5 Nov 2022 03:11:36 +0000 Subject: [PATCH 04/89] Merge branch 'cmake-develop-flag' into 'next' cmake: Add SRB2_CONFIG_DEV_BUILD See merge request STJr/SRB2!1837 (cherry picked from commit 896a7609a77247a09eb93815a335dad5c85d0431) 518cb0b3 cmake: Add SRB2_CONFIG_DEV_BUILD --- src/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ae93aac37..95689a2d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -37,6 +37,8 @@ set(SRB2_CONFIG_YASM OFF CACHE BOOL "Use YASM in place of NASM.") set(SRB2_CONFIG_STATIC_OPENGL OFF CACHE BOOL "Use statically linked OpenGL. NOT RECOMMENDED.") +set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL + "Compile a development build of SRB2.") ### use internal libraries? if(${CMAKE_SYSTEM} MATCHES "Windows") ###set on Windows only @@ -255,6 +257,10 @@ endif() set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wno-trigraphs) +if(${SRB2_CONFIG_DEV_BUILD}) + target_compile_definitions(SRB2SDL2 PRIVATE -DDEVELOP) +endif() + target_compile_definitions(SRB2SDL2 PRIVATE -DCMAKECONFIG) #add_library(SRB2Core STATIC From ce5c41d7eb952e68eb5db593d1f94a5aed9dc4c6 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sat, 5 Nov 2022 05:14:32 +0000 Subject: [PATCH 05/89] Merge branch 'the-one-cmake' into 'next' Overhaul cmake build See merge request STJr/SRB2!1832 (cherry picked from commit 4337205fa82dd30adee9842894e24d56185bf2c7) # Conflicts: # src/sdl/CMakeLists.txt --- CMakeLists.txt | 169 +++++----- cmake/CPM.cmake | 21 ++ cmake/Modules/FindGME.cmake | 12 +- cmake/Modules/FindOPENMPT.cmake | 12 +- cmake/Modules/FindSDL2.cmake | 10 + cmake/Modules/FindSDL2_mixer.cmake | 10 + src/CMakeLists.txt | 242 ++++---------- src/sdl/CMakeLists.txt | 399 +++++++---------------- src/sdl/mixer_sound.c | 2 +- thirdparty/CMakeLists.txt | 499 +++++++++++++++++++++++++++++ thirdparty/openmpt_svn_version.h | 10 + 11 files changed, 822 insertions(+), 564 deletions(-) create mode 100644 cmake/CPM.cmake create mode 100644 thirdparty/CMakeLists.txt create mode 100644 thirdparty/openmpt_svn_version.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f901d3d7..f5364bc88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,68 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +# Set up CMAKE path +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") + +include(CMakeDependentOption) +include(cmake/CPM.cmake) + +file(STRINGS src/version.h SRB2_VERSION) +string(REGEX MATCH "[0-9]+\\.[0-9.]+" SRB2_VERSION ${SRB2_VERSION}) + +# DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string. +# Version change is fine. +project(SRB2 + VERSION ${SRB2_VERSION} + LANGUAGES C CXX) + +##### PACKAGE CONFIGURATION ##### + +set(SRB2_CPACK_GENERATOR "" CACHE STRING "Generator to use for making a package. E.g., ZIP, TGZ, DragNDrop (OSX only). Leave blank for default generator.") + +if("${SRB2_CPACK_GENERATOR}" STREQUAL "") + if("${CMAKE_SYSTEM_NAME}" MATCHES "Windows") + set(SRB2_CPACK_GENERATOR "ZIP") + elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + set(SRB2_CPACK_GENERATOR "TGZ") + elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + set(SRB2_CPACK_GENERATOR "TGZ") + endif() +endif() + +set(CPACK_GENERATOR ${SRB2_CPACK_GENERATOR}) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Sonic Robo Blast 2" CACHE STRING "Program name for display purposes") +set(CPACK_PACKAGE_VENDOR "Sonic Team Jr." CACHE STRING "Vendor name for display purposes") +#set(CPACK_PACKAGE_DESCRIPTION_FILE ) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_PACKAGE_VERSION_MAJOR ${SRB2_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${SRB2_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${SRB2_VERSION_PATCH}) +set(CPACK_PACKAGE_INSTALL_DIRECTORY "CMake ${CMAKE_VERSION_MAJOR}.${CMAKE_VERSION_MINOR}") +SET(CPACK_OUTPUT_FILE_PREFIX package) +include(CPack) + +# Options + +if("${CMAKE_SYSTEM_NAME}" MATCHES Linux) + set(SRB2_CONFIG_SYSTEM_LIBRARIES_DEFAULT ON) +else() + set(SRB2_CONFIG_SYSTEM_LIBRARIES_DEFAULT OFF) +endif() + +option( + SRB2_CONFIG_SYSTEM_LIBRARIES + "Link dependencies using CMake's find_package and do not use internal builds" + ${SRB2_CONFIG_SYSTEM_LIBRARIES_DEFAULT} +) +# This option isn't recommended for distribution builds and probably won't work (yet). +cmake_dependent_option( + SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES + "Use dynamic libraries when compiling internal dependencies" + OFF "NOT SRB2_CONFIG_SYSTEM_LIBRARIES" + OFF +) +option(SRB2_CONFIG_HWRENDER "Enable hardware render (OpenGL) support" ON) +option(SRB2_CONFIG_STATIC_OPENGL "Enable static linking GL (do not do this)" OFF) # Enable CCache early set(SRB2_USE_CCACHE OFF CACHE BOOL "Use CCache") @@ -12,14 +76,18 @@ if (${SRB2_USE_CCACHE}) endif() endif() -file(STRINGS src/version.h SRB2_VERSION) -string(REGEX MATCH "[0-9]+\\.[0-9.]+" SRB2_VERSION ${SRB2_VERSION}) +# Dependencies +add_subdirectory(thirdparty) -# DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string. -# Version change is fine. -project(SRB2 - VERSION ${SRB2_VERSION} - LANGUAGES C) +if("${SRB2_CONFIG_SYSTEM_LIBRARIES}") + find_package(ZLIB REQUIRED) + find_package(PNG REQUIRED) + find_package(SDL2 REQUIRED) + find_package(SDL2_mixer REQUIRED) + find_package(CURL REQUIRED) + find_package(OPENMPT REQUIRED) + find_package(GME REQUIRED) +endif() if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) message(FATAL_ERROR "In-source builds will bring you a world of pain. Please make a separate directory to invoke CMake from.") @@ -29,11 +97,6 @@ if ((${SRB2_USE_CCACHE}) AND (${CMAKE_C_COMPILER} MATCHES "clang")) message(WARNING "Using clang and CCache: You may want to set environment variable CCACHE_CPP2=yes to prevent include errors during compile.") endif() -# Set up CMAKE path -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") - -### Useful functions - # Add sources from Sourcefile function(target_sourcefile type) file(STRINGS Sourcefile list @@ -41,40 +104,6 @@ function(target_sourcefile type) target_sources(SRB2SDL2 PRIVATE ${list}) endfunction() -# Macro to add OSX framework -macro(add_framework fwname appname) - find_library(FRAMEWORK_${fwname} - NAMES ${fwname} - PATHS ${CMAKE_OSX_SYSROOT}/System/Library - ${CMAKE_OSX_SYSROOT}/Library - /System/Library - /Library - ATH_SUFFIXES Frameworks - NO_DEFAULT_PATH) - if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND) - MESSAGE(ERROR ": Framework ${fwname} not found") - else() - TARGET_LINK_LIBRARIES(${appname} PRIVATE "${FRAMEWORK_${fwname}}/${fwname}") - MESSAGE(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}") - endif() -endmacro() - -# Macro to copy Windows DLLs to Debug/Release folder for easy debugging -# Note: this is general purpose, we could copy anything. Just using for DLLs on MSVC though -macro(copy_files_to_build_dir target dlllist_var) - if(MSVC) - # http://stackoverflow.com/a/26983405/3064195 - foreach(dlllist_item ${${dlllist_var}}) - get_filename_component(dllname ${dlllist_item} NAME) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${dlllist_item} - $/${dllname} - ) - endforeach() - endif() -endmacro() - # bitness check set(SRB2_SYSTEM_BITS 0) if(CMAKE_SIZEOF_VOID_P EQUAL 8) @@ -89,26 +118,6 @@ if(${SRB2_SYSTEM_BITS} EQUAL 0) message(STATUS "Target bitness is unknown") endif() -# OS macros -if (UNIX) - add_definitions(-DUNIXCOMMON) -endif() - -if(CMAKE_COMPILER_IS_GNUCC) - find_program(OBJCOPY objcopy) -endif() - -if(${CMAKE_SYSTEM} MATCHES "Linux") - add_definitions(-DLINUX) - if(${SRB2_SYSTEM_BITS} EQUAL 64) - add_definitions(-DLINUX64) - endif() -endif() - -if(${CMAKE_SYSTEM} MATCHES "Darwin") - add_definitions(-DMACOSX) -endif() - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") set(CMAKE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") @@ -129,29 +138,3 @@ git_current_branch(SRB2_GIT_BRANCH "${CMAKE_SOURCE_DIR}") set(SRB2_COMP_BRANCH "${SRB2_GIT_BRANCH}") set(SRB2_COMP_REVISION "${SRB2_COMP_COMMIT}") configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/config.h) - -##### PACKAGE CONFIGURATION ##### - -set(SRB2_CPACK_GENERATOR "" CACHE STRING "Generator to use for making a package. E.g., ZIP, TGZ, DragNDrop (OSX only). Leave blank for default generator.") - -if("${SRB2_CPACK_GENERATOR}" STREQUAL "") - if(${CMAKE_SYSTEM} MATCHES "Windows") - set(SRB2_CPACK_GENERATOR "ZIP") - elseif(${CMAKE_SYSTEM} MATCHES "Linux") - set(SRB2_CPACK_GENERATOR "TGZ") - elseif(${CMAKE_SYSTEM} MATCHES "Darwin") - set(SRB2_CPACK_GENERATOR "TGZ") - endif() -endif() - -set(CPACK_GENERATOR ${SRB2_CPACK_GENERATOR}) -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Sonic Robo Blast 2" CACHE STRING "Program name for display purposes") -set(CPACK_PACKAGE_VENDOR "Sonic Team Jr." CACHE STRING "Vendor name for display purposes") -#set(CPACK_PACKAGE_DESCRIPTION_FILE ) -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") -set(CPACK_PACKAGE_VERSION_MAJOR ${SRB2_VERSION_MAJOR}) -set(CPACK_PACKAGE_VERSION_MINOR ${SRB2_VERSION_MINOR}) -set(CPACK_PACKAGE_VERSION_PATCH ${SRB2_VERSION_PATCH}) -set(CPACK_PACKAGE_INSTALL_DIRECTORY "CMake ${CMAKE_VERSION_MAJOR}.${CMAKE_VERSION_MINOR}") -SET(CPACK_OUTPUT_FILE_PREFIX package) -include(CPack) diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 000000000..772103fc3 --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,21 @@ +set(CPM_DOWNLOAD_VERSION 0.36.0) + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) +if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) + message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") + file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} + ) +endif() + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/cmake/Modules/FindGME.cmake b/cmake/Modules/FindGME.cmake index ea80af454..3af0a94be 100644 --- a/cmake/Modules/FindGME.cmake +++ b/cmake/Modules/FindGME.cmake @@ -20,4 +20,14 @@ find_library(GME_LIBRARY set(GME_PROCESS_INCLUDES GME_INCLUDE_DIR) set(GME_PROCESS_LIBS GME_LIBRARY) -libfind_process(GME) \ No newline at end of file +libfind_process(GME) + +if(GME_FOUND AND NOT TARGET gme) + add_library(gme UNKNOWN IMPORTED) + set_target_properties( + gme + PROPERTIES + IMPORTED_LOCATION "${GME_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GME_INCLUDE_DIR}" + ) +endif() diff --git a/cmake/Modules/FindOPENMPT.cmake b/cmake/Modules/FindOPENMPT.cmake index 2d334b6f0..7e5b2d5a3 100644 --- a/cmake/Modules/FindOPENMPT.cmake +++ b/cmake/Modules/FindOPENMPT.cmake @@ -20,4 +20,14 @@ find_library(OPENMPT_LIBRARY set(OPENMPT_PROCESS_INCLUDES OPENMPT_INCLUDE_DIR) set(OPENMPT_PROCESS_LIBS OPENMPT_LIBRARY) -libfind_process(OPENMPT) \ No newline at end of file +libfind_process(OPENMPT) + +if(OPENMPT_FOUND AND NOT TARGET openmpt) + add_library(openmpt UNKNOWN IMPORTED) + set_target_properties( + openmpt + PROPERTIES + IMPORTED_LOCATION "${OPENMPT_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${OPENMPT_INCLUDE_DIR}" + ) +endif() diff --git a/cmake/Modules/FindSDL2.cmake b/cmake/Modules/FindSDL2.cmake index 2fc833cef..2d625f84c 100644 --- a/cmake/Modules/FindSDL2.cmake +++ b/cmake/Modules/FindSDL2.cmake @@ -31,3 +31,13 @@ find_library(SDL2_LIBRARY set(SDL2_PROCESS_INCLUDES SDL2_INCLUDE_DIR) set(SDL2_PROCESS_LIBS SDL2_LIBRARY) libfind_process(SDL2) + +if(SDL2_FOUND AND NOT TARGET SDL2::SDL2) + add_library(SDL2::SDL2 UNKNOWN IMPORTED) + set_target_properties( + SDL2::SDL2 + PROPERTIES + IMPORTED_LOCATION "${SDL2_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" + ) +endif() diff --git a/cmake/Modules/FindSDL2_mixer.cmake b/cmake/Modules/FindSDL2_mixer.cmake index 9af3e26dd..637498e53 100644 --- a/cmake/Modules/FindSDL2_mixer.cmake +++ b/cmake/Modules/FindSDL2_mixer.cmake @@ -32,3 +32,13 @@ find_library(SDL2_MIXER_LIBRARY set(SDL2_MIXER_PROCESS_INCLUDES SDL2_MIXER_INCLUDE_DIR) set(SDL2_MIXER_PROCESS_LIBS SDL2_MIXER_LIBRARY) libfind_process(SDL2_MIXER) + +if(SDL2_MIXER_FOUND AND NOT TARGET SDL2_mixer::SDL2_mixer) + add_library(SDL2_mixer::SDL2_mixer UNKNOWN IMPORTED) + set_target_properties( + SDL2_mixer::SDL2_mixer + PROPERTIES + IMPORTED_LOCATION "${SDL2_MIXER_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SDL2_MIXER_INCLUDE_DIR}" + ) +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 95689a2d1..2c98019e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,10 @@ -# SRB2 Core - add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32) +if("${CMAKE_COMPILER_IS_GNUCC}" AND "${CMAKE_SYSTEM_NAME}" MATCHES "Windows" AND NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + # On MinGW with internal libraries, link the standard library statically + target_link_options(SRB2SDL2 PRIVATE "-static") +endif() + # Core sources target_sourcefile(c) target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h.in) @@ -11,193 +14,69 @@ set(SRB2_ASM_SOURCES vid_copy.s) set(SRB2_NASM_SOURCES tmap_mmx.nas tmap.nas) ### Configuration -set(SRB2_CONFIG_HAVE_PNG ON CACHE BOOL - "Enable PNG support. Depends on zlib, so will be disabled if you don't enable that too.") -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_OPENMPT ON CACHE BOOL - "Enable OpenMPT support.") -set(SRB2_CONFIG_HAVE_CURL ON CACHE BOOL - "Enable curl support.") -set(SRB2_CONFIG_HAVE_THREADS ON CACHE BOOL - "Enable multithreading support.") -if(${CMAKE_SYSTEM} MATCHES Windows) - set(SRB2_CONFIG_HAVE_MIXERX ON CACHE BOOL - "Enable SDL Mixer X support.") -else() - set(SRB2_CONFIG_HAVE_MIXERX OFF) -endif() -set(SRB2_CONFIG_HWRENDER ON CACHE BOOL - "Enable hardware rendering through OpenGL.") set(SRB2_CONFIG_USEASM OFF CACHE BOOL "Enable NASM tmap implementation for software mode speedup.") set(SRB2_CONFIG_YASM OFF CACHE BOOL "Use YASM in place of NASM.") -set(SRB2_CONFIG_STATIC_OPENGL OFF CACHE BOOL - "Use statically linked OpenGL. NOT RECOMMENDED.") set(SRB2_CONFIG_DEV_BUILD OFF CACHE BOOL "Compile a development build of SRB2.") -### use internal libraries? -if(${CMAKE_SYSTEM} MATCHES "Windows") ###set on Windows only - set(SRB2_CONFIG_USE_INTERNAL_LIBRARIES OFF CACHE BOOL - "Use SRB2's internal copies of required dependencies (SDL2, PNG, zlib, GME, OpenMPT).") -endif() - add_subdirectory(blua) -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) - 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") - endif() - else() - find_package(GME) - endif() - if(${GME_FOUND}) - set(SRB2_HAVE_GME ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_GME) - else() - message(WARNING "You have specified that GME is available but it was not found.") +# OS macros +if (UNIX) + target_compile_definitions(SRB2SDL2 PRIVATE -DUNIXCOMMON) +endif() + +if(CMAKE_COMPILER_IS_GNUCC) + find_program(OBJCOPY objcopy) +endif() + +if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") + target_compile_definitions(SRB2SDL2 PRIVATE -DLINUX) + if(${SRB2_SYSTEM_BITS} EQUAL 64) + target_compile_definitions(SRB2SDL2 PRIVATE -DLINUX64) endif() endif() -if(${SRB2_CONFIG_HAVE_OPENMPT}) - if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - set(OPENMPT_FOUND ON) - set(OPENMPT_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/libopenmpt/inc) - if(${SRB2_SYSTEM_BITS} EQUAL 64) - set(OPENMPT_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/libopenmpt/lib/x86_64/mingw -lopenmpt") - else() # 32-bit - set(OPENMPT_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/libopenmpt/lib/x86/mingw -lopenmpt") - endif() - else() - find_package(OPENMPT) - endif() - if(${OPENMPT_FOUND}) - set(SRB2_HAVE_OPENMPT ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_OPENMPT) - else() - message(WARNING "You have specified that OpenMPT is available but it was not found.") - endif() +if("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + target_compile_definitions(SRB2SDL2 PRIVATE -DMACOSX) endif() -if(${SRB2_CONFIG_HAVE_MIXERX}) - if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - set(MIXERX_FOUND ON) - set(MIXERX_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDLMixerX/i686-w64-mingw32/include/SDL2) - if(${SRB2_SYSTEM_BITS} EQUAL 64) - set(MIXERX_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDLMixerX/x86_64-w64-mingw32/lib -lSDL2_mixer_ext") - else() # 32-bit - set(MIXERX_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDLMixerX/i686-w64-mingw32/lib -lSDL2_mixer_ext") - endif() - else() - # No support for non-Windows (yet?) - #find_package(MIXERX) - message(WARNING "SDL Mixer X is not supported as an external library.") - set(MIXERX_FOUND OFF) - endif() - if(${MIXERX_FOUND}) - set(SRB2_HAVE_MIXERX ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_MIXERX) - else() - message(WARNING "You have specified that SDL Mixer X is available but it was not found.") - endif() +target_link_libraries(SRB2SDL2 PRIVATE gme) +target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_GME) +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + # this sucks but gme doesn't use modern cmake to delineate public headers + target_include_directories(SRB2SDL2 PRIVATE "${libgme_SOURCE_DIR}") endif() -if(${SRB2_CONFIG_HAVE_ZLIB}) - if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - set(ZLIB_FOUND ON) - set(ZLIB_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/zlib) - if(${SRB2_SYSTEM_BITS} EQUAL 64) - set(ZLIB_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/zlib/win32 -lz64") - else() # 32-bit - set(ZLIB_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/zlib/win32 -lz32") - endif() - else() - find_package(ZLIB) - endif() - if(${ZLIB_FOUND}) - set(SRB2_HAVE_ZLIB ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_ZLIB) - else() - message(WARNING "You have specified that ZLIB is available but it was not found. SRB2 may not compile correctly.") - endif() -endif() +target_link_libraries(SRB2SDL2 PRIVATE openmpt) +target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_OPENMPT) -if(${SRB2_CONFIG_HAVE_PNG} AND ${SRB2_CONFIG_HAVE_ZLIB}) - if (${ZLIB_FOUND}) - if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - set(PNG_FOUND ON) - set(PNG_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/libpng-src) - if(${SRB2_SYSTEM_BITS} EQUAL 64) - set(PNG_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/libpng-src/projects -lpng64") - else() # 32-bit - set(PNG_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/libpng-src/projects -lpng32") - endif() - else() - find_package(PNG) - endif() - if(${PNG_FOUND}) - set(SRB2_HAVE_PNG ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_PNG) - target_compile_definitions(SRB2SDL2 PRIVATE -D_LARGEFILE64_SOURCE) - target_sources(SRB2SDL2 PRIVATE apng.c) - else() - message(WARNING "You have specified that PNG is available but it was not found. SRB2 may not compile correctly.") - endif() - endif() -endif() +target_link_libraries(SRB2SDL2 PRIVATE ZLIB::ZLIB PNG::PNG CURL::libcurl) +target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_ZLIB -DHAVE_PNG -DHAVE_CURL -D_LARGEFILE64_SOURCE) +target_sources(SRB2SDL2 PRIVATE apng.c) -if(${SRB2_CONFIG_HAVE_CURL}) - if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - set(CURL_FOUND ON) - 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 - set(CURL_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/curl/lib32 -lcurl") - endif() - else() - find_package(CURL) - endif() - if(${CURL_FOUND}) - set(SRB2_HAVE_CURL ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_CURL) - else() - message(WARNING "You have specified that CURL is available but it was not found. SRB2 may not compile correctly.") - endif() -endif() +set(SRB2_HAVE_THREADS ON) +target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_THREADS) -if(${SRB2_CONFIG_HAVE_THREADS}) - set(SRB2_HAVE_THREADS ON) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_THREADS) -endif() - -if(${SRB2_CONFIG_HWRENDER}) +if("${SRB2_CONFIG_HWRENDER}") target_compile_definitions(SRB2SDL2 PRIVATE -DHWRENDER) add_subdirectory(hardware) -endif() -if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_STATIC_OPENGL}) - find_package(OpenGL) - if(${OPENGL_FOUND}) - target_compile_definitions(SRB2SDL2 PRIVATE -DHWRENDER) - target_compile_definitions(SRB2SDL2 PRIVATE -DSTATIC_OPENGL) - else() - message(WARNING "You have specified static opengl but opengl was not found. Not setting HWRENDER.") + if("${SRB2_CONFIG_STATIC_OPENGL}") + find_package(OpenGL) + if(${OPENGL_FOUND}) + target_compile_definitions(SRB2SDL2 PRIVATE -DSTATIC_OPENGL) + else() + message(WARNING "You have specified static opengl but opengl was not found. Not setting HWRENDER.") + endif() endif() endif() if(${SRB2_CONFIG_USEASM}) #SRB2_ASM_FLAGS can be used to pass flags to either nasm or yasm. - if(${CMAKE_SYSTEM} MATCHES "Linux") + if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") set(SRB2_ASM_FLAGS "-DLINUX ${SRB2_ASM_FLAGS}") endif() @@ -213,7 +92,7 @@ if(${SRB2_CONFIG_USEASM}) set(SRB2_USEASM ON) target_compile_definitions(SRB2SDL2 PRIVATE -DUSEASM) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse3 -mfpmath=sse") + target_compile_options(SRB2SDL2 PRIVATE -msse3 -mfpmath=sse) target_sources(SRB2SDL2 PRIVATE ${SRB2_ASM_SOURCES} ${SRB2_NASM_SOURCES}) @@ -226,7 +105,7 @@ endif() # If using CCACHE, then force it. # https://github.com/Cockatrice/Cockatrice/pull/3052/files -if (${CMAKE_SYSTEM} MATCHES "Darwin") +if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") get_property(RULE_LAUNCH_COMPILE GLOBAL PROPERTY RULE_LAUNCH_COMPILE) if(RULE_LAUNCH_COMPILE) MESSAGE(STATUS "Force enabling CCache usage under macOS") @@ -248,34 +127,33 @@ endif() # Compatibility flag with later versions of GCC # We should really fix our code to not need this if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -mno-ms-bitfields) + target_compile_options(SRB2SDL2 PRIVATE -mno-ms-bitfields) + target_compile_options(SRB2SDL2 PRIVATE -Wno-trigraphs) endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wno-absolute-value) + target_compile_options(SRB2SDL2 PRIVATE -Wno-absolute-value) endif() -set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wno-trigraphs) - if(${SRB2_CONFIG_DEV_BUILD}) target_compile_definitions(SRB2SDL2 PRIVATE -DDEVELOP) endif() - target_compile_definitions(SRB2SDL2 PRIVATE -DCMAKECONFIG) -#add_library(SRB2Core STATIC -# ${SRB2_CORE_SOURCES} -# ${SRB2_CORE_HEADERS} -# ${SRB2_CORE_RENDER_SOURCES} -# ${SRB2_CORE_GAME_SOURCES} -# ${SRB2_LUA_SOURCES} -# ${SRB2_LUA_HEADERS} -# ${SRB2_BLUA_SOURCES} -# ${SRB2_BLUA_HEADERS} -#) - add_subdirectory(sdl) -if(NOT ${SRB2_SDL2_AVAILABLE}) - message(FATAL_ERROR "There are no targets available to build an SRB2 executable. :(") +# strip debug symbols into separate file when using gcc. +# to be consistent with Makefile, don't generate for OS X. +if((CMAKE_COMPILER_IS_GNUCC) AND NOT ("${CMAKE_SYSTEM_NAME}" MATCHES Darwin)) + if((${CMAKE_BUILD_TYPE} MATCHES Debug) OR (${CMAKE_BUILD_TYPE} MATCHES RelWithDebInfo)) + if(${CMAKE_BUILD_TYPE} MATCHES Debug) + set(OBJCOPY_ONLY_KEEP_DEBUG "--only-keep-debug") + endif() + message(STATUS "Will make separate debug symbols in *.debug") + add_custom_command(TARGET SRB2SDL2 POST_BUILD + COMMAND ${OBJCOPY} ${OBJCOPY_ONLY_KEEP_DEBUG} $ $.debug + COMMAND ${OBJCOPY} --strip-debug $ + COMMAND ${OBJCOPY} --add-gnu-debuglink=$.debug $ + ) + endif() endif() diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index 4f19d93df..be540b778 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -1,309 +1,136 @@ # Declare SDL2 interface sources -if(NOT ${SRB2_CONFIG_HAVE_MIXERX}) - set(SRB2_CONFIG_SDL2_USEMIXER ON CACHE BOOL "Use SDL2_mixer or regular sdl sound") -else() - set(SRB2_CONFIG_SDL2_USEMIXER OFF) -endif() - -if(${SRB2_CONFIG_SDL2_USEMIXER}) - if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - set(SDL2_MIXER_FOUND ON) - if(${SRB2_SYSTEM_BITS} EQUAL 64) - set(SDL2_MIXER_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/x86_64-w64-mingw32/include/SDL2) - set(SDL2_MIXER_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/x86_64-w64-mingw32/lib -lSDL2_mixer") - else() # 32-bit - set(SDL2_MIXER_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/i686-w64-mingw32/include/SDL2) - set(SDL2_MIXER_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/i686-w64-mingw32/lib -lSDL2_mixer") - endif() - else() - find_package(SDL2_mixer) - endif() - if(${SDL2_MIXER_FOUND}) - set(SRB2_HAVE_MIXER ON) - target_sources(SRB2SDL2 PRIVATE mixer_sound.c) - else() - message(WARNING "You specified that SDL2_mixer is available, but it was not found. Falling back to sdl sound.") - target_sources(SRB2SDL2 PRIVATE sdl_sound.c) - endif() -elseif(${MIXERX_FOUND}) - target_sources(SRB2SDL2 PRIVATE mixer_sound.c) -else() - target_sources(SRB2SDL2 PRIVATE sdl_sound.c) -endif() +target_sources(SRB2SDL2 PRIVATE mixer_sound.c) target_sourcefile(c) target_sources(SRB2SDL2 PRIVATE ogl_sdl.c) -if(${SRB2_CONFIG_HAVE_THREADS}) - target_sources(SRB2SDL2 PRIVATE i_threads.c) +target_sources(SRB2SDL2 PRIVATE i_threads.c) + +if(${SRB2_USEASM}) + set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES LANGUAGE C) + set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp") endif() -# Dependency -if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - set(SDL2_FOUND ON) - if(${SRB2_SYSTEM_BITS} EQUAL 64) - set(SDL2_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/include/SDL2) - set(SDL2_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/lib -lSDL2") - else() # 32-bit - set(SDL2_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/include/SDL2) - set(SDL2_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/lib -lSDL2") - endif() +if("${CMAKE_SYSTEM_NAME}" MATCHES Windows) + target_sources(SRB2SDL2 PRIVATE + ../win32/win_dbg.c + ../win32/Srb2win.rc) +endif() + +if("${CMAKE_SYSTEM_NAME}" MATCHES Darwin) + set(MACOSX_BUNDLE_ICON_FILE Srb2mac.icns) + set_source_files_properties(macosx/Srb2mac.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + target_sources(SRB2SDL2 PRIVATE + macosx/mac_alert.c + macosx/mac_alert.h + macosx/mac_resources.c + macosx/mac_resources.h + macosx/Srb2mac.icns + ) +endif() + +if("${CMAKE_SYSTEM_NAME}" MATCHES Windows) + set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME srb2win) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES Linux) + set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME lsdlsrb2) else() - find_package(SDL2) + set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME srb2) endif() -if(${SDL2_FOUND}) - if(${SRB2_USEASM}) - set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES LANGUAGE C) - set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp") - endif() +if("${CMAKE_SYSTEM_NAME}" MATCHES Darwin) + find_library(CORE_FOUNDATION_LIBRARY "CoreFoundation") + target_link_libraries(SRB2SDL2 PRIVATE + ${CORE_FOUNDATION_LIBRARY} + ) - if(${CMAKE_SYSTEM} MATCHES Windows) - target_sources(SRB2SDL2 PRIVATE - ../win32/win_dbg.c - ../win32/Srb2win.rc) - endif() + set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") - if(${CMAKE_SYSTEM} MATCHES Darwin) - set(MACOSX_BUNDLE_ICON_FILE Srb2mac.icns) - set_source_files_properties(macosx/Srb2mac.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - target_sources(SRB2SDL2 PRIVATE - macosx/mac_alert.c - macosx/mac_alert.h - macosx/mac_resources.c - macosx/mac_resources.h - macosx/Srb2mac.icns - ) - endif() + # Configure the app bundle icon and plist properties + target_sources(SRB2SDL2 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/macosx/Srb2mac.icns") + set_target_properties(SRB2SDL2 PROPERTIES + MACOSX_BUNDLE_ICON_FILE "Srb2mac" + MACOSX_BUNDLE_BUNDLE_NAME "Sonic Robo Blast 2" + MACOSX_BUNDLE_BUNDLE_VERSION ${SRB2_VERSION} - if(${CMAKE_SYSTEM} MATCHES Windows) - set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME srb2win) - elseif(${CMAKE_SYSTEM} MATCHES Linux) - set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME lsdlsrb2) + RESOURCE "${CMAKE_CURRENT_SOURCE_DIR}/macosx/Srb2mac.icns" + ) +endif() + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}" AND NOT "${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}") + target_link_libraries(SRB2SDL2 PRIVATE SDL2::SDL2-static SDL2_mixer::SDL2_mixer-static) +else() + target_link_libraries(SRB2SDL2 PRIVATE SDL2::SDL2 SDL2_mixer::SDL2_mixer) +endif() + +if("${CMAKE_SYSTEM_NAME}" MATCHES Linux) + target_link_libraries(SRB2SDL2 PRIVATE m rt) +endif() + +if(${SRB2_USEASM}) + if(${SRB2_CONFIG_YASM}) + set(ASM_ASSEMBLER_TEMP ${CMAKE_ASM_YASM_COMPILER}) + set(ASM_ASSEMBLER_OBJFORMAT ${CMAKE_ASM_YASM_OBJECT_FORMAT}) + set_source_files_properties(${SRB2_NASM_SOURCES} LANGUAGE ASM_YASM) else() - set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME srb2) + set(ASM_ASSEMBLER_TEMP ${CMAKE_ASM_NASM_COMPILER}) + set(ASM_ASSEMBLER_OBJFORMAT ${CMAKE_ASM_NASM_OBJECT_FORMAT}) + set_source_files_properties(${SRB2_NASM_SOURCES} LANGUAGE ASM_NASM) endif() +endif() - if(${CMAKE_SYSTEM} MATCHES Darwin) - find_library(CORE_LIB CoreFoundation) - target_link_libraries(SRB2SDL2 PRIVATE - ${CORE_LIB} - SDL2 - SDL2_mixer - ${GME_LIBRARIES} - ${OPENMPT_LIBRARIES} - ${MIXERX_LIBRARIES} - ${PNG_LIBRARIES} - ${ZLIB_LIBRARIES} - ${OPENGL_LIBRARIES} - ${CURL_LIBRARIES} - ) - set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}") - else() - target_link_libraries(SRB2SDL2 PRIVATE - ${SDL2_LIBRARIES} - ${SDL2_MIXER_LIBRARIES} - ${GME_LIBRARIES} - ${OPENMPT_LIBRARIES} - ${MIXERX_LIBRARIES} - ${PNG_LIBRARIES} - ${ZLIB_LIBRARIES} - ${OPENGL_LIBRARIES} - ${CURL_LIBRARIES} - ) +if("${CMAKE_SYSTEM_NAME}" MATCHES Windows) + target_link_libraries(SRB2SDL2 PRIVATE + ws2_32 + ) + target_compile_options(SRB2SDL2 PRIVATE + -U_WINDOWS + ) +endif() - if(${CMAKE_SYSTEM} MATCHES Linux) - target_link_libraries(SRB2SDL2 PRIVATE - m - rt +target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_MIXER -DSOUND=SOUND_MIXER) +target_compile_definitions(SRB2SDL2 PRIVATE -DDIRECTFULLSCREEN -DHAVE_SDL) + +#### Installation #### +if("${CMAKE_SYSTEM_NAME}" MATCHES Darwin) + install(TARGETS SRB2SDL2 + BUNDLE DESTINATION . + ) + set_property(TARGET SRB2SDL2 PROPERTY INSTALL_RPATH_USE_LINK_PATH ON) +else() + install(TARGETS SRB2SDL2 SRB2SDL2 + RUNTIME DESTINATION . + ) + if ((${CMAKE_BUILD_TYPE} MATCHES Debug) OR (${CMAKE_BUILD_TYPE} MATCHES RelWithDebInfo)) + set(SRB2_DEBUG_INSTALL OFF CACHE BOOL "Insert *.debug file into the install directory or package.") + if (${SRB2_DEBUG_INSTALL}) + install(FILES $.debug + DESTINATION . + OPTIONAL ) endif() endif() - - #target_link_libraries(SRB2SDL2 PRIVATE SRB2Core) - - if(${SRB2_USEASM}) - if(${SRB2_CONFIG_YASM}) - set(ASM_ASSEMBLER_TEMP ${CMAKE_ASM_YASM_COMPILER}) - set(ASM_ASSEMBLER_OBJFORMAT ${CMAKE_ASM_YASM_OBJECT_FORMAT}) - set_source_files_properties(${SRB2_NASM_SOURCES} LANGUAGE ASM_YASM) - else() - set(ASM_ASSEMBLER_TEMP ${CMAKE_ASM_NASM_COMPILER}) - set(ASM_ASSEMBLER_OBJFORMAT ${CMAKE_ASM_NASM_OBJECT_FORMAT}) - set_source_files_properties(${SRB2_NASM_SOURCES} LANGUAGE ASM_NASM) - endif() - endif() - - set_target_properties(SRB2SDL2 PROPERTIES VERSION ${SRB2_VERSION}) - - if(${CMAKE_SYSTEM} MATCHES Windows) - target_link_libraries(SRB2SDL2 PRIVATE - ws2_32 - ) - target_compile_options(SRB2SDL2 PRIVATE - -U_WINDOWS - ) - endif() - - target_include_directories(SRB2SDL2 PRIVATE - ${SDL2_INCLUDE_DIRS} - ${SDL2_MIXER_INCLUDE_DIRS} - ${GME_INCLUDE_DIRS} - ${OPENMPT_INCLUDE_DIRS} - ${MIXERX_INCLUDE_DIRS} - ${PNG_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS} - ${OPENGL_INCLUDE_DIRS} - ${CURL_INCLUDE_DIRS} - ) - - if((${SRB2_HAVE_MIXER}) OR (${SRB2_HAVE_MIXERX})) - target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_MIXER -DSOUND=SOUND_MIXER) - endif() - - target_compile_definitions(SRB2SDL2 PRIVATE - -DDIRECTFULLSCREEN -DHAVE_SDL - ) - - ## strip debug symbols into separate file when using gcc. - ## to be consistent with Makefile, don't generate for OS X. - if((CMAKE_COMPILER_IS_GNUCC) AND NOT (${CMAKE_SYSTEM} MATCHES Darwin)) - if((${CMAKE_BUILD_TYPE} MATCHES Debug) OR (${CMAKE_BUILD_TYPE} MATCHES RelWithDebInfo)) - if(${CMAKE_BUILD_TYPE} MATCHES Debug) - set(OBJCOPY_ONLY_KEEP_DEBUG "--only-keep-debug") - endif() - message(STATUS "Will make separate debug symbols in *.debug") - add_custom_command(TARGET SRB2SDL2 POST_BUILD - COMMAND ${OBJCOPY} ${OBJCOPY_ONLY_KEEP_DEBUG} $ $.debug - COMMAND ${OBJCOPY} --strip-debug $ - COMMAND ${OBJCOPY} --add-gnu-debuglink=$.debug $ - ) - endif() - endif() - - #### Installation #### - if(${CMAKE_SYSTEM} MATCHES Darwin) - install(TARGETS SRB2SDL2 - BUNDLE DESTINATION . - ) - else() - install(TARGETS SRB2SDL2 SRB2SDL2 - RUNTIME DESTINATION . - ) - if ((${CMAKE_BUILD_TYPE} MATCHES Debug) OR (${CMAKE_BUILD_TYPE} MATCHES RelWithDebInfo)) - set(SRB2_DEBUG_INSTALL OFF CACHE BOOL "Insert *.debug file into the install directory or package.") - if (${SRB2_DEBUG_INSTALL}) - install(FILES $.debug - DESTINATION . - OPTIONAL - ) - endif() - endif() - endif() - - if(${CMAKE_SYSTEM} MATCHES Windows) - set(win_extra_dll_list "") - macro(getwinlib dllname defaultname) - if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) - if (${CMAKE_GENERATOR} STREQUAL "MinGW Makefiles") - if(${SRB2_SYSTEM_BITS} EQUAL 64) - find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}" - HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/x86_64 - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/bin - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/x86_64-w64-mingw32/bin - HINTS ${CMAKE_SOURCE_DIR}/libs/libopenmpt/bin/x86_64/mingw - HINTS ${CMAKE_SOURCE_DIR}/libs/SDLMixerX/x86_64-w64-mingw32/bin - ) - else() - find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}" - HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/i686 - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/bin - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/i686-w64-mingw32/bin - HINTS ${CMAKE_SOURCE_DIR}/libs/libopenmpt/bin/x86/mingw - HINTS ${CMAKE_SOURCE_DIR}/libs/SDLMixerX/i686-w64-mingw32/bin - ) - endif() - else() - if(${SRB2_SYSTEM_BITS} EQUAL 64) - find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}" - HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/x86_64 - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/lib/x64 - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/lib/x64 - HINTS ${CMAKE_SOURCE_DIR}/libs/libopenmpt/bin/x86_64/mingw - HINTS ${CMAKE_SOURCE_DIR}/libs/SDLMixerX/x86_64-w64-mingw32/bin - ) - else() - find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}" - HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/i686 - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/lib/x86 - HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/lib/x86 - HINTS ${CMAKE_SOURCE_DIR}/libs/libopenmpt/bin/x86/mingw - HINTS ${CMAKE_SOURCE_DIR}/libs/SDLMixerX/i686-w64-mingw32/bin - ) - endif() - endif() - - list(APPEND win_extra_dll_list ${SRB2_SDL2_DLL_${dllname}}) - else() - find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}") - list(APPEND win_extra_dll_list ${SRB2_SDL2_DLL_${dllname}}) - endif() - endmacro() - getwinlib(SDL2 "SDL2.dll") - if(${SRB2_CONFIG_SDL2_USEMIXER}) - getwinlib(SDL2_mixer "SDL2_mixer.dll") - getwinlib(libogg_0 "libogg-0.dll") - getwinlib(libvorbis_0 "libvorbis-0.dll") - getwinlib(libvorbisfile_3 "libvorbisfile-3.dll") - endif() - if(${SRB2_CONFIG_HAVE_GME}) - getwinlib(libgme "libgme.dll") - endif() - if(${SRB2_CONFIG_HAVE_OPENMPT}) - getwinlib(libopenmpt "libopenmpt.dll") - endif() - if(${SRB2_CONFIG_HAVE_MIXERX}) - getwinlib(SDL2_mixer_ext "SDL2_mixer_ext.dll") - getwinlib(libfluidsynth-2 "libfluidsynth-2.dll") - getwinlib(libgcc_s_sjlj-1 "libgcc_s_sjlj-1.dll") - getwinlib(libstdc++-6 "libstdc++-6.dll") - endif() - - install(PROGRAMS - ${win_extra_dll_list} - DESTINATION . - ) - - # We also want to copy those DLLs to build directories on MSVC. - # So we'll add a post_build step. - copy_files_to_build_dir(SRB2SDL2 win_extra_dll_list) - endif() - - - # Mac bundle fixup - # HACK: THIS IS IMPORTANT! See the escaped \${CMAKE_INSTALL_PREFIX}? This - # makes it so that var is evaluated LATER during cpack, not right now! - # This fixes the quirk where the bundled libraries don't land in the final package - # https://cmake.org/pipermail/cmake/2011-March/043532.html - # - # HOWEVER: ${CPACK_PACKAGE_DESCRIPTION_SUMMARY} is NOT escaped, because that var - # is only available to us at this step. Read the link: ${CMAKE_INSTALL_PREFIX} at - # this current step points to the CMAKE build folder, NOT the folder that CPACK uses. - # Therefore, it makes sense to escape that var, but not the other. - if(${CMAKE_SYSTEM} MATCHES Darwin) - install(CODE " - include(BundleUtilities) - fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${CPACK_PACKAGE_DESCRIPTION_SUMMARY}.app\" - \"\" - /Library/Frameworks - )" - ) - endif() - - set(SRB2_SDL2_AVAILABLE YES PARENT_SCOPE) -else() - message(WARNING "SDL2 was not found, so the SDL2 target will not be available.") - set(SRB2_SDL2_AVAILABLE NO PARENT_SCOPE) endif() + +# Mac bundle fixup +# HACK: THIS IS IMPORTANT! See the escaped \${CMAKE_INSTALL_PREFIX}? This +# makes it so that var is evaluated LATER during cpack, not right now! +# This fixes the quirk where the bundled libraries don't land in the final package +# https://cmake.org/pipermail/cmake/2011-March/043532.html +# +# HOWEVER: ${CPACK_PACKAGE_DESCRIPTION_SUMMARY} is NOT escaped, because that var +# is only available to us at this step. Read the link: ${CMAKE_INSTALL_PREFIX} at +# this current step points to the CMAKE build folder, NOT the folder that CPACK uses. +# Therefore, it makes sense to escape that var, but not the other. +if("${CMAKE_SYSTEM_NAME}" MATCHES Darwin) + install(CODE " + include(BundleUtilities) + fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${CPACK_PACKAGE_DESCRIPTION_SUMMARY}.app\" + \"\" + /Library/Frameworks + )" + ) +endif() + +set(SRB2_SDL2_AVAILABLE YES PARENT_SCOPE) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 748cd374b..e56a5bc1b 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -74,7 +74,7 @@ #endif #ifdef HAVE_GME -#include "gme/gme.h" +#include #define GME_TREBLE 5.0f #define GME_BASS 1.0f #endif // HAVE_GME diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt new file mode 100644 index 000000000..208044113 --- /dev/null +++ b/thirdparty/CMakeLists.txt @@ -0,0 +1,499 @@ +macro(export) +endmacro() + +if(SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES) + set(SRB2_INTERNAL_LIBRARY_TYPE SHARED) + set(NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES OFF) +else() + set(SRB2_INTERNAL_LIBRARY_TYPE STATIC) + set(NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES ON) +endif() + + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + CPMAddPackage( + NAME SDL2 + VERSION 2.24.2 + URL "https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.24.2.zip" + EXCLUDE_FROM_ALL ON + OPTIONS + "BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}" + "SDL_SHARED ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}" + "SDL_STATIC ${NOT_SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}" + "SDL_TEST OFF" + "SDL2_DISABLE_SDL2MAIN ON" + "SDL2_DISABLE_INSTALL ON" + ) +endif() + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + CPMAddPackage( + NAME SDL2_mixer + VERSION 2.6.2 + URL "https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-2.6.2.zip" + EXCLUDE_FROM_ALL ON + OPTIONS + "BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}" + "SDL2MIXER_INSTALL OFF" + "SDL2MIXER_DEPS_SHARED OFF" + "SDL2MIXER_SAMPLES OFF" + "SDL2MIXER_VENDORED ON" + "SDL2MIXER_FLAC ON" + "SDL2MIXER_FLAC_LIBFLAC OFF" + "SDL2MIXER_FLAC_DRFLAC ON" + "SDL2MIXER_MOD OFF" + "SDL2MIXER_MP3 ON" + "SDL2MIXER_MP3_DRMP3 ON" + "SDL2MIXER_MIDI ON" + "SDL2MIXER_OPUS OFF" + "SDL2MIXER_VORBIS STB" + "SDL2MIXER_WAVE ON" + ) +endif() + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + CPMAddPackage( + NAME ZLIB + VERSION 1.2.13 + URL "https://github.com/madler/zlib/archive/refs/tags/v1.2.13.zip" + EXCLUDE_FROM_ALL + OPTIONS + # The assembly optimizations are unmaintained and slated to be removed + "ASM686 Off" + "AMD64 Off" + "SKIP_INSTALL_ALL ON" + ) + file(MAKE_DIRECTORY "${zlib_BINARY_DIR}/include") + file(COPY "${zlib_SOURCE_DIR}/zlib.h" DESTINATION "${zlib_BINARY_DIR}/include") + file(COPY "${zlib_BINARY_DIR}/zconf.h" DESTINATION "${zlib_BINARY_DIR}/include") + # honestly this should probably be built like png is + set_target_properties(zlib PROPERTIES EXCLUDE_FROM_ALL ON) + set_target_properties(minigzip PROPERTIES EXCLUDE_FROM_ALL ON) + set_target_properties(example PROPERTIES EXCLUDE_FROM_ALL ON) + # zlib cmake also adds these 64 targets separately + if(HAVE_OFF64_T) + set_target_properties(minigzip64 PROPERTIES EXCLUDE_FROM_ALL ON) + set_target_properties(example64 PROPERTIES EXCLUDE_FROM_ALL ON) + endif() + target_include_directories(zlib INTERFACE "${zlib_BINARY_DIR}/include") + target_include_directories(zlibstatic INTERFACE "${zlib_BINARY_DIR}/include") + if(SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES) + add_library(ZLIB::ZLIB ALIAS zlib) + else() + add_library(ZLIB::ZLIB ALIAS zlibstatic) + endif() +endif() + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + CPMAddPackage( + NAME png + VERSION 1.6.38 + URL "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.38.zip" + # png cmake build is broken on msys/mingw32 + DOWNLOAD_ONLY YES + ) + + if(png_ADDED) + # Since png's cmake build is broken, we're going to create a target manually + set( + PNG_SOURCES + + png.h + pngconf.h + + pngpriv.h + pngdebug.h + pnginfo.h + pngstruct.h + + png.c + pngerror.c + pngget.c + pngmem.c + pngpread.c + pngread.c + pngrio.c + pngrtran.c + pngrutil.c + pngset.c + pngtrans.c + pngwio.c + pngwrite.c + pngwtran.c + pngwutil.c + ) + list(TRANSFORM PNG_SOURCES PREPEND "${png_SOURCE_DIR}/") + + add_custom_command( + OUTPUT "${png_BINARY_DIR}/include/png.h" "${png_BINARY_DIR}/include/pngconf.h" + COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h" "${png_BINARY_DIR}/include" + DEPENDS "${png_SOURCE_DIR}/png.h" "${png_SOURCE_DIR}/pngconf.h" + VERBATIM + ) + add_custom_command( + OUTPUT "${png_BINARY_DIR}/include/pnglibconf.h" + COMMAND ${CMAKE_COMMAND} -E copy "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt" "${png_BINARY_DIR}/include/pnglibconf.h" + DEPENDS "${png_SOURCE_DIR}/scripts/pnglibconf.h.prebuilt" + VERBATIM + ) + list( + APPEND PNG_SOURCES + "${png_BINARY_DIR}/include/png.h" + "${png_BINARY_DIR}/include/pngconf.h" + "${png_BINARY_DIR}/include/pnglibconf.h" + ) + add_library(png "${SRB2_INTERNAL_LIBRARY_TYPE}" ${PNG_SOURCES}) + + # Disable ARM NEON since having it automatic breaks libpng external build on clang for some reason + target_compile_definitions(png PRIVATE -DPNG_ARM_NEON_OPT=0) + + # The png includes need to be available to consumers + target_include_directories(png PUBLIC "${png_BINARY_DIR}/include") + + # ... and these also need to be present only for png build + target_include_directories(png PRIVATE "${zlib_SOURCE_DIR}") + target_include_directories(png PRIVATE "${zlib_BINARY_DIR}") + target_include_directories(png PRIVATE "${png_BINARY_DIR}") + + target_link_libraries(png PRIVATE ZLIB::ZLIB) + add_library(PNG::PNG ALIAS png) + endif() +endif() + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + set( + internal_curl_options + + "BUILD_CURL_EXE OFF" + "BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}" + "CURL_DISABLE_TESTS ON" + "HTTP_ONLY ON" + "CURL_DISABLE_CRYPTO_AUTH ON" + "CURL_DISABLE_NTLM ON" + "ENABLE_MANUAL OFF" + "ENABLE_THREADED_RESOLVER OFF" + "CURL_USE_LIBPSL OFF" + "CURL_USE_LIBSSH2 OFF" + "USE_LIBIDN2 OFF" + "CURL_ENABLE_EXPORT_TARGET OFF" + ) + if(${CMAKE_SYSTEM} MATCHES Windows) + list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF") + list(APPEND internal_curl_options "CURL_USE_SCHANNEL ON") + endif() + if(${CMAKE_SYSTEM} MATCHES Darwin) + list(APPEND internal_curl_options "CURL_USE_OPENSSL OFF") + list(APPEND internal_curl_options "CURL_USE_SECTRANSP ON") + endif() + if(${CMAKE_SYSTEM} MATCHES Linux) + list(APPEND internal_curl_options "CURL_USE_OPENSSL ON") + endif() + + CPMAddPackage( + NAME curl + VERSION 7.86.0 + URL "https://github.com/curl/curl/archive/refs/tags/curl-7_86_0.zip" + EXCLUDE_FROM_ALL ON + OPTIONS ${internal_curl_options} + ) +endif() + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + CPMAddPackage( + NAME openmpt + VERSION 0.4.30 + URL "https://github.com/OpenMPT/openmpt/archive/refs/tags/libopenmpt-0.4.30.zip" + DOWNLOAD_ONLY ON + ) + + if(openmpt_ADDED) + set( + openmpt_SOURCES + + # minimp3 + # -DMPT_WITH_MINIMP3 + include/minimp3/minimp3.c + + common/mptStringParse.cpp + common/mptLibrary.cpp + common/Logging.cpp + common/Profiler.cpp + common/version.cpp + common/mptCPU.cpp + common/ComponentManager.cpp + common/mptOS.cpp + common/serialization_utils.cpp + common/mptStringFormat.cpp + common/FileReader.cpp + common/mptWine.cpp + common/mptPathString.cpp + common/mptAlloc.cpp + common/mptUUID.cpp + common/mptTime.cpp + common/mptString.cpp + common/mptFileIO.cpp + common/mptStringBuffer.cpp + common/mptRandom.cpp + common/mptIO.cpp + common/misc_util.cpp + + common/mptCRC.h + common/mptLibrary.h + common/mptIO.h + common/version.h + common/stdafx.h + common/ComponentManager.h + common/Endianness.h + common/mptStringFormat.h + common/mptMutex.h + common/mptUUID.h + common/mptExceptionText.h + common/BuildSettings.h + common/mptAlloc.h + common/mptTime.h + common/FileReaderFwd.h + common/Logging.h + common/mptException.h + common/mptWine.h + common/mptStringBuffer.h + common/misc_util.h + common/mptBaseMacros.h + common/mptMemory.h + common/mptFileIO.h + common/serialization_utils.h + common/mptSpan.h + common/mptThread.h + common/FlagSet.h + common/mptString.h + common/mptStringParse.h + common/mptBaseUtils.h + common/mptRandom.h + common/CompilerDetect.h + common/FileReader.h + common/mptAssert.h + common/mptPathString.h + common/Profiler.h + common/mptOS.h + common/mptBaseTypes.h + common/mptCPU.h + common/mptBufferIO.h + common/versionNumber.h + + soundlib/WAVTools.cpp + soundlib/ITTools.cpp + soundlib/AudioCriticalSection.cpp + soundlib/Load_stm.cpp + soundlib/MixerLoops.cpp + soundlib/Load_dbm.cpp + soundlib/ModChannel.cpp + soundlib/Load_gdm.cpp + soundlib/Snd_fx.cpp + soundlib/Load_mid.cpp + soundlib/mod_specifications.cpp + soundlib/Snd_flt.cpp + soundlib/Load_psm.cpp + soundlib/Load_far.cpp + soundlib/patternContainer.cpp + soundlib/Load_med.cpp + soundlib/Load_dmf.cpp + soundlib/Paula.cpp + soundlib/modcommand.cpp + soundlib/Message.cpp + soundlib/SoundFilePlayConfig.cpp + soundlib/Load_uax.cpp + soundlib/plugins/PlugInterface.cpp + soundlib/plugins/LFOPlugin.cpp + soundlib/plugins/PluginManager.cpp + soundlib/plugins/DigiBoosterEcho.cpp + soundlib/plugins/dmo/DMOPlugin.cpp + soundlib/plugins/dmo/Flanger.cpp + soundlib/plugins/dmo/Distortion.cpp + soundlib/plugins/dmo/ParamEq.cpp + soundlib/plugins/dmo/Gargle.cpp + soundlib/plugins/dmo/I3DL2Reverb.cpp + soundlib/plugins/dmo/Compressor.cpp + soundlib/plugins/dmo/WavesReverb.cpp + soundlib/plugins/dmo/Echo.cpp + soundlib/plugins/dmo/Chorus.cpp + soundlib/Load_ams.cpp + soundlib/tuningbase.cpp + soundlib/ContainerUMX.cpp + soundlib/Load_ptm.cpp + soundlib/ContainerXPK.cpp + soundlib/SampleFormatMP3.cpp + soundlib/tuning.cpp + soundlib/Sndfile.cpp + soundlib/ContainerMMCMP.cpp + soundlib/Load_amf.cpp + soundlib/Load_669.cpp + soundlib/modsmp_ctrl.cpp + soundlib/Load_mtm.cpp + soundlib/OggStream.cpp + soundlib/Load_plm.cpp + soundlib/Tables.cpp + soundlib/Load_c67.cpp + soundlib/Load_mod.cpp + soundlib/Load_sfx.cpp + soundlib/Sndmix.cpp + soundlib/load_j2b.cpp + soundlib/ModSequence.cpp + soundlib/SampleFormatFLAC.cpp + soundlib/ModInstrument.cpp + soundlib/Load_mo3.cpp + soundlib/ModSample.cpp + soundlib/Dlsbank.cpp + soundlib/Load_itp.cpp + soundlib/UpgradeModule.cpp + soundlib/MIDIMacros.cpp + soundlib/ContainerPP20.cpp + soundlib/RowVisitor.cpp + soundlib/Load_imf.cpp + soundlib/SampleFormatVorbis.cpp + soundlib/Load_dsm.cpp + soundlib/Load_mt2.cpp + soundlib/MixerSettings.cpp + soundlib/S3MTools.cpp + soundlib/Load_xm.cpp + soundlib/MIDIEvents.cpp + soundlib/pattern.cpp + soundlib/Load_digi.cpp + soundlib/Load_s3m.cpp + soundlib/tuningCollection.cpp + soundlib/SampleIO.cpp + soundlib/Dither.cpp + soundlib/Load_mdl.cpp + soundlib/OPL.cpp + soundlib/WindowedFIR.cpp + soundlib/SampleFormats.cpp + soundlib/Load_wav.cpp + soundlib/Load_it.cpp + soundlib/UMXTools.cpp + soundlib/Load_stp.cpp + soundlib/Load_okt.cpp + soundlib/Load_ult.cpp + soundlib/MixFuncTable.cpp + soundlib/SampleFormatOpus.cpp + soundlib/Fastmix.cpp + soundlib/Tagging.cpp + soundlib/ITCompression.cpp + soundlib/Load_dtm.cpp + soundlib/MPEGFrame.cpp + soundlib/XMTools.cpp + soundlib/SampleFormatMediaFoundation.cpp + soundlib/InstrumentExtensions.cpp + + soundlib/MixerInterface.h + soundlib/SoundFilePlayConfig.h + soundlib/ModSample.h + soundlib/MIDIEvents.h + soundlib/ModSampleCopy.h + soundlib/patternContainer.h + soundlib/ChunkReader.h + soundlib/ITCompression.h + soundlib/Dither.h + soundlib/S3MTools.h + soundlib/MPEGFrame.h + soundlib/WAVTools.h + soundlib/mod_specifications.h + soundlib/ITTools.h + soundlib/RowVisitor.h + soundlib/plugins/PluginMixBuffer.h + soundlib/plugins/PluginStructs.h + soundlib/plugins/LFOPlugin.h + soundlib/plugins/PlugInterface.h + soundlib/plugins/DigiBoosterEcho.h + soundlib/plugins/OpCodes.h + soundlib/plugins/dmo/Echo.h + soundlib/plugins/dmo/I3DL2Reverb.h + soundlib/plugins/dmo/WavesReverb.h + soundlib/plugins/dmo/ParamEq.h + soundlib/plugins/dmo/Gargle.h + soundlib/plugins/dmo/DMOPlugin.h + soundlib/plugins/dmo/Chorus.h + soundlib/plugins/dmo/Compressor.h + soundlib/plugins/dmo/Distortion.h + soundlib/plugins/dmo/Flanger.h + soundlib/plugins/PluginManager.h + soundlib/SampleIO.h + soundlib/Container.h + soundlib/ModSequence.h + soundlib/UMXTools.h + soundlib/Message.h + soundlib/modcommand.h + soundlib/XMTools.h + soundlib/Snd_defs.h + soundlib/MixFuncTable.h + soundlib/pattern.h + soundlib/modsmp_ctrl.h + soundlib/Tagging.h + soundlib/tuningcollection.h + soundlib/Mixer.h + soundlib/FloatMixer.h + soundlib/AudioCriticalSection.h + soundlib/Tables.h + soundlib/tuningbase.h + soundlib/WindowedFIR.h + soundlib/Sndfile.h + soundlib/Paula.h + soundlib/ModInstrument.h + soundlib/Dlsbank.h + soundlib/IntMixer.h + soundlib/OPL.h + soundlib/Resampler.h + soundlib/ModChannel.h + soundlib/MixerSettings.h + soundlib/AudioReadTarget.h + soundlib/MixerLoops.h + soundlib/tuning.h + soundlib/MIDIMacros.h + soundlib/OggStream.h + soundlib/Loaders.h + soundlib/BitReader.h + soundlib/opal.h + + sounddsp/AGC.cpp + sounddsp/EQ.cpp + sounddsp/DSP.cpp + sounddsp/Reverb.cpp + sounddsp/Reverb.h + sounddsp/EQ.h + sounddsp/DSP.h + sounddsp/AGC.h + + libopenmpt/libopenmpt_c.cpp + libopenmpt/libopenmpt_cxx.cpp + libopenmpt/libopenmpt_impl.cpp + libopenmpt/libopenmpt_ext_impl.cpp + ) + list(TRANSFORM openmpt_SOURCES PREPEND "${openmpt_SOURCE_DIR}/") + + # -DLIBOPENMPT_BUILD + configure_file("openmpt_svn_version.h" "svn_version.h") + add_library(openmpt "${SRB2_INTERNAL_LIBRARY_TYPE}" ${openmpt_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/svn_version.h) + target_compile_features(openmpt PRIVATE cxx_std_11) + target_compile_definitions(openmpt PRIVATE -DLIBOPENMPT_BUILD) + + target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/common") + target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/src") + target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}/include") + target_include_directories(openmpt PRIVATE "${openmpt_SOURCE_DIR}") + target_include_directories(openmpt PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + + # I wish this wasn't necessary, but it is + target_include_directories(openmpt PUBLIC "${openmpt_SOURCE_DIR}") + endif() +endif() + +if(NOT "${SRB2_CONFIG_SYSTEM_LIBRARIES}") + CPMAddPackage( + NAME libgme + VERSION 0.6.3 + URL "https://bitbucket.org/mpyne/game-music-emu/get/e76bdc0cb916e79aa540290e6edd0c445879d3ba.zip" + EXCLUDE_FROM_ALL ON + OPTIONS + "BUILD_SHARED_LIBS ${SRB2_CONFIG_SHARED_INTERNAL_LIBRARIES}" + "ENABLE_UBSAN OFF" + ) + target_compile_features(gme PRIVATE cxx_std_11) + target_link_libraries(gme PRIVATE ZLIB::ZLIB) +endif() diff --git a/thirdparty/openmpt_svn_version.h b/thirdparty/openmpt_svn_version.h new file mode 100644 index 000000000..a45ed9f22 --- /dev/null +++ b/thirdparty/openmpt_svn_version.h @@ -0,0 +1,10 @@ + +#pragma once +#define OPENMPT_VERSION_SVNVERSION "17963" +#define OPENMPT_VERSION_REVISION 17963 +#define OPENMPT_VERSION_DIRTY 0 +#define OPENMPT_VERSION_MIXEDREVISIONS 0 +#define OPENMPT_VERSION_URL "https://source.openmpt.org/svn/openmpt/tags/libopenmpt-0.4.32" +#define OPENMPT_VERSION_DATE "2022-09-25T14:19:05.052596Z" +#define OPENMPT_VERSION_IS_PACKAGE 1 + From b637df4ce0e64c6f26a73754400131bc24c5ebae Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Sat, 5 Nov 2022 21:20:53 +0000 Subject: [PATCH 06/89] removed a name from credits by request --- src/f_finale.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index b5715b863..ec325206b 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1182,7 +1182,6 @@ static const char *credits[] = { "Ben \"Mystic\" Geyer", "Nathan \"Jazz\" Giroux", "Vivian \"toaster\" Grannell", - "Dan \"Blitzzo\" Hagerstrand", "James \"SeventhSentinel\" Hall", "Kepa \"Nev3r\" Iceta", "Thomas \"Shadow Hog\" Igoe", From 3b9ed3e8029431e8122b6c313f988b8837be017c Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Tue, 27 Dec 2022 10:54:24 +0100 Subject: [PATCH 07/89] EV_DoFloor: Set dummy tag correctly for chained linedef executing --- src/p_floor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_floor.c b/src/p_floor.c index a367a08d8..869384b53 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -1660,7 +1660,7 @@ void EV_DoFloor(mtag_t tag, line_t *line, floor_e floortype) // chained linedef executing ability // Only set it on one of the moving sectors (the smallest numbered) if (line->args[3]) - dofloor->tag = firstone ? (INT16)line->args[3] : -1; + dofloor->tag = firstone ? (INT16)line->args[3] : 0; // flat changing ability dofloor->texture = line->args[4] ? line->frontsector->floorpic : -1; From 937127e987ac4e0e16101c44d184ac86b11d05cb Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Tue, 27 Dec 2022 16:49:52 +0100 Subject: [PATCH 08/89] Disable exit sectors in non-NiGHTS special stages because they interfere with the pits --- src/p_spec.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_spec.c b/src/p_spec.c index 5c9caa82f..fd114ced0 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4579,6 +4579,9 @@ static void P_ProcessExitSector(player_t *player, mtag_t sectag) if (player->bot) return; + if (G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS)) + return; + // Exit (for FOF exits; others are handled in P_PlayerThink in p_user.c) P_DoPlayerFinish(player); From ffbe140af59e1010092cf194903c96cc292e8d10 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Wed, 28 Dec 2022 16:12:05 +0100 Subject: [PATCH 09/89] Move snake minigame to its own files --- src/Sourcefile | 1 + src/d_clisrv.c | 525 +----------------------------------------------- src/snake.c | 535 +++++++++++++++++++++++++++++++++++++++++++++++++ src/snake.h | 20 ++ 4 files changed, 564 insertions(+), 517 deletions(-) create mode 100644 src/snake.c create mode 100644 src/snake.h diff --git a/src/Sourcefile b/src/Sourcefile index 9de90eee4..83e9ebc3b 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -83,6 +83,7 @@ i_tcp.c lzf.c vid_copy.s b_bot.c +snake.c lua_script.c lua_baselib.c lua_mathlib.c diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3091f3344..be10e4f9b 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -57,6 +57,7 @@ // cl loading screen #include "v_video.h" #include "f_finale.h" +#include "snake.h" #endif // @@ -543,505 +544,7 @@ static cl_mode_t cl_mode = CL_SEARCHING; static UINT16 cl_lastcheckedfilecount = 0; // used for full file list #ifndef NONET -#define SNAKE_SPEED 5 - -#define SNAKE_NUM_BLOCKS_X 20 -#define SNAKE_NUM_BLOCKS_Y 10 -#define SNAKE_BLOCK_SIZE 12 -#define SNAKE_BORDER_SIZE 12 - -#define SNAKE_MAP_WIDTH (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE) -#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE) - -#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE) -#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1) -#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48) -#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1) - -enum snake_bonustype_s { - SNAKE_BONUS_NONE = 0, - SNAKE_BONUS_SLOW, - SNAKE_BONUS_FAST, - SNAKE_BONUS_GHOST, - SNAKE_BONUS_NUKE, - SNAKE_BONUS_SCISSORS, - SNAKE_BONUS_REVERSE, - SNAKE_BONUS_EGGMAN, - SNAKE_NUM_BONUSES, -}; - -static const char *snake_bonuspatches[] = { - NULL, - "DL_SLOW", - "TVSSC0", - "TVIVC0", - "TVARC0", - "DL_SCISSORS", - "TVRCC0", - "TVEGC0", -}; - -static const char *snake_backgrounds[] = { - "RVPUMICF", - "FRSTRCKF", - "TAR", - "MMFLRB4", - "RVDARKF1", - "RVZWALF1", - "RVZWALF4", - "RVZWALF5", - "RVZGRS02", - "RVZGRS04", -}; - -typedef struct snake_s -{ - boolean paused; - boolean pausepressed; - tic_t time; - tic_t nextupdate; - boolean gameover; - UINT8 background; - - UINT16 snakelength; - enum snake_bonustype_s snakebonus; - tic_t snakebonustime; - UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - - UINT8 applex; - UINT8 appley; - - enum snake_bonustype_s bonustype; - UINT8 bonusx; - UINT8 bonusy; -} snake_t; - -static snake_t *snake = NULL; - -static void Snake_Initialise(void) -{ - if (!snake) - snake = malloc(sizeof(snake_t)); - - snake->paused = false; - snake->pausepressed = false; - snake->time = 0; - snake->nextupdate = SNAKE_SPEED; - snake->gameover = false; - snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds)); - - snake->snakelength = 1; - snake->snakebonus = SNAKE_BONUS_NONE; - snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X); - snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y); - snake->snakedir[0] = 0; - snake->snakedir[1] = 0; - - snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X); - snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y); - - snake->bonustype = SNAKE_BONUS_NONE; -} - -static UINT8 Snake_GetOppositeDir(UINT8 dir) -{ - if (dir == 1 || dir == 3) - return dir + 1; - else if (dir == 2 || dir == 4) - return dir - 1; - else - return 12 + 5 - dir; -} - -static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) -{ - UINT8 x, y; - UINT16 i; - - do - { - x = M_RandomKey(SNAKE_NUM_BLOCKS_X); - y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); - - for (i = 0; i < snake->snakelength; i++) - if (x == snake->snakex[i] && y == snake->snakey[i]) - break; - } while (i < snake->snakelength || (x == headx && y == heady) - || (x == snake->applex && y == snake->appley) - || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); - - *freex = x; - *freey = y; -} - -static void Snake_Handle(void) -{ - UINT8 x, y; - UINT8 oldx, oldy; - UINT16 i; - UINT16 joystate = 0; - - // Handle retry - if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER])) - { - Snake_Initialise(); - snake->pausepressed = true; // Avoid accidental pause on respawn - } - - // Handle pause - if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER]) - { - if (!snake->pausepressed) - snake->paused = !snake->paused; - snake->pausepressed = true; - } - else - snake->pausepressed = false; - - if (snake->paused) - return; - - snake->time++; - - x = snake->snakex[0]; - y = snake->snakey[0]; - oldx = snake->snakex[1]; - oldy = snake->snakey[1]; - - // Update direction - if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) - { - if (snake->snakelength < 2 || x <= oldx) - snake->snakedir[0] = 1; - } - else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) - { - if (snake->snakelength < 2 || x >= oldx) - snake->snakedir[0] = 2; - } - else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) - { - if (snake->snakelength < 2 || y <= oldy) - snake->snakedir[0] = 3; - } - else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) - { - if (snake->snakelength < 2 || y >= oldy) - snake->snakedir[0] = 4; - } - - if (snake->snakebonustime) - { - snake->snakebonustime--; - if (!snake->snakebonustime) - snake->snakebonus = SNAKE_BONUS_NONE; - } - - snake->nextupdate--; - if (snake->nextupdate) - return; - if (snake->snakebonus == SNAKE_BONUS_SLOW) - snake->nextupdate = SNAKE_SPEED * 2; - else if (snake->snakebonus == SNAKE_BONUS_FAST) - snake->nextupdate = SNAKE_SPEED * 2 / 3; - else - snake->nextupdate = SNAKE_SPEED; - - if (snake->gameover) - return; - - // Find new position - switch (snake->snakedir[0]) - { - case 1: - if (x > 0) - x--; - else - snake->gameover = true; - break; - case 2: - if (x < SNAKE_NUM_BLOCKS_X - 1) - x++; - else - snake->gameover = true; - break; - case 3: - if (y > 0) - y--; - else - snake->gameover = true; - break; - case 4: - if (y < SNAKE_NUM_BLOCKS_Y - 1) - y++; - else - snake->gameover = true; - break; - } - - // Check collision with snake - if (snake->snakebonus != SNAKE_BONUS_GHOST) - for (i = 1; i < snake->snakelength - 1; i++) - if (x == snake->snakex[i] && y == snake->snakey[i]) - { - if (snake->snakebonus == SNAKE_BONUS_SCISSORS) - { - snake->snakebonus = SNAKE_BONUS_NONE; - snake->snakelength = i; - S_StartSound(NULL, sfx_adderr); - } - else - snake->gameover = true; - } - - if (snake->gameover) - { - S_StartSound(NULL, sfx_lose); - return; - } - - // Check collision with apple - if (x == snake->applex && y == snake->appley) - { - if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) - { - snake->snakelength++; - snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; - snake->snakey [snake->snakelength - 1] = snake->snakey [snake->snakelength - 2]; - snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2]; - } - - // Spawn new apple - Snake_FindFreeSlot(&snake->applex, &snake->appley, x, y); - - // Spawn new bonus - if (!(snake->snakelength % 5)) - { - do - { - snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1; - } while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4 - && (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE)); - - Snake_FindFreeSlot(&snake->bonusx, &snake->bonusy, x, y); - } - - S_StartSound(NULL, sfx_s3k6b); - } - - if (snake->snakelength > 1 && snake->snakedir[0]) - { - UINT8 dir = snake->snakedir[0]; - - oldx = snake->snakex[1]; - oldy = snake->snakey[1]; - - // Move - for (i = snake->snakelength - 1; i > 0; i--) - { - snake->snakex[i] = snake->snakex[i - 1]; - snake->snakey[i] = snake->snakey[i - 1]; - snake->snakedir[i] = snake->snakedir[i - 1]; - } - - // Handle corners - if (x < oldx && dir == 3) - dir = 5; - else if (x > oldx && dir == 3) - dir = 6; - else if (x < oldx && dir == 4) - dir = 7; - else if (x > oldx && dir == 4) - dir = 8; - else if (y < oldy && dir == 1) - dir = 9; - else if (y < oldy && dir == 2) - dir = 10; - else if (y > oldy && dir == 1) - dir = 11; - else if (y > oldy && dir == 2) - dir = 12; - snake->snakedir[1] = dir; - } - - snake->snakex[0] = x; - snake->snakey[0] = y; - - // Check collision with bonus - if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy) - { - S_StartSound(NULL, sfx_ncchip); - - switch (snake->bonustype) - { - case SNAKE_BONUS_SLOW: - snake->snakebonus = SNAKE_BONUS_SLOW; - snake->snakebonustime = 20 * TICRATE; - break; - case SNAKE_BONUS_FAST: - snake->snakebonus = SNAKE_BONUS_FAST; - snake->snakebonustime = 20 * TICRATE; - break; - case SNAKE_BONUS_GHOST: - snake->snakebonus = SNAKE_BONUS_GHOST; - snake->snakebonustime = 10 * TICRATE; - break; - case SNAKE_BONUS_NUKE: - for (i = 0; i < snake->snakelength; i++) - { - snake->snakex [i] = snake->snakex [0]; - snake->snakey [i] = snake->snakey [0]; - snake->snakedir[i] = snake->snakedir[0]; - } - - S_StartSound(NULL, sfx_bkpoof); - break; - case SNAKE_BONUS_SCISSORS: - snake->snakebonus = SNAKE_BONUS_SCISSORS; - snake->snakebonustime = 60 * TICRATE; - break; - case SNAKE_BONUS_REVERSE: - for (i = 0; i < (snake->snakelength + 1) / 2; i++) - { - UINT16 i2 = snake->snakelength - 1 - i; - UINT8 tmpx = snake->snakex [i]; - UINT8 tmpy = snake->snakey [i]; - UINT8 tmpdir = snake->snakedir[i]; - - // Swap first segment with last segment - snake->snakex [i] = snake->snakex [i2]; - snake->snakey [i] = snake->snakey [i2]; - snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]); - snake->snakex [i2] = tmpx; - snake->snakey [i2] = tmpy; - snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir); - } - - snake->snakedir[0] = 0; - - S_StartSound(NULL, sfx_gravch); - break; - default: - if (snake->snakebonus != SNAKE_BONUS_GHOST) - { - snake->gameover = true; - S_StartSound(NULL, sfx_lose); - } - } - - snake->bonustype = SNAKE_BONUS_NONE; - } -} - -static void Snake_Draw(void) -{ - INT16 i; - - // Background - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - - V_DrawFlatFill( - SNAKE_LEFT_X + SNAKE_BORDER_SIZE, - SNAKE_TOP_Y + SNAKE_BORDER_SIZE, - SNAKE_MAP_WIDTH, - SNAKE_MAP_HEIGHT, - W_GetNumForName(snake_backgrounds[snake->background]) - ); - - // Borders - V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top - V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right - V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom - V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left - - // Apple - V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - FRACUNIT / 4, - 0, - W_CachePatchLongName("DL_APPLE", PU_HUDGFX), - NULL - ); - - // Bonus - if (snake->bonustype != SNAKE_BONUS_NONE) - V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 ) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT, - FRACUNIT / 2, - 0, - W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX), - NULL - ); - - // Snake - if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over - { - for (i = snake->snakelength - 1; i >= 0; i--) - { - const char *patchname; - UINT8 dir = snake->snakedir[i]; - - if (i == 0) // Head - { - switch (dir) - { - case 1: patchname = "DL_SNAKEHEAD_L"; break; - case 2: patchname = "DL_SNAKEHEAD_R"; break; - case 3: patchname = "DL_SNAKEHEAD_T"; break; - case 4: patchname = "DL_SNAKEHEAD_B"; break; - default: patchname = "DL_SNAKEHEAD_M"; - } - } - else // Body - { - switch (dir) - { - case 1: patchname = "DL_SNAKEBODY_L"; break; - case 2: patchname = "DL_SNAKEBODY_R"; break; - case 3: patchname = "DL_SNAKEBODY_T"; break; - case 4: patchname = "DL_SNAKEBODY_B"; break; - case 5: patchname = "DL_SNAKEBODY_LT"; break; - case 6: patchname = "DL_SNAKEBODY_RT"; break; - case 7: patchname = "DL_SNAKEBODY_LB"; break; - case 8: patchname = "DL_SNAKEBODY_RB"; break; - case 9: patchname = "DL_SNAKEBODY_TL"; break; - case 10: patchname = "DL_SNAKEBODY_TR"; break; - case 11: patchname = "DL_SNAKEBODY_BL"; break; - case 12: patchname = "DL_SNAKEBODY_BR"; break; - default: patchname = "DL_SNAKEBODY_B"; - } - } - - V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2, - snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0, - W_CachePatchLongName(patchname, PU_HUDGFX), - NULL - ); - } - } - - // Length - V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); - - // Bonus - if (snake->snakebonus != SNAKE_BONUS_NONE - && (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2)) - V_DrawFixedPatch( - (SNAKE_RIGHT_X + 10) * FRACUNIT, - (SNAKE_TOP_Y + 24) * FRACUNIT, - FRACUNIT / 2, - 0, - W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX), - NULL - ); -} +static void *snake = NULL; static void CL_DrawConnectionStatusBox(void) { @@ -1167,7 +670,7 @@ static inline void CL_DrawConnectionStatus(void) char *filename; if (snake) - Snake_Draw(); + Snake_Draw(snake); // Draw the bottom box. CL_DrawConnectionStatusBox(); @@ -1213,7 +716,7 @@ static inline void CL_DrawConnectionStatus(void) else { if (snake) - Snake_Draw(); + Snake_Draw(snake); CL_DrawConnectionStatusBox(); V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, @@ -1950,7 +1453,7 @@ static void M_ConfirmConnect(event_t *ev) if (CL_SendFileRequest()) { cl_mode = CL_DOWNLOADFILES; - Snake_Initialise(); + Snake_Allocate(&snake); } } else @@ -2300,13 +1803,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (waitmore) break; // exit the case -#ifndef NONET - if (snake) - { - free(snake); - snake = NULL; - } -#endif + Snake_Free(&snake); cl_mode = CL_LOADFILES; break; @@ -2401,13 +1898,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic CONS_Printf(M_GetText("Network game synchronization aborted.\n")); M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); -#ifndef NONET - if (snake) - { - free(snake); - snake = NULL; - } -#endif + Snake_Free(&snake); D_QuitNetGame(); CL_Reset(); @@ -2417,7 +1908,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic } #ifndef NONET else if (cl_mode == CL_DOWNLOADFILES && snake) - Snake_Handle(); + Snake_Update(snake); #endif if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) diff --git a/src/snake.c b/src/snake.c new file mode 100644 index 000000000..2c80adeb9 --- /dev/null +++ b/src/snake.c @@ -0,0 +1,535 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2023-2023 by Louis-Antoine de Moulins de Rochefort. +// +// 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 snake.c +/// \brief Snake minigame for the download screen. + +#include "snake.h" +#include "g_input.h" +#include "m_random.h" +#include "s_sound.h" +#include "screen.h" +#include "v_video.h" +#include "w_wad.h" +#include "z_zone.h" + +#define SNAKE_SPEED 5 + +#define SNAKE_NUM_BLOCKS_X 20 +#define SNAKE_NUM_BLOCKS_Y 10 +#define SNAKE_BLOCK_SIZE 12 +#define SNAKE_BORDER_SIZE 12 + +#define SNAKE_MAP_WIDTH (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE) +#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE) + +#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE) +#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1) +#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48) +#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1) + +enum snake_bonustype_s { + SNAKE_BONUS_NONE = 0, + SNAKE_BONUS_SLOW, + SNAKE_BONUS_FAST, + SNAKE_BONUS_GHOST, + SNAKE_BONUS_NUKE, + SNAKE_BONUS_SCISSORS, + SNAKE_BONUS_REVERSE, + SNAKE_BONUS_EGGMAN, + SNAKE_NUM_BONUSES, +}; + +typedef struct snake_s +{ + boolean paused; + boolean pausepressed; + tic_t time; + tic_t nextupdate; + boolean gameover; + UINT8 background; + + UINT16 snakelength; + enum snake_bonustype_s snakebonus; + tic_t snakebonustime; + UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + + UINT8 applex; + UINT8 appley; + + enum snake_bonustype_s bonustype; + UINT8 bonusx; + UINT8 bonusy; +} snake_t; + +static const char *snake_bonuspatches[] = { + NULL, + "DL_SLOW", + "TVSSC0", + "TVIVC0", + "TVARC0", + "DL_SCISSORS", + "TVRCC0", + "TVEGC0", +}; + +static const char *snake_backgrounds[] = { + "RVPUMICF", + "FRSTRCKF", + "TAR", + "MMFLRB4", + "RVDARKF1", + "RVZWALF1", + "RVZWALF4", + "RVZWALF5", + "RVZGRS02", + "RVZGRS04", +}; + +static void Snake_Initialise(snake_t *snake) +{ + snake->paused = false; + snake->pausepressed = false; + snake->time = 0; + snake->nextupdate = SNAKE_SPEED; + snake->gameover = false; + snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds)); + + snake->snakelength = 1; + snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X); + snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + snake->snakedir[0] = 0; + snake->snakedir[1] = 0; + + snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X); + snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + + snake->bonustype = SNAKE_BONUS_NONE; +} + +static UINT8 Snake_GetOppositeDir(UINT8 dir) +{ + if (dir == 1 || dir == 3) + return dir + 1; + else if (dir == 2 || dir == 4) + return dir - 1; + else + return 12 + 5 - dir; +} + +static void Snake_FindFreeSlot(snake_t *snake, UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) +{ + UINT8 x, y; + UINT16 i; + + do + { + x = M_RandomKey(SNAKE_NUM_BLOCKS_X); + y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + + for (i = 0; i < snake->snakelength; i++) + if (x == snake->snakex[i] && y == snake->snakey[i]) + break; + } while (i < snake->snakelength || (x == headx && y == heady) + || (x == snake->applex && y == snake->appley) + || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); + + *freex = x; + *freey = y; +} + +void Snake_Allocate(void **opaque) +{ + if (*opaque) + Snake_Free(opaque); + *opaque = malloc(sizeof(snake_t)); + Snake_Initialise(*opaque); +} + +void Snake_Update(void *opaque) +{ + UINT8 x, y; + UINT8 oldx, oldy; + UINT16 i; + UINT16 joystate = 0; + + snake_t *snake = opaque; + + // Handle retry + if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER])) + { + Snake_Initialise(snake); + snake->pausepressed = true; // Avoid accidental pause on respawn + } + + // Handle pause + if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER]) + { + if (!snake->pausepressed) + snake->paused = !snake->paused; + snake->pausepressed = true; + } + else + snake->pausepressed = false; + + if (snake->paused) + return; + + snake->time++; + + x = snake->snakex[0]; + y = snake->snakey[0]; + oldx = snake->snakex[1]; + oldy = snake->snakey[1]; + + // Update direction + if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) + { + if (snake->snakelength < 2 || x <= oldx) + snake->snakedir[0] = 1; + } + else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) + { + if (snake->snakelength < 2 || x >= oldx) + snake->snakedir[0] = 2; + } + else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) + { + if (snake->snakelength < 2 || y <= oldy) + snake->snakedir[0] = 3; + } + else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) + { + if (snake->snakelength < 2 || y >= oldy) + snake->snakedir[0] = 4; + } + + if (snake->snakebonustime) + { + snake->snakebonustime--; + if (!snake->snakebonustime) + snake->snakebonus = SNAKE_BONUS_NONE; + } + + snake->nextupdate--; + if (snake->nextupdate) + return; + if (snake->snakebonus == SNAKE_BONUS_SLOW) + snake->nextupdate = SNAKE_SPEED * 2; + else if (snake->snakebonus == SNAKE_BONUS_FAST) + snake->nextupdate = SNAKE_SPEED * 2 / 3; + else + snake->nextupdate = SNAKE_SPEED; + + if (snake->gameover) + return; + + // Find new position + switch (snake->snakedir[0]) + { + case 1: + if (x > 0) + x--; + else + snake->gameover = true; + break; + case 2: + if (x < SNAKE_NUM_BLOCKS_X - 1) + x++; + else + snake->gameover = true; + break; + case 3: + if (y > 0) + y--; + else + snake->gameover = true; + break; + case 4: + if (y < SNAKE_NUM_BLOCKS_Y - 1) + y++; + else + snake->gameover = true; + break; + } + + // Check collision with snake + if (snake->snakebonus != SNAKE_BONUS_GHOST) + for (i = 1; i < snake->snakelength - 1; i++) + if (x == snake->snakex[i] && y == snake->snakey[i]) + { + if (snake->snakebonus == SNAKE_BONUS_SCISSORS) + { + snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakelength = i; + S_StartSound(NULL, sfx_adderr); + } + else + snake->gameover = true; + } + + if (snake->gameover) + { + S_StartSound(NULL, sfx_lose); + return; + } + + // Check collision with apple + if (x == snake->applex && y == snake->appley) + { + if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) + { + snake->snakelength++; + snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; + snake->snakey [snake->snakelength - 1] = snake->snakey [snake->snakelength - 2]; + snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2]; + } + + // Spawn new apple + Snake_FindFreeSlot(snake, &snake->applex, &snake->appley, x, y); + + // Spawn new bonus + if (!(snake->snakelength % 5)) + { + do + { + snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1; + } while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4 + && (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE)); + + Snake_FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y); + } + + S_StartSound(NULL, sfx_s3k6b); + } + + if (snake->snakelength > 1 && snake->snakedir[0]) + { + UINT8 dir = snake->snakedir[0]; + + oldx = snake->snakex[1]; + oldy = snake->snakey[1]; + + // Move + for (i = snake->snakelength - 1; i > 0; i--) + { + snake->snakex[i] = snake->snakex[i - 1]; + snake->snakey[i] = snake->snakey[i - 1]; + snake->snakedir[i] = snake->snakedir[i - 1]; + } + + // Handle corners + if (x < oldx && dir == 3) + dir = 5; + else if (x > oldx && dir == 3) + dir = 6; + else if (x < oldx && dir == 4) + dir = 7; + else if (x > oldx && dir == 4) + dir = 8; + else if (y < oldy && dir == 1) + dir = 9; + else if (y < oldy && dir == 2) + dir = 10; + else if (y > oldy && dir == 1) + dir = 11; + else if (y > oldy && dir == 2) + dir = 12; + snake->snakedir[1] = dir; + } + + snake->snakex[0] = x; + snake->snakey[0] = y; + + // Check collision with bonus + if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy) + { + S_StartSound(NULL, sfx_ncchip); + + switch (snake->bonustype) + { + case SNAKE_BONUS_SLOW: + snake->snakebonus = SNAKE_BONUS_SLOW; + snake->snakebonustime = 20 * TICRATE; + break; + case SNAKE_BONUS_FAST: + snake->snakebonus = SNAKE_BONUS_FAST; + snake->snakebonustime = 20 * TICRATE; + break; + case SNAKE_BONUS_GHOST: + snake->snakebonus = SNAKE_BONUS_GHOST; + snake->snakebonustime = 10 * TICRATE; + break; + case SNAKE_BONUS_NUKE: + for (i = 0; i < snake->snakelength; i++) + { + snake->snakex [i] = snake->snakex [0]; + snake->snakey [i] = snake->snakey [0]; + snake->snakedir[i] = snake->snakedir[0]; + } + + S_StartSound(NULL, sfx_bkpoof); + break; + case SNAKE_BONUS_SCISSORS: + snake->snakebonus = SNAKE_BONUS_SCISSORS; + snake->snakebonustime = 60 * TICRATE; + break; + case SNAKE_BONUS_REVERSE: + for (i = 0; i < (snake->snakelength + 1) / 2; i++) + { + UINT16 i2 = snake->snakelength - 1 - i; + UINT8 tmpx = snake->snakex [i]; + UINT8 tmpy = snake->snakey [i]; + UINT8 tmpdir = snake->snakedir[i]; + + // Swap first segment with last segment + snake->snakex [i] = snake->snakex [i2]; + snake->snakey [i] = snake->snakey [i2]; + snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]); + snake->snakex [i2] = tmpx; + snake->snakey [i2] = tmpy; + snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir); + } + + snake->snakedir[0] = 0; + + S_StartSound(NULL, sfx_gravch); + break; + default: + if (snake->snakebonus != SNAKE_BONUS_GHOST) + { + snake->gameover = true; + S_StartSound(NULL, sfx_lose); + } + } + + snake->bonustype = SNAKE_BONUS_NONE; + } +} + +void Snake_Draw(void *opaque) +{ + INT16 i; + + snake_t *snake = opaque; + + // Background + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + + V_DrawFlatFill( + SNAKE_LEFT_X + SNAKE_BORDER_SIZE, + SNAKE_TOP_Y + SNAKE_BORDER_SIZE, + SNAKE_MAP_WIDTH, + SNAKE_MAP_HEIGHT, + W_GetNumForName(snake_backgrounds[snake->background]) + ); + + // Borders + V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top + V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right + V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom + V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left + + // Apple + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + FRACUNIT / 4, + 0, + W_CachePatchLongName("DL_APPLE", PU_HUDGFX), + NULL + ); + + // Bonus + if (snake->bonustype != SNAKE_BONUS_NONE) + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 ) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT, + FRACUNIT / 2, + 0, + W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX), + NULL + ); + + // Snake + if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over + { + for (i = snake->snakelength - 1; i >= 0; i--) + { + const char *patchname; + UINT8 dir = snake->snakedir[i]; + + if (i == 0) // Head + { + switch (dir) + { + case 1: patchname = "DL_SNAKEHEAD_L"; break; + case 2: patchname = "DL_SNAKEHEAD_R"; break; + case 3: patchname = "DL_SNAKEHEAD_T"; break; + case 4: patchname = "DL_SNAKEHEAD_B"; break; + default: patchname = "DL_SNAKEHEAD_M"; + } + } + else // Body + { + switch (dir) + { + case 1: patchname = "DL_SNAKEBODY_L"; break; + case 2: patchname = "DL_SNAKEBODY_R"; break; + case 3: patchname = "DL_SNAKEBODY_T"; break; + case 4: patchname = "DL_SNAKEBODY_B"; break; + case 5: patchname = "DL_SNAKEBODY_LT"; break; + case 6: patchname = "DL_SNAKEBODY_RT"; break; + case 7: patchname = "DL_SNAKEBODY_LB"; break; + case 8: patchname = "DL_SNAKEBODY_RB"; break; + case 9: patchname = "DL_SNAKEBODY_TL"; break; + case 10: patchname = "DL_SNAKEBODY_TR"; break; + case 11: patchname = "DL_SNAKEBODY_BL"; break; + case 12: patchname = "DL_SNAKEBODY_BR"; break; + default: patchname = "DL_SNAKEBODY_B"; + } + } + + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2, + snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0, + W_CachePatchLongName(patchname, PU_HUDGFX), + NULL + ); + } + } + + // Length + V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); + + // Bonus + if (snake->snakebonus != SNAKE_BONUS_NONE + && (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2)) + V_DrawFixedPatch( + (SNAKE_RIGHT_X + 10) * FRACUNIT, + (SNAKE_TOP_Y + 24) * FRACUNIT, + FRACUNIT / 2, + 0, + W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX), + NULL + ); +} + +void Snake_Free(void **opaque) +{ + if (*opaque) + { + free(opaque); + *opaque = NULL; + } +} diff --git a/src/snake.h b/src/snake.h new file mode 100644 index 000000000..a3106bb0f --- /dev/null +++ b/src/snake.h @@ -0,0 +1,20 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2023-2023 by Louis-Antoine de Moulins de Rochefort. +// +// 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 snake.h +/// \brief Snake minigame for the download screen. + +#ifndef __SNAKE__ +#define __SNAKE__ + +void Snake_Allocate(void **opaque); +void Snake_Update(void *opaque); +void Snake_Draw(void *opaque); +void Snake_Free(void **opaque); + +#endif From 33c76453e11bc06d4ec9b8c26937dc443581c8f4 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Wed, 28 Dec 2022 18:50:00 +0100 Subject: [PATCH 10/89] Remove snake_ prefix --- src/snake.c | 198 ++++++++++++++++++++++++++-------------------------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/src/snake.c b/src/snake.c index 2c80adeb9..21e79401d 100644 --- a/src/snake.c +++ b/src/snake.c @@ -18,31 +18,31 @@ #include "w_wad.h" #include "z_zone.h" -#define SNAKE_SPEED 5 +#define SPEED 5 -#define SNAKE_NUM_BLOCKS_X 20 -#define SNAKE_NUM_BLOCKS_Y 10 -#define SNAKE_BLOCK_SIZE 12 -#define SNAKE_BORDER_SIZE 12 +#define NUM_BLOCKS_X 20 +#define NUM_BLOCKS_Y 10 +#define BLOCK_SIZE 12 +#define BORDER_SIZE 12 -#define SNAKE_MAP_WIDTH (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE) -#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE) +#define MAP_WIDTH (NUM_BLOCKS_X * BLOCK_SIZE) +#define MAP_HEIGHT (NUM_BLOCKS_Y * BLOCK_SIZE) -#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE) -#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1) -#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48) -#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1) +#define LEFT_X ((BASEVIDWIDTH - MAP_WIDTH) / 2 - BORDER_SIZE) +#define RIGHT_X (LEFT_X + MAP_WIDTH + BORDER_SIZE * 2 - 1) +#define BOTTOM_Y (BASEVIDHEIGHT - 48) +#define TOP_Y (BOTTOM_Y - MAP_HEIGHT - BORDER_SIZE * 2 + 1) -enum snake_bonustype_s { - SNAKE_BONUS_NONE = 0, - SNAKE_BONUS_SLOW, - SNAKE_BONUS_FAST, - SNAKE_BONUS_GHOST, - SNAKE_BONUS_NUKE, - SNAKE_BONUS_SCISSORS, - SNAKE_BONUS_REVERSE, - SNAKE_BONUS_EGGMAN, - SNAKE_NUM_BONUSES, +enum bonustype_s { + BONUS_NONE = 0, + BONUS_SLOW, + BONUS_FAST, + BONUS_GHOST, + BONUS_NUKE, + BONUS_SCISSORS, + BONUS_REVERSE, + BONUS_EGGMAN, + NUM_BONUSES, }; typedef struct snake_s @@ -55,21 +55,21 @@ typedef struct snake_s UINT8 background; UINT16 snakelength; - enum snake_bonustype_s snakebonus; + enum bonustype_s snakebonus; tic_t snakebonustime; - UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; - UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + UINT8 snakex[NUM_BLOCKS_X * NUM_BLOCKS_Y]; + UINT8 snakey[NUM_BLOCKS_X * NUM_BLOCKS_Y]; + UINT8 snakedir[NUM_BLOCKS_X * NUM_BLOCKS_Y]; UINT8 applex; UINT8 appley; - enum snake_bonustype_s bonustype; + enum bonustype_s bonustype; UINT8 bonusx; UINT8 bonusy; } snake_t; -static const char *snake_bonuspatches[] = { +static const char *bonuspatches[] = { NULL, "DL_SLOW", "TVSSC0", @@ -80,7 +80,7 @@ static const char *snake_bonuspatches[] = { "TVEGC0", }; -static const char *snake_backgrounds[] = { +static const char *backgrounds[] = { "RVPUMICF", "FRSTRCKF", "TAR", @@ -93,29 +93,29 @@ static const char *snake_backgrounds[] = { "RVZGRS04", }; -static void Snake_Initialise(snake_t *snake) +static void Initialise(snake_t *snake) { snake->paused = false; snake->pausepressed = false; snake->time = 0; - snake->nextupdate = SNAKE_SPEED; + snake->nextupdate = SPEED; snake->gameover = false; - snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds)); + snake->background = M_RandomKey(sizeof(backgrounds) / sizeof(*backgrounds)); snake->snakelength = 1; - snake->snakebonus = SNAKE_BONUS_NONE; - snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X); - snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + snake->snakebonus = BONUS_NONE; + snake->snakex[0] = M_RandomKey(NUM_BLOCKS_X); + snake->snakey[0] = M_RandomKey(NUM_BLOCKS_Y); snake->snakedir[0] = 0; snake->snakedir[1] = 0; - snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X); - snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + snake->applex = M_RandomKey(NUM_BLOCKS_X); + snake->appley = M_RandomKey(NUM_BLOCKS_Y); - snake->bonustype = SNAKE_BONUS_NONE; + snake->bonustype = BONUS_NONE; } -static UINT8 Snake_GetOppositeDir(UINT8 dir) +static UINT8 GetOppositeDir(UINT8 dir) { if (dir == 1 || dir == 3) return dir + 1; @@ -125,22 +125,22 @@ static UINT8 Snake_GetOppositeDir(UINT8 dir) return 12 + 5 - dir; } -static void Snake_FindFreeSlot(snake_t *snake, UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) +static void FindFreeSlot(snake_t *snake, UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) { UINT8 x, y; UINT16 i; do { - x = M_RandomKey(SNAKE_NUM_BLOCKS_X); - y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + x = M_RandomKey(NUM_BLOCKS_X); + y = M_RandomKey(NUM_BLOCKS_Y); for (i = 0; i < snake->snakelength; i++) if (x == snake->snakex[i] && y == snake->snakey[i]) break; } while (i < snake->snakelength || (x == headx && y == heady) || (x == snake->applex && y == snake->appley) - || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); + || (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); *freex = x; *freey = y; @@ -151,7 +151,7 @@ void Snake_Allocate(void **opaque) if (*opaque) Snake_Free(opaque); *opaque = malloc(sizeof(snake_t)); - Snake_Initialise(*opaque); + Initialise(*opaque); } void Snake_Update(void *opaque) @@ -166,7 +166,7 @@ void Snake_Update(void *opaque) // Handle retry if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER])) { - Snake_Initialise(snake); + Initialise(snake); snake->pausepressed = true; // Avoid accidental pause on respawn } @@ -216,18 +216,18 @@ void Snake_Update(void *opaque) { snake->snakebonustime--; if (!snake->snakebonustime) - snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakebonus = BONUS_NONE; } snake->nextupdate--; if (snake->nextupdate) return; - if (snake->snakebonus == SNAKE_BONUS_SLOW) - snake->nextupdate = SNAKE_SPEED * 2; - else if (snake->snakebonus == SNAKE_BONUS_FAST) - snake->nextupdate = SNAKE_SPEED * 2 / 3; + if (snake->snakebonus == BONUS_SLOW) + snake->nextupdate = SPEED * 2; + else if (snake->snakebonus == BONUS_FAST) + snake->nextupdate = SPEED * 2 / 3; else - snake->nextupdate = SNAKE_SPEED; + snake->nextupdate = SPEED; if (snake->gameover) return; @@ -242,7 +242,7 @@ void Snake_Update(void *opaque) snake->gameover = true; break; case 2: - if (x < SNAKE_NUM_BLOCKS_X - 1) + if (x < NUM_BLOCKS_X - 1) x++; else snake->gameover = true; @@ -254,7 +254,7 @@ void Snake_Update(void *opaque) snake->gameover = true; break; case 4: - if (y < SNAKE_NUM_BLOCKS_Y - 1) + if (y < NUM_BLOCKS_Y - 1) y++; else snake->gameover = true; @@ -262,13 +262,13 @@ void Snake_Update(void *opaque) } // Check collision with snake - if (snake->snakebonus != SNAKE_BONUS_GHOST) + if (snake->snakebonus != BONUS_GHOST) for (i = 1; i < snake->snakelength - 1; i++) if (x == snake->snakex[i] && y == snake->snakey[i]) { - if (snake->snakebonus == SNAKE_BONUS_SCISSORS) + if (snake->snakebonus == BONUS_SCISSORS) { - snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakebonus = BONUS_NONE; snake->snakelength = i; S_StartSound(NULL, sfx_adderr); } @@ -285,7 +285,7 @@ void Snake_Update(void *opaque) // Check collision with apple if (x == snake->applex && y == snake->appley) { - if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) + if (snake->snakelength + 3 < NUM_BLOCKS_X * NUM_BLOCKS_Y) { snake->snakelength++; snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; @@ -294,18 +294,18 @@ void Snake_Update(void *opaque) } // Spawn new apple - Snake_FindFreeSlot(snake, &snake->applex, &snake->appley, x, y); + FindFreeSlot(snake, &snake->applex, &snake->appley, x, y); // Spawn new bonus if (!(snake->snakelength % 5)) { do { - snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1; - } while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4 - && (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE)); + snake->bonustype = M_RandomKey(NUM_BONUSES - 1) + 1; + } while (snake->snakelength > NUM_BLOCKS_X * NUM_BLOCKS_Y * 3 / 4 + && (snake->bonustype == BONUS_EGGMAN || snake->bonustype == BONUS_FAST || snake->bonustype == BONUS_REVERSE)); - Snake_FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y); + FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y); } S_StartSound(NULL, sfx_s3k6b); @@ -350,25 +350,25 @@ void Snake_Update(void *opaque) snake->snakey[0] = y; // Check collision with bonus - if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy) + if (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy) { S_StartSound(NULL, sfx_ncchip); switch (snake->bonustype) { - case SNAKE_BONUS_SLOW: - snake->snakebonus = SNAKE_BONUS_SLOW; + case BONUS_SLOW: + snake->snakebonus = BONUS_SLOW; snake->snakebonustime = 20 * TICRATE; break; - case SNAKE_BONUS_FAST: - snake->snakebonus = SNAKE_BONUS_FAST; + case BONUS_FAST: + snake->snakebonus = BONUS_FAST; snake->snakebonustime = 20 * TICRATE; break; - case SNAKE_BONUS_GHOST: - snake->snakebonus = SNAKE_BONUS_GHOST; + case BONUS_GHOST: + snake->snakebonus = BONUS_GHOST; snake->snakebonustime = 10 * TICRATE; break; - case SNAKE_BONUS_NUKE: + case BONUS_NUKE: for (i = 0; i < snake->snakelength; i++) { snake->snakex [i] = snake->snakex [0]; @@ -378,11 +378,11 @@ void Snake_Update(void *opaque) S_StartSound(NULL, sfx_bkpoof); break; - case SNAKE_BONUS_SCISSORS: - snake->snakebonus = SNAKE_BONUS_SCISSORS; + case BONUS_SCISSORS: + snake->snakebonus = BONUS_SCISSORS; snake->snakebonustime = 60 * TICRATE; break; - case SNAKE_BONUS_REVERSE: + case BONUS_REVERSE: for (i = 0; i < (snake->snakelength + 1) / 2; i++) { UINT16 i2 = snake->snakelength - 1 - i; @@ -393,10 +393,10 @@ void Snake_Update(void *opaque) // Swap first segment with last segment snake->snakex [i] = snake->snakex [i2]; snake->snakey [i] = snake->snakey [i2]; - snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]); + snake->snakedir[i] = GetOppositeDir(snake->snakedir[i2]); snake->snakex [i2] = tmpx; snake->snakey [i2] = tmpy; - snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir); + snake->snakedir[i2] = GetOppositeDir(tmpdir); } snake->snakedir[0] = 0; @@ -404,14 +404,14 @@ void Snake_Update(void *opaque) S_StartSound(NULL, sfx_gravch); break; default: - if (snake->snakebonus != SNAKE_BONUS_GHOST) + if (snake->snakebonus != BONUS_GHOST) { snake->gameover = true; S_StartSound(NULL, sfx_lose); } } - snake->bonustype = SNAKE_BONUS_NONE; + snake->bonustype = BONUS_NONE; } } @@ -425,23 +425,23 @@ void Snake_Draw(void *opaque) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); V_DrawFlatFill( - SNAKE_LEFT_X + SNAKE_BORDER_SIZE, - SNAKE_TOP_Y + SNAKE_BORDER_SIZE, - SNAKE_MAP_WIDTH, - SNAKE_MAP_HEIGHT, - W_GetNumForName(snake_backgrounds[snake->background]) + LEFT_X + BORDER_SIZE, + TOP_Y + BORDER_SIZE, + MAP_WIDTH, + MAP_HEIGHT, + W_GetNumForName(backgrounds[snake->background]) ); // Borders - V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top - V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right - V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom - V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left + V_DrawFill(LEFT_X, TOP_Y, BORDER_SIZE + MAP_WIDTH, BORDER_SIZE, 242); // Top + V_DrawFill(LEFT_X + BORDER_SIZE + MAP_WIDTH, TOP_Y, BORDER_SIZE, BORDER_SIZE + MAP_HEIGHT, 242); // Right + V_DrawFill(LEFT_X + BORDER_SIZE, TOP_Y + BORDER_SIZE + MAP_HEIGHT, BORDER_SIZE + MAP_WIDTH, BORDER_SIZE, 242); // Bottom + V_DrawFill(LEFT_X, TOP_Y + BORDER_SIZE, BORDER_SIZE, BORDER_SIZE + MAP_HEIGHT, 242); // Left // Apple V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (LEFT_X + BORDER_SIZE + snake->applex * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, + (TOP_Y + BORDER_SIZE + snake->appley * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, FRACUNIT / 4, 0, W_CachePatchLongName("DL_APPLE", PU_HUDGFX), @@ -449,13 +449,13 @@ void Snake_Draw(void *opaque) ); // Bonus - if (snake->bonustype != SNAKE_BONUS_NONE) + if (snake->bonustype != BONUS_NONE) V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 ) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT, + (LEFT_X + BORDER_SIZE + snake->bonusx * BLOCK_SIZE + BLOCK_SIZE / 2 ) * FRACUNIT, + (TOP_Y + BORDER_SIZE + snake->bonusy * BLOCK_SIZE + BLOCK_SIZE / 2 + 4) * FRACUNIT, FRACUNIT / 2, 0, - W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX), + W_CachePatchLongName(bonuspatches[snake->bonustype], PU_HUDGFX), NULL ); @@ -499,10 +499,10 @@ void Snake_Draw(void *opaque) } V_DrawFixedPatch( - (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, - (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (LEFT_X + BORDER_SIZE + snake->snakex[i] * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, + (TOP_Y + BORDER_SIZE + snake->snakey[i] * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2, - snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0, + snake->snakebonus == BONUS_GHOST ? V_TRANSLUCENT : 0, W_CachePatchLongName(patchname, PU_HUDGFX), NULL ); @@ -510,17 +510,17 @@ void Snake_Draw(void *opaque) } // Length - V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); + V_DrawString(RIGHT_X + 4, TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); // Bonus - if (snake->snakebonus != SNAKE_BONUS_NONE + if (snake->snakebonus != BONUS_NONE && (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2)) V_DrawFixedPatch( - (SNAKE_RIGHT_X + 10) * FRACUNIT, - (SNAKE_TOP_Y + 24) * FRACUNIT, + (RIGHT_X + 10) * FRACUNIT, + (TOP_Y + 24) * FRACUNIT, FRACUNIT / 2, 0, - W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX), + W_CachePatchLongName(bonuspatches[snake->snakebonus], PU_HUDGFX), NULL ); } From 262ed6b7f34440ec292c139e7977c0af18366404 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 29 Dec 2022 10:23:12 +0100 Subject: [PATCH 11/89] Split packet handling switch into functions --- src/d_clisrv.c | 1111 ++++++++++++++++++++++++------------------------ src/d_netfil.c | 49 ++- src/d_netfil.h | 8 +- 3 files changed, 605 insertions(+), 563 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index be10e4f9b..d3f6854c7 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3677,8 +3677,531 @@ static void HandleServerInfo(SINT8 node) } #endif -static void PT_WillResendGamestate(void) +// Helper function for packets that should only be sent by the server +// If it is NOT from the server, bail out and close the connection! +static boolean ServerOnly(SINT8 node) { + if (node == servernode) + return false; + + Net_CloseConnection(node); + return true; +} + +static void PT_AskInfoViaMS(SINT8 node) +{ + Net_CloseConnection(node); +} + +static void PT_TellFilesNeeded(SINT8 node) +{ + if (server && serverrunning) + { + UINT8 *p; + INT32 firstfile = netbuffer->u.filesneedednum; + + netbuffer->packettype = PT_MOREFILESNEEDED; + netbuffer->u.filesneededcfg.first = firstfile; + netbuffer->u.filesneededcfg.more = 0; + + p = PutFileNeeded(firstfile); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); + } + else // Shouldn't get this if you aren't the server...? + Net_CloseConnection(node); +} + +static void PT_MoreFilesNeeded(SINT8 node) +{ + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) + { + D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); + if (!netbuffer->u.filesneededcfg.more) + cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list + } +} + +static void PT_AskInfo(SINT8 node) +{ + if (server && serverrunning) + { + SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); + SV_SendPlayerInfo(node); // Send extra info + } + Net_CloseConnection(node); +} + +// Negative response of client join request +static void PT_ServerRefuse(SINT8 node) +{ + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + if (cl_mode == CL_WAITJOINRESPONSE) + { + // Save the reason so it can be displayed after quitting the netgame + char *reason = strdup(netbuffer->u.serverrefuse.reason); + if (!reason) + I_Error("Out of memory!\n"); + + 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); + return; + } + + 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. + cl_mode = CL_ABORTED; + } +} + +// Positive response of client join request +static void PT_ServerCFG(SINT8 node) +{ + if (server && serverrunning && node != servernode) + { // but wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + /// \note how would this happen? and is it doing the right thing if it does? + if (cl_mode != CL_WAITJOINRESPONSE) + return; + + if (client) + { + maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); + G_SetGametype(netbuffer->u.servercfg.gametype); + modifiedgame = netbuffer->u.servercfg.modifiedgame; + memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + } + + nodeingame[(UINT8)servernode] = true; + serverplayer = netbuffer->u.servercfg.serverplayer; + doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); + mynode = netbuffer->u.servercfg.clientnode; + if (serverplayer >= 0) + playernode[(UINT8)serverplayer] = servernode; + + if (netgame) +#ifndef NONET + CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); +#else + CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); +#endif + DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); + +#ifndef NONET + /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? + /// Shouldn't them be downloaded even at intermission time? + /// Also, according to HandleConnect, the server will send the savegame even during intermission... + if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || + netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) + cl_mode = CL_DOWNLOADSAVEGAME; + else +#endif + cl_mode = CL_CONNECTED; +} + +static void PT_ClientCmd(SINT8 node, INT32 netconsole) +{ + tic_t realend, realstart; + + if (client) + return; + + // To save bytes, only the low byte of tic numbers are sent + // Use ExpandTics to figure out what the rest of the bytes are + realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); + + if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS + || netbuffer->packettype == PT_NODEKEEPALIVEMIS + || supposedtics[node] < realend) + { + supposedtics[node] = realend; + } + // Discard out of order packet + if (nettics[node] > realend) + { + DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); + return; + } + + // Update the nettics + nettics[node] = realend; + + // Don't do anything for packets of type NODEKEEPALIVE? + if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE + || netbuffer->packettype == PT_NODEKEEPALIVEMIS) + return; + + // As long as clients send valid ticcmds, the server can keep running, so reset the timeout + /// \todo Use a separate cvar for that kind of timeout? + freezetimeout[node] = I_GetTime() + connectiontimeout; + + // Copy ticcmd + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); + + // Check ticcmd for "speed hacks" + if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE + || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); + //D_Clearticcmd(k); + + SendKick(netconsole, KICK_MSG_CON_FAIL); + return; + } + + // Splitscreen cmd + if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) + && nodetoplayer2[node] >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], + &netbuffer->u.client2pak.cmd2, 1); + + // Check player consistancy during the level + if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) +#ifndef NONET + && !SV_ResendingSavegameToAnyone() +#endif + && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) + { + if (cv_resynchattempts.value) + { + // Tell the client we are about to resend them the gamestate + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + HSendPacket(node, true, 0, 0); + + resendingsavegame[node] = true; + + if (cv_blamecfail.value) + CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), + netconsole+1, player_names[netconsole], + consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy)); + DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + return; + } + else + { + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + return; + } + } +} + +static void PT_TextCmd(SINT8 node, INT32 netconsole) +{ + if (client) + return; + + // splitscreen special + if (netbuffer->packettype == PT_TEXTCMD2) + netconsole = nodetoplayer2[node]; + + if (netconsole < 0 || netconsole >= MAXPLAYERS) + Net_UnAcknowledgePacket(node); + else + { + size_t j; + tic_t tic = maketic; + UINT8 *textcmd; + + // ignore if the textcmd has a reported size of zero + // this shouldn't be sent at all + if (!netbuffer->u.textcmd[0]) + { + DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", + node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // ignore if the textcmd size var is actually larger than it should be + // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength + if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) + { + DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", + netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), + node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // check if tic that we are making isn't too large else we cannot send it :( + // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time + j = software_MAXPACKETLENGTH + - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE + + (doomcom->numslots+1)*sizeof(ticcmd_t)); + + // search a tic that have enougth space in the ticcmd + while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), + (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) + && tic < firstticstosend + BACKUPTICS) + tic++; + + if (tic >= firstticstosend + BACKUPTICS) + { + DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " + "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), + maketic, firstticstosend, node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // Make sure we have a buffer + if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); + + DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", + tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); + + M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); + textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; + } +} + +static void PT_Login(SINT8 node, INT32 netconsole) +{ + (void)node; + + if (client) + return; + +#ifndef NOMD5 + UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ + + if (doomcom->datalength < 16)/* ignore partial sends */ + return; + + if (!adminpasswordset) + { + CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); + return; + } + + // Do the final pass to compare with the sent md5 + D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); + + if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) + { + CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); + COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately + } + else + CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); +#else + (void)netconsole; +#endif +} + +static void PT_ClientQuit(SINT8 node, INT32 netconsole) +{ + if (client) + return; + + if (!nodeingame[node]) + { + Net_CloseConnection(node); + return; + } + + // nodeingame will be put false in the execution of kick command + // this allow to send some packets to the quitting client to have their ack back + nodewaiting[node] = 0; + if (netconsole != -1 && playeringame[netconsole]) + { + UINT8 kickmsg; + + if (netbuffer->packettype == PT_NODETIMEOUT) + kickmsg = KICK_MSG_TIMEOUT; + else + kickmsg = KICK_MSG_PLAYER_QUIT; + kickmsg |= KICK_MSG_KEEP_BODY; + + SendKick(netconsole, kickmsg); + nodetoplayer[node] = -1; + + if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 + && playeringame[(UINT8)nodetoplayer2[node]]) + { + SendKick(nodetoplayer2[node], kickmsg); + nodetoplayer2[node] = -1; + } + } + Net_CloseConnection(node); + nodeingame[node] = false; +} + +static void PT_CanReceiveGamestate(SINT8 node) +{ +#ifndef NONET + if (client || sendingsavegame[node]) + return; + + CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + + SV_SendSaveGame(node, true); // Resend a complete game state + resendingsavegame[node] = true; +#else + (void)node; +#endif +} + +static void PT_AskLuaFile(SINT8 node) +{ + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) + AddLuaFileToSendQueue(node, luafiletransfers->realfilename); +} + +static void PT_HasLuaFile(SINT8 node) +{ + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) + SV_HandleLuaFileSent(node); +} + +static void PT_ReceivedGamestate(SINT8 node) +{ + sendingsavegame[node] = false; + resendingsavegame[node] = false; + savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE; +} + +static void PT_ServerTics(SINT8 node, INT32 netconsole) +{ + UINT8 *pak, *txtpak, numtxtpak; + tic_t realend, realstart; + + if (!nodeingame[node]) + { + // Do not remove my own server (we have just get a out of order packet) + if (node != servernode) + { + DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); + Net_CloseConnection(node); + } + return; + } + + // Only accept PT_SERVERTICS from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + + realstart = netbuffer->u.serverpak.starttic; + realend = realstart + netbuffer->u.serverpak.numtics; + + txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots + * netbuffer->u.serverpak.numtics]; + + if (realend > gametic + CLIENTBACKUPTICS) + realend = gametic + CLIENTBACKUPTICS; + cl_packetmissed = realstart > neededtic; + + if (realstart <= neededtic && realend > neededtic) + { + tic_t i, j; + pak = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realstart; i < realend; i++) + { + // clear first + D_Clearticcmd(i); + + // copy the tics + pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, + netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); + + // copy the textcmds + numtxtpak = *txtpak++; + for (j = 0; j < numtxtpak; j++) + { + INT32 k = *txtpak++; // playernum + const size_t txtsize = txtpak[0]+1; + + if (i >= gametic) // Don't copy old net commands + M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); + txtpak += txtsize; + } + } + + neededtic = realend; + } + else + { + DEBFILE(va("frame not in bound: %u\n", neededtic)); + /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) + I_Error("Received an out of order PT_SERVERTICS packet!\n" + "Got tics %d-%d, needed tic %d\n\n" + "Please report this crash on the Master Board,\n" + "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ + } +} + +static void PT_Ping(SINT8 node, INT32 netconsole) +{ + // Only accept PT_PING from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + + //Update client ping table from the server. + if (client) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; + + servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; + } +} + +static void PT_WillResendGamestate(SINT8 node) +{ + (void)node; + #ifndef NONET char tmpsave[256]; @@ -3705,19 +4228,12 @@ static void PT_WillResendGamestate(void) #endif } -static void PT_CanReceiveGamestate(SINT8 node) +static void PT_SendingLuaFile(SINT8 node) { -#ifndef NONET - if (client || sendingsavegame[node]) - return; - - CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); - - SV_SendSaveGame(node, true); // Resend a complete game state - resendingsavegame[node] = true; -#else (void)node; -#endif + + if (client) + CL_PrepareDownloadLuaFile(); } /** Handles a packet received from a node that isn't in game @@ -3733,202 +4249,27 @@ static void HandlePacketFromAwayNode(SINT8 node) if (node != servernode) DEBFILE(va("Received packet from unknown host %d\n", node)); -// macro for packets that should only be sent by the server -// if it is NOT from the server, bail out and close the connection! -#define SERVERONLY \ - if (node != servernode) \ - { \ - Net_CloseConnection(node); \ - break; \ - } switch (netbuffer->packettype) { - case PT_ASKINFOVIAMS: - Net_CloseConnection(node); - break; - - case PT_TELLFILESNEEDED: - if (server && serverrunning) - { - UINT8 *p; - INT32 firstfile = netbuffer->u.filesneedednum; - - netbuffer->packettype = PT_MOREFILESNEEDED; - netbuffer->u.filesneededcfg.first = firstfile; - netbuffer->u.filesneededcfg.more = 0; - - p = PutFileNeeded(firstfile); - - HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); - } - else // Shouldn't get this if you aren't the server...? - Net_CloseConnection(node); - break; - - case PT_MOREFILESNEEDED: - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) - { - D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); - if (!netbuffer->u.filesneededcfg.more) - cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list - } - break; - - case PT_ASKINFO: - if (server && serverrunning) - { - SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); - SV_SendPlayerInfo(node); // Send extra info - } - Net_CloseConnection(node); - break; - - case PT_SERVERREFUSE: // Negative response of client join request - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - if (cl_mode == CL_WAITJOINRESPONSE) - { - // Save the reason so it can be displayed after quitting the netgame - char *reason = strdup(netbuffer->u.serverrefuse.reason); - if (!reason) - I_Error("Out of memory!\n"); - - 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. - cl_mode = CL_ABORTED; - } - break; - - case PT_SERVERCFG: // Positive response of client join request - { - if (server && serverrunning && node != servernode) - { // but wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - /// \note how would this happen? and is it doing the right thing if it does? - if (cl_mode != CL_WAITJOINRESPONSE) - break; - - if (client) - { - maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); - G_SetGametype(netbuffer->u.servercfg.gametype); - modifiedgame = netbuffer->u.servercfg.modifiedgame; - memcpy(server_context, netbuffer->u.servercfg.server_context, 8); - } - - nodeingame[(UINT8)servernode] = true; - serverplayer = netbuffer->u.servercfg.serverplayer; - doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); - mynode = netbuffer->u.servercfg.clientnode; - if (serverplayer >= 0) - playernode[(UINT8)serverplayer] = servernode; - - if (netgame) -#ifndef NONET - CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); -#else - CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); -#endif - DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); - -#ifndef NONET - /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? - /// Shouldn't them be downloaded even at intermission time? - /// Also, according to HandleConnect, the server will send the savegame even during intermission... - if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || - netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) - cl_mode = CL_DOWNLOADSAVEGAME; - else -#endif - cl_mode = CL_CONNECTED; - break; - } - - // Handled in d_netfil.c - case PT_FILEFRAGMENT: - if (server) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - break; - } - SERVERONLY - PT_FileFragment(); - break; - - case PT_FILEACK: - if (server) - PT_FileAck(); - break; - - case PT_FILERECEIVED: - if (server) - PT_FileReceived(); - break; - - case PT_REQUESTFILE: - if (server) - { - if (!cv_downloading.value || !PT_RequestFile(node)) - Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway - } - else - Net_CloseConnection(node); // nope - break; - - case PT_NODETIMEOUT: - case PT_CLIENTQUIT: - if (server) - Net_CloseConnection(node); - break; - - case PT_CLIENTCMD: - break; // This is not an "unknown packet" - - case PT_SERVERTICS: - // Do not remove my own server (we have just get a out of order packet) - if (node == servernode) - break; - /* FALLTHRU */ + case PT_ASKINFOVIAMS : PT_AskInfoViaMS (node ); break; + case PT_TELLFILESNEEDED: PT_TellFilesNeeded(node ); break; + case PT_MOREFILESNEEDED: PT_MoreFilesNeeded(node ); break; + case PT_ASKINFO : PT_AskInfo (node ); break; + case PT_SERVERREFUSE : PT_ServerRefuse (node ); break; + case PT_SERVERCFG : PT_ServerCFG (node ); break; + case PT_FILEFRAGMENT : PT_FileFragment (node, -1); break; + case PT_FILEACK : PT_FileAck (node ); break; + case PT_FILERECEIVED : PT_FileReceived (node ); break; + case PT_REQUESTFILE : PT_RequestFile (node ); break; + case PT_NODETIMEOUT : PT_ClientQuit (node, -1); break; + case PT_CLIENTQUIT : PT_ClientQuit (node, -1); break; + case PT_SERVERTICS : PT_ServerTics (node, -1); break; + case PT_CLIENTCMD : break; // This is not an "unknown packet" default: DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); Net_CloseConnection(node); - break; // Ignore it - } -#undef SERVERONLY } /** Handles a packet received from a node that is in game @@ -3942,13 +4283,6 @@ static void HandlePacketFromAwayNode(SINT8 node) static void HandlePacketFromPlayer(SINT8 node) { INT32 netconsole; - tic_t realend, realstart; - UINT8 *pak, *txtpak, numtxtpak; -#ifndef NOMD5 - UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ -#endif - - txtpak = NULL; if (dedicated && node == 0) netconsole = 0; @@ -3961,358 +4295,39 @@ static void HandlePacketFromPlayer(SINT8 node) switch (netbuffer->packettype) { -// -------------------------------------------- SERVER RECEIVE ---------- + // SERVER RECEIVE case PT_CLIENTCMD: case PT_CLIENT2CMD: case PT_CLIENTMIS: case PT_CLIENT2MIS: case PT_NODEKEEPALIVE: case PT_NODEKEEPALIVEMIS: - if (client) - break; - - // To save bytes, only the low byte of tic numbers are sent - // Use ExpandTics to figure out what the rest of the bytes are - realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); - - if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS - || netbuffer->packettype == PT_NODEKEEPALIVEMIS - || supposedtics[node] < realend) - { - supposedtics[node] = realend; - } - // Discard out of order packet - if (nettics[node] > realend) - { - DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); - break; - } - - // Update the nettics - nettics[node] = realend; - - // Don't do anything for packets of type NODEKEEPALIVE? - if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE - || netbuffer->packettype == PT_NODEKEEPALIVEMIS) - break; - - // As long as clients send valid ticcmds, the server can keep running, so reset the timeout - /// \todo Use a separate cvar for that kind of timeout? - freezetimeout[node] = I_GetTime() + connectiontimeout; - - // Copy ticcmd - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); - - // Check ticcmd for "speed hacks" - if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE - || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); - //D_Clearticcmd(k); - - SendKick(netconsole, KICK_MSG_CON_FAIL); - break; - } - - // Splitscreen cmd - if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - && nodetoplayer2[node] >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], - &netbuffer->u.client2pak.cmd2, 1); - - // Check player consistancy during the level - if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL - && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) -#ifndef NONET - && !SV_ResendingSavegameToAnyone() -#endif - && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) - { - if (cv_resynchattempts.value) - { - // Tell the client we are about to resend them the gamestate - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - HSendPacket(node, true, 0, 0); - - resendingsavegame[node] = true; - - if (cv_blamecfail.value) - CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), - netconsole+1, player_names[netconsole], - consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy)); - DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - else - { - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - break; - } - } + PT_ClientCmd(node, netconsole); break; - case PT_TEXTCMD2: // splitscreen special - netconsole = nodetoplayer2[node]; - /* FALLTHRU */ - case PT_TEXTCMD: - if (client) - break; + case PT_TEXTCMD : PT_TextCmd (node, netconsole); break; + case PT_TEXTCMD2 : PT_TextCmd (node, netconsole); break; + case PT_LOGIN : PT_Login (node, netconsole); break; + case PT_NODETIMEOUT : PT_ClientQuit (node, netconsole); break; + case PT_CLIENTQUIT : PT_ClientQuit (node, netconsole); break; + case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node ); break; + case PT_ASKLUAFILE : PT_AskLuaFile (node ); break; + case PT_HASLUAFILE : PT_HasLuaFile (node ); break; + case PT_RECEIVEDGAMESTATE : PT_ReceivedGamestate (node ); break; - if (netconsole < 0 || netconsole >= MAXPLAYERS) - Net_UnAcknowledgePacket(node); - else - { - size_t j; - tic_t tic = maketic; - UINT8 *textcmd; + // CLIENT RECEIVE + case PT_SERVERTICS : PT_ServerTics (node, netconsole); break; + case PT_PING : PT_Ping (node, netconsole); break; + case PT_FILEFRAGMENT : PT_FileFragment (node, netconsole); break; + case PT_FILEACK : PT_FileAck (node ); break; + case PT_FILERECEIVED : PT_FileReceived (node ); break; + case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node ); break; + case PT_SENDINGLUAFILE : PT_SendingLuaFile (node ); break; + case PT_SERVERCFG : break; - // ignore if the textcmd has a reported size of zero - // this shouldn't be sent at all - if (!netbuffer->u.textcmd[0]) - { - DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", - node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // ignore if the textcmd size var is actually larger than it should be - // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength - if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) - { - DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", - netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), - node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // check if tic that we are making isn't too large else we cannot send it :( - // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time - j = software_MAXPACKETLENGTH - - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE - + (doomcom->numslots+1)*sizeof(ticcmd_t)); - - // search a tic that have enougth space in the ticcmd - while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), - (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) - && tic < firstticstosend + BACKUPTICS) - tic++; - - if (tic >= firstticstosend + BACKUPTICS) - { - DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " - "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), - maketic, firstticstosend, node, netconsole)); - Net_UnAcknowledgePacket(node); - break; - } - - // Make sure we have a buffer - if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); - - DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", - tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); - - M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); - textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; - } - break; - case PT_LOGIN: - if (client) - break; - -#ifndef NOMD5 - if (doomcom->datalength < 16)/* ignore partial sends */ - break; - - if (!adminpasswordset) - { - CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); - break; - } - - // Do the final pass to compare with the sent md5 - D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); - - if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) - { - CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); - COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately - } - else - CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); -#endif - break; - case PT_NODETIMEOUT: - case PT_CLIENTQUIT: - if (client) - break; - - // nodeingame will be put false in the execution of kick command - // this allow to send some packets to the quitting client to have their ack back - nodewaiting[node] = 0; - if (netconsole != -1 && playeringame[netconsole]) - { - UINT8 kickmsg; - - if (netbuffer->packettype == PT_NODETIMEOUT) - kickmsg = KICK_MSG_TIMEOUT; - else - kickmsg = KICK_MSG_PLAYER_QUIT; - kickmsg |= KICK_MSG_KEEP_BODY; - - SendKick(netconsole, kickmsg); - nodetoplayer[node] = -1; - - if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 - && playeringame[(UINT8)nodetoplayer2[node]]) - { - SendKick(nodetoplayer2[node], kickmsg); - nodetoplayer2[node] = -1; - } - } - Net_CloseConnection(node); - nodeingame[node] = false; - break; - case PT_CANRECEIVEGAMESTATE: - PT_CanReceiveGamestate(node); - break; - case PT_ASKLUAFILE: - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) - AddLuaFileToSendQueue(node, luafiletransfers->realfilename); - break; - case PT_HASLUAFILE: - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) - SV_HandleLuaFileSent(node); - break; - case PT_RECEIVEDGAMESTATE: - sendingsavegame[node] = false; - resendingsavegame[node] = false; - savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE; - break; -// -------------------------------------------- CLIENT RECEIVE ---------- - case PT_SERVERTICS: - // Only accept PT_SERVERTICS from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - - realstart = netbuffer->u.serverpak.starttic; - realend = realstart + netbuffer->u.serverpak.numtics; - - if (!txtpak) - txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots - * netbuffer->u.serverpak.numtics]; - - if (realend > gametic + CLIENTBACKUPTICS) - realend = gametic + CLIENTBACKUPTICS; - cl_packetmissed = realstart > neededtic; - - if (realstart <= neededtic && realend > neededtic) - { - tic_t i, j; - pak = (UINT8 *)&netbuffer->u.serverpak.cmds; - - for (i = realstart; i < realend; i++) - { - // clear first - D_Clearticcmd(i); - - // copy the tics - pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, - netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); - - // copy the textcmds - numtxtpak = *txtpak++; - for (j = 0; j < numtxtpak; j++) - { - INT32 k = *txtpak++; // playernum - const size_t txtsize = txtpak[0]+1; - - if (i >= gametic) // Don't copy old net commands - M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); - txtpak += txtsize; - } - } - - neededtic = realend; - } - else - { - DEBFILE(va("frame not in bound: %u\n", neededtic)); - /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) - I_Error("Received an out of order PT_SERVERTICS packet!\n" - "Got tics %d-%d, needed tic %d\n\n" - "Please report this crash on the Master Board,\n" - "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ - } - break; - case PT_PING: - // Only accept PT_PING from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - - //Update client ping table from the server. - if (client) - { - UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; - - servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; - } - - break; - case PT_SERVERCFG: - break; - case PT_FILEFRAGMENT: - // Only accept PT_FILEFRAGMENT from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - break; - } - if (client) - PT_FileFragment(); - break; - case PT_FILEACK: - if (server) - PT_FileAck(); - break; - case PT_FILERECEIVED: - if (server) - PT_FileReceived(); - break; - case PT_WILLRESENDGAMESTATE: - PT_WillResendGamestate(); - break; - case PT_SENDINGLUAFILE: - if (client) - CL_PrepareDownloadLuaFile(); - break; default: DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", netbuffer->packettype, node)); - } // end switch + } } /** Handles all received packets, if any diff --git a/src/d_netfil.c b/src/d_netfil.c index edbef30bb..7c9ed54d8 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -413,12 +413,17 @@ boolean CL_SendFileRequest(void) } // get request filepak and put it on the send queue -// returns false if a requested file was not found or cannot be sent -boolean PT_RequestFile(INT32 node) +void PT_RequestFile(SINT8 node) { UINT8 *p = netbuffer->u.textcmd; UINT8 id; + if (client || !cv_downloading.value) + { + Net_CloseConnection(node); // close connection if you are not the server or disabled downloading + return; + } + while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow { id = READUINT8(p); @@ -428,11 +433,12 @@ boolean PT_RequestFile(INT32 node) if (!AddFileToSendQueue(node, id)) { SV_AbortSendFiles(node); - return false; // don't read the rest of the files + Net_CloseConnection(node); // close connection if one of the requested files could not be sent + return; // don't read the rest of the files } } - return true; // no problems with any files + return; // no problems with any files } /** Checks if the files needed aren't already loaded or on the disk @@ -1137,13 +1143,15 @@ void FileSendTicker(void) } } -void PT_FileAck(void) +void PT_FileAck(SINT8 node) { fileack_pak *packet = &netbuffer->u.fileack; - INT32 node = doomcom->remotenode; filetran_t *trans = &transfer[node]; INT32 i, j; + if (client) + return; + // Wrong file id? Ignore it, it's probably a late packet if (!(trans->txlist && packet->fileid == trans->txlist->fileid)) return; @@ -1190,12 +1198,12 @@ void PT_FileAck(void) } } -void PT_FileReceived(void) +void PT_FileReceived(SINT8 node) { - filetx_t *trans = transfer[doomcom->remotenode].txlist; + filetx_t *trans = transfer[node].txlist; - if (trans && netbuffer->u.filereceived == trans->fileid) - SV_EndFileSend(doomcom->remotenode); + if (server && trans && netbuffer->u.filereceived == trans->fileid) + SV_EndFileSend(node); } static void SendAckPacket(fileack_pak *packet, UINT8 fileid) @@ -1281,8 +1289,27 @@ void FileReceiveTicker(void) } } -void PT_FileFragment(void) +void PT_FileFragment(SINT8 node, INT32 netconsole) { + if (nodeingame[node]) + { + // Only accept PT_FILEFRAGMENT from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + if (server) + return; + } + else if (server || node != servernode) + { + Net_CloseConnection(node); + return; + } + INT32 filenum = netbuffer->u.filetxpak.fileid; fileneeded_t *file = &fileneeded[filenum]; UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position); diff --git a/src/d_netfil.h b/src/d_netfil.h index f778a518f..5c7f4ef49 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -90,16 +90,16 @@ void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemet UINT8 fileid); void FileSendTicker(void); -void PT_FileAck(void); -void PT_FileReceived(void); +void PT_FileAck(SINT8 node); +void PT_FileReceived(SINT8 node); boolean SendingFile(INT32 node); void FileReceiveTicker(void); -void PT_FileFragment(void); +void PT_FileFragment(SINT8 node, INT32 netconsole); boolean CL_CheckDownloadable(void); boolean CL_SendFileRequest(void); -boolean PT_RequestFile(INT32 node); +void PT_RequestFile(SINT8 node); typedef enum { From e6e9eef0e2a2ba3a36c6d7159748fb9485938b17 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 29 Dec 2022 15:52:45 +0100 Subject: [PATCH 12/89] Merge node arrays into a structure --- src/d_clisrv.c | 202 ++++++++++++++++++++++++------------------------- src/d_net.h | 23 +++++- src/d_netfil.c | 4 +- src/i_tcp.c | 14 ++-- 4 files changed, 125 insertions(+), 118 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d3f6854c7..e311bf15a 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -84,6 +84,8 @@ boolean serverrunning = false; INT32 serverplayer = 0; char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) +netnode_t netnodes[MAXNETNODES]; + // Server specific vars UINT8 playernode[MAXPLAYERS]; char playeraddress[MAXPLAYERS][64]; @@ -91,10 +93,6 @@ char playeraddress[MAXPLAYERS][64]; // Minimum timeout for sending the savegame // The actual timeout will be longer depending on the savegame length tic_t jointimeout = (10*TICRATE); -static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? -static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame? -static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again? -static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? // Incremented by cv_joindelay when a client joins, decremented each tic. // If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. @@ -103,14 +101,8 @@ static tic_t joindelay = 0; UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. -SINT8 nodetoplayer[MAXNETNODES]; -SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) -UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen -boolean nodeingame[MAXNETNODES]; // set false as nodes leave game tic_t servermaxping = 800; // server's max ping. Defaults to 800 -static tic_t nettics[MAXNETNODES]; // what tic the client have received -static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet -static UINT8 nodewaiting[MAXNETNODES]; + static tic_t firstticstosend; // min of the nettics static tic_t tictoclear = 0; // optimize d_clearticcmd static tic_t maketic; @@ -218,14 +210,14 @@ tic_t ExpandTics(INT32 low, INT32 node) { INT32 delta; - delta = low - (nettics[node] & UINT8_MAX); + delta = low - (netnodes[node].tic & UINT8_MAX); if (delta >= -64 && delta <= 64) - return (nettics[node] & ~UINT8_MAX) + low; + return (netnodes[node].tic & ~UINT8_MAX) + low; else if (delta > 64) - return (nettics[node] & ~UINT8_MAX) - 256 + low; + return (netnodes[node].tic & ~UINT8_MAX) - 256 + low; else //if (delta < -64) - return (nettics[node] & ~UINT8_MAX) + 256 + low; + return (netnodes[node].tic & ~UINT8_MAX) + 256 + low; } // ----------------------------------------------------------------- @@ -999,7 +991,7 @@ static boolean SV_ResendingSavegameToAnyone(void) INT32 i; for (i = 0; i < MAXNETNODES; i++) - if (resendingsavegame[i]) + if (netnodes[i].resendingsavegame) return true; return false; } @@ -1068,8 +1060,8 @@ static void SV_SendSaveGame(INT32 node, boolean resending) save_p = NULL; // Remember when we started sending the savegame so we can handle timeouts - sendingsavegame[node] = true; - freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte + netnodes[node].sendingsavegame = true; + netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte } #ifdef DUMPCONSISTENCY @@ -2034,7 +2026,7 @@ static void CL_ConnectToServer(void) { pnumnodes = 0; for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) + if (netnodes[i].ingame) pnumnodes++; } } @@ -2316,10 +2308,10 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) if (server && !demoplayback && playernode[playernum] != UINT8_MAX) { INT32 node = playernode[playernum]; - playerpernode[node]--; - if (playerpernode[node] <= 0) + netnodes[node].numplayers--; + if (netnodes[node].numplayers <= 0) { - nodeingame[node] = false; + netnodes[node].ingame = false; Net_CloseConnection(node); ResetNode(node); } @@ -2431,7 +2423,7 @@ void CL_Reset(void) if (servernode > 0 && servernode < MAXNETNODES) { - nodeingame[(UINT8)servernode] = false; + netnodes[(UINT8)servernode].ingame = false; Net_CloseConnection(servernode); } D_CloseConnection(); // netgame = false @@ -2674,7 +2666,7 @@ static void Command_Kick(void) // Special case if we are trying to kick a player who is downloading the game state: // trigger a timeout instead of kicking them, because a kick would only // take effect after they have finished downloading - if (server && playernode[pn] != UINT8_MAX && sendingsavegame[playernode[pn]]) + if (server && playernode[pn] != UINT8_MAX && netnodes[playernode[pn]].sendingsavegame) { Net_ConnectionTimeout(playernode[pn]); return; @@ -2763,8 +2755,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) // Is playernum authorized to make this kick? if (playernum != serverplayer && !IsPlayerAdmin(playernum) - && !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2 - && nodetoplayer2[playernode[playernum]] == pnum)) + && !(playernode[playernum] != UINT8_MAX && netnodes[playernode[playernum]].numplayers == 2 + && netnodes[playernode[playernum]].player2 == pnum)) { // We received a kick command from someone who isn't the // server or admin, and who isn't in splitscreen removing @@ -2802,10 +2794,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) "\n" "If you think this is a bug, please report it, including all of the details above.\n", playernum, pnum, - playernode[playernum], playerpernode[playernode[playernum]], - nodetoplayer2[playernode[playernum]], - playernode[pnum], playerpernode[playernode[pnum]], - nodetoplayer2[playernode[pnum]]); + playernode[playernum], netnodes[playernode[playernum]].numplayers, + netnodes[playernode[playernum]].player2, + playernode[pnum], netnodes[playernode[pnum]].numplayers, + netnodes[playernode[pnum]].player2); */ pnum = playernum; msg = KICK_MSG_CON_FAIL; @@ -2926,10 +2918,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (server && !demoplayback && playernode[pnum] != UINT8_MAX) { INT32 node = playernode[pnum]; - playerpernode[node]--; - if (playerpernode[node] <= 0) + netnodes[node].numplayers--; + if (netnodes[node].numplayers <= 0) { - nodeingame[node] = false; + netnodes[node].ingame = false; Net_CloseConnection(node); ResetNode(node); } @@ -3017,19 +3009,19 @@ void D_ClientServerInit(void) static void ResetNode(INT32 node) { - nodeingame[node] = false; - nodewaiting[node] = 0; + netnodes[node].ingame = false; + netnodes[node].numplayerswaiting = 0; - nettics[node] = gametic; - supposedtics[node] = gametic; + netnodes[node].tic = gametic; + netnodes[node].supposedtic = gametic; - nodetoplayer[node] = -1; - nodetoplayer2[node] = -1; - playerpernode[node] = 0; + netnodes[node].player = -1; + netnodes[node].player2 = -1; + netnodes[node].numplayers = 0; - sendingsavegame[node] = false; - resendingsavegame[node] = false; - savegameresendcooldown[node] = 0; + netnodes[node].sendingsavegame = false; + netnodes[node].resendingsavegame = false; + netnodes[node].savegameresendcooldown = 0; } void SV_ResetServer(void) @@ -3067,7 +3059,7 @@ void SV_ResetServer(void) if (dedicated) { - nodeingame[0] = true; + netnodes[0].ingame = true; serverplayer = 0; } else @@ -3130,14 +3122,14 @@ void D_QuitNetGame(void) netbuffer->packettype = PT_SERVERSHUTDOWN; for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) + if (netnodes[i].ingame) HSendPacket(i, true, 0, 0); #ifdef MASTERSERVER if (serverrunning && ms_RoomId > 0) UnregisterServer(); #endif } - else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) + else if (servernode > 0 && servernode < MAXNETNODES && netnodes[(UINT8)servernode].ingame) { netbuffer->packettype = PT_CLIENTQUIT; HSendPacket(servernode, true, 0, 0); @@ -3161,12 +3153,12 @@ void D_QuitNetGame(void) // Adds a node to the game (player will follow at map change or at savegame....) static inline void SV_AddNode(INT32 node) { - nettics[node] = gametic; - supposedtics[node] = gametic; + netnodes[node].tic = gametic; + netnodes[node].supposedtic = gametic; // little hack because the server connects to itself and puts // nodeingame when connected not here if (node) - nodeingame[node] = true; + netnodes[node].ingame = true; } // Xcmd XD_ADDPLAYER @@ -3311,7 +3303,7 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) for (node = 0; node < MAXNETNODES; node++) { // splitscreen can allow 2 player in one node - for (; nodewaiting[node] > 0; nodewaiting[node]--) + for (; netnodes[node].numplayerswaiting > 0; netnodes[node].numplayerswaiting--) { newplayer = true; @@ -3325,7 +3317,7 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) if (playeringame[newplayernum]) continue; for (n = 0; n < MAXNETNODES; n++) - if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum) + if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum) break; if (n == MAXNETNODES) break; @@ -3341,18 +3333,18 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) p = buf + 2; buf[0] = (UINT8)node; buf[1] = newplayernum; - if (playerpernode[node] < 1) + if (netnodes[node].numplayers < 1) { - nodetoplayer[node] = newplayernum; + netnodes[node].player = newplayernum; WRITESTRINGN(p, name, MAXPLAYERNAME); } else { - nodetoplayer2[node] = newplayernum; + netnodes[node].player2 = newplayernum; buf[1] |= 0x80; WRITESTRINGN(p, name2, MAXPLAYERNAME); } - playerpernode[node]++; + netnodes[node].numplayers++; SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); @@ -3571,7 +3563,7 @@ static void HandleConnect(SINT8 node) boolean newnode = false; #endif - for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++) + for (i = 0; i < netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers; i++) { strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) @@ -3582,8 +3574,8 @@ static void HandleConnect(SINT8 node) } // client authorised to join - nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]); - if (!nodeingame[node]) + netnodes[node].numplayerswaiting = (UINT8)(netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers); + if (!netnodes[node].ingame) { gamestate_t backupstate = gamestate; #ifndef NONET @@ -3608,7 +3600,7 @@ static void HandleConnect(SINT8 node) DEBFILE("new node joined\n"); } #ifndef NONET - if (nodewaiting[node]) + if (netnodes[node].numplayerswaiting) { if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) { @@ -3803,7 +3795,7 @@ static void PT_ServerCFG(SINT8 node) memcpy(server_context, netbuffer->u.servercfg.server_context, 8); } - nodeingame[(UINT8)servernode] = true; + netnodes[(UINT8)servernode].ingame = true; serverplayer = netbuffer->u.servercfg.serverplayer; doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); mynode = netbuffer->u.servercfg.clientnode; @@ -3844,19 +3836,19 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole) if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS || netbuffer->packettype == PT_NODEKEEPALIVEMIS - || supposedtics[node] < realend) + || netnodes[node].supposedtic < realend) { - supposedtics[node] = realend; + netnodes[node].supposedtic = realend; } // Discard out of order packet - if (nettics[node] > realend) + if (netnodes[node].tic > realend) { - DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); + DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic)); return; } // Update the nettics - nettics[node] = realend; + netnodes[node].tic = realend; // Don't do anything for packets of type NODEKEEPALIVE? if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE @@ -3865,7 +3857,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole) // As long as clients send valid ticcmds, the server can keep running, so reset the timeout /// \todo Use a separate cvar for that kind of timeout? - freezetimeout[node] = I_GetTime() + connectiontimeout; + netnodes[node].freezetimeout = I_GetTime() + connectiontimeout; // Copy ticcmd G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); @@ -3883,8 +3875,8 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole) // Splitscreen cmd if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - && nodetoplayer2[node] >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], + && netnodes[node].player2 >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2], &netbuffer->u.client2pak.cmd2, 1); // Check player consistancy during the level @@ -3893,7 +3885,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole) #ifndef NONET && !SV_ResendingSavegameToAnyone() #endif - && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) + && !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime()) { if (cv_resynchattempts.value) { @@ -3901,7 +3893,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole) netbuffer->packettype = PT_WILLRESENDGAMESTATE; HSendPacket(node, true, 0, 0); - resendingsavegame[node] = true; + netnodes[node].resendingsavegame = true; if (cv_blamecfail.value) CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), @@ -3931,7 +3923,7 @@ static void PT_TextCmd(SINT8 node, INT32 netconsole) // splitscreen special if (netbuffer->packettype == PT_TEXTCMD2) - netconsole = nodetoplayer2[node]; + netconsole = netnodes[node].player2; if (netconsole < 0 || netconsole >= MAXPLAYERS) Net_UnAcknowledgePacket(node); @@ -4033,7 +4025,7 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) if (client) return; - if (!nodeingame[node]) + if (!netnodes[node].ingame) { Net_CloseConnection(node); return; @@ -4041,7 +4033,7 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) // nodeingame will be put false in the execution of kick command // this allow to send some packets to the quitting client to have their ack back - nodewaiting[node] = 0; + netnodes[node].numplayerswaiting = 0; if (netconsole != -1 && playeringame[netconsole]) { UINT8 kickmsg; @@ -4053,29 +4045,29 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) kickmsg |= KICK_MSG_KEEP_BODY; SendKick(netconsole, kickmsg); - nodetoplayer[node] = -1; + netnodes[node].player = -1; - if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 - && playeringame[(UINT8)nodetoplayer2[node]]) + if (netnodes[node].player2 != -1 && netnodes[node].player2 >= 0 + && playeringame[(UINT8)netnodes[node].player2]) { - SendKick(nodetoplayer2[node], kickmsg); - nodetoplayer2[node] = -1; + SendKick(netnodes[node].player2, kickmsg); + netnodes[node].player2 = -1; } } Net_CloseConnection(node); - nodeingame[node] = false; + netnodes[node].ingame = false; } static void PT_CanReceiveGamestate(SINT8 node) { #ifndef NONET - if (client || sendingsavegame[node]) + if (client || netnodes[node].sendingsavegame) return; - CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]); SV_SendSaveGame(node, true); // Resend a complete game state - resendingsavegame[node] = true; + netnodes[node].resendingsavegame = true; #else (void)node; #endif @@ -4095,9 +4087,9 @@ static void PT_HasLuaFile(SINT8 node) static void PT_ReceivedGamestate(SINT8 node) { - sendingsavegame[node] = false; - resendingsavegame[node] = false; - savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE; + netnodes[node].sendingsavegame = false; + netnodes[node].resendingsavegame = false; + netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE; } static void PT_ServerTics(SINT8 node, INT32 netconsole) @@ -4105,7 +4097,7 @@ static void PT_ServerTics(SINT8 node, INT32 netconsole) UINT8 *pak, *txtpak, numtxtpak; tic_t realend, realstart; - if (!nodeingame[node]) + if (!netnodes[node].ingame) { // Do not remove my own server (we have just get a out of order packet) if (node != servernode) @@ -4287,7 +4279,7 @@ static void HandlePacketFromPlayer(SINT8 node) if (dedicated && node == 0) netconsole = 0; else - netconsole = nodetoplayer[node]; + netconsole = netnodes[node].player; #ifdef PARANOIA if (netconsole >= MAXPLAYERS) I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); @@ -4376,7 +4368,7 @@ static void GetPackets(void) continue; // We do nothing with PLAYERINFO, that's for the MS browser. // Packet received from someone already playing - if (nodeingame[node]) + if (netnodes[node].ingame) HandlePacketFromPlayer(node); // Packet received from someone not playing else @@ -4572,13 +4564,13 @@ static void SV_SendTics(void) // send to all client but not to me // for each node create a packet with x tics and send it - // x is computed using supposedtics[n], max packet size and maketic + // x is computed using netnodes[n].supposedtic, max packet size and maketic for (n = 1; n < MAXNETNODES; n++) - if (nodeingame[n]) + if (netnodes[n].ingame) { - // assert supposedtics[n]>=nettics[n] - realfirsttic = supposedtics[n]; - lasttictosend = min(maketic, nettics[n] + CLIENTBACKUPTICS); + // assert netnodes[n].supposedtic>=netnodes[n].tic + realfirsttic = netnodes[n].supposedtic; + lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS); if (realfirsttic >= lasttictosend) { @@ -4587,8 +4579,8 @@ static void SV_SendTics(void) // packet detection work when we have received packet with firsttic > neededtic // (getpacket servertics case) DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", - n, maketic, supposedtics[n], nettics[n])); - realfirsttic = nettics[n]; + n, maketic, netnodes[n].supposedtic, netnodes[n].tic)); + realfirsttic = netnodes[n].tic; if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) // all tic are ok continue; @@ -4666,13 +4658,13 @@ static void SV_SendTics(void) HSendPacket(n, false, 0, packsize); // when tic are too large, only one tic is sent so don't go backward! if (lasttictosend-doomcom->extratics > realfirsttic) - supposedtics[n] = lasttictosend-doomcom->extratics; + netnodes[n].supposedtic = lasttictosend-doomcom->extratics; else - supposedtics[n] = lasttictosend; - if (supposedtics[n] < nettics[n]) supposedtics[n] = nettics[n]; + netnodes[n].supposedtic = lasttictosend; + if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic; } // node 0 is me! - supposedtics[0] = maketic; + netnodes[0].supposedtic = maketic; } // @@ -4915,7 +4907,7 @@ static inline void PingUpdate(void) //send out our ping packets for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) + if (netnodes[i].ingame) HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); pingmeasurecount = 1; //Reset count @@ -4993,11 +4985,11 @@ void NetUpdate(void) firstticstosend = gametic; for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i] && nettics[i] < firstticstosend) + if (netnodes[i].ingame && netnodes[i].tic < firstticstosend) { - firstticstosend = nettics[i]; + firstticstosend = netnodes[i].tic; - if (maketic + 1 >= nettics[i] + BACKUPTICS) + if (maketic + 1 >= netnodes[i].tic + BACKUPTICS) Net_ConnectionTimeout(i); } @@ -5025,7 +5017,7 @@ void NetUpdate(void) if (server) { for (i = 1; i < MAXNETNODES; i++) - if (nodeingame[i] && freezetimeout[i] < I_GetTime()) + if (netnodes[i].ingame && netnodes[i].freezetimeout < I_GetTime()) Net_ConnectionTimeout(i); // In case the cvar value was lowered @@ -5066,7 +5058,7 @@ INT32 D_NumPlayers(void) tic_t GetLag(INT32 node) { - return gametic - nettics[node]; + return gametic - netnodes[node].tic; } void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest) diff --git a/src/d_net.h b/src/d_net.h index 5baa593a0..9eee53894 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -37,10 +37,25 @@ boolean Net_GetNetStat(void); extern INT32 getbytes; extern INT64 sendbytes; // Realtime updated -extern SINT8 nodetoplayer[MAXNETNODES]; -extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen) -extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen -extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game +typedef struct netnode_s +{ + boolean ingame; // set false as nodes leave game + tic_t freezetimeout; // Until when can this node freeze the server before getting a timeout? + + SINT8 player; + SINT8 player2; // say the numplayer for this node if any (splitscreen) + UINT8 numplayers; // used specialy for scplitscreen + UINT8 numplayerswaiting; + + tic_t tic; // what tic the client have received + tic_t supposedtic; // nettics prevision for smaller packet + + boolean sendingsavegame; // Are we sending the savegame? + boolean resendingsavegame; // Are we resending the savegame? + tic_t savegameresendcooldown; // How long before we can resend again? +} netnode_t; + +extern netnode_t netnodes[MAXNETNODES]; extern boolean serverrunning; diff --git a/src/d_netfil.c b/src/d_netfil.c index 7c9ed54d8..1fee1df81 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -667,7 +667,7 @@ void SV_PrepareSendLuaFile(void) // Set status to "waiting" for everyone for (i = 0; i < MAXNETNODES; i++) - luafiletransfers->nodestatus[i] = (nodeingame[i] ? LFTNS_WAITING : LFTNS_NONE); + luafiletransfers->nodestatus[i] = (netnodes[i].ingame ? LFTNS_WAITING : LFTNS_NONE); if (FIL_ReadFileOK(luafiletransfers->realfilename)) { @@ -1291,7 +1291,7 @@ void FileReceiveTicker(void) void PT_FileFragment(SINT8 node, INT32 netconsole) { - if (nodeingame[node]) + if (netnodes[node].ingame) { // Only accept PT_FILEFRAGMENT from the server. if (node != servernode) diff --git a/src/i_tcp.c b/src/i_tcp.c index 8838ba725..00aebddd3 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -449,7 +449,7 @@ static void cleanupnodes(void) // Why can't I start at zero? for (j = 1; j < MAXNETNODES; j++) - if (!(nodeingame[j] || SendingFile(j))) + if (!(netnodes[j].ingame || SendingFile(j))) nodeconnected[j] = false; } @@ -473,7 +473,7 @@ static SINT8 getfreenode(void) */ /*I_Error("No more free nodes!!1!11!11!!1111\n"); for (j = 1; j < MAXNETNODES; j++) - if (!nodeingame[j]) + if (!netnodes[j].ingame) return j;*/ return -1; @@ -488,24 +488,24 @@ void Command_Numnodes(void) for (i = 1; i < MAXNETNODES; i++) { - if (!(nodeconnected[i] || nodeingame[i])) + if (!(nodeconnected[i] || netnodes[i].ingame)) continue; if (nodeconnected[i]) connected++; - if (nodeingame[i]) + if (netnodes[i].ingame) ingame++; CONS_Printf("%2d - ", i); - if (nodetoplayer[i] != -1) - CONS_Printf("player %.2d", nodetoplayer[i]); + if (netnodes[i].player != -1) + CONS_Printf("player %.2d", netnodes[i].player); else CONS_Printf(" "); if (nodeconnected[i]) CONS_Printf(" - connected"); else CONS_Printf(" - "); - if (nodeingame[i]) + if (netnodes[i].ingame) CONS_Printf(" - ingame"); else CONS_Printf(" - "); From dc716d9ecee9aabcc7724b1a59c3ee544bd3f8b9 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 29 Dec 2022 20:45:56 +0100 Subject: [PATCH 13/89] Cleanup ResetNode --- src/d_clisrv.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index e311bf15a..3ab0df1d0 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2278,7 +2278,12 @@ static void Command_connect(void) } #endif -static void ResetNode(INT32 node); +static void ResetNode(INT32 node) +{ + memset(&netnodes[node], 0, sizeof(*netnodes)); + netnodes[node].player = -1; + netnodes[node].player2 = -1; +} // // CL_ClearPlayer @@ -3007,23 +3012,6 @@ void D_ClientServerInit(void) SV_SpawnServer(); } -static void ResetNode(INT32 node) -{ - netnodes[node].ingame = false; - netnodes[node].numplayerswaiting = 0; - - netnodes[node].tic = gametic; - netnodes[node].supposedtic = gametic; - - netnodes[node].player = -1; - netnodes[node].player2 = -1; - netnodes[node].numplayers = 0; - - netnodes[node].sendingsavegame = false; - netnodes[node].resendingsavegame = false; - netnodes[node].savegameresendcooldown = 0; -} - void SV_ResetServer(void) { INT32 i; From 2b2797ec665102fbd3224f93e358c686e145e001 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 29 Dec 2022 20:48:17 +0100 Subject: [PATCH 14/89] Move player-node unlinking to a function --- src/d_clisrv.c | 48 ++++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3ab0df1d0..1fee20a3f 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2298,6 +2298,24 @@ void CL_ClearPlayer(INT32 playernum) memset(playeraddress[playernum], 0, sizeof(*playeraddress)); } +static void UnlinkPlayerFromNode(INT32 playernum) +{ + INT32 node = playernode[playernum]; + + if (node == UINT8_MAX) + return; + + playernode[playernum] = UINT8_MAX; + + netnodes[node].numplayers--; + if (netnodes[node].numplayers <= 0) + { + netnodes[node].ingame = false; + Net_CloseConnection(node); + ResetNode(node); + } +} + // // CL_RemovePlayer // @@ -2310,17 +2328,8 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) if (!playeringame[playernum]) return; - if (server && !demoplayback && playernode[playernum] != UINT8_MAX) - { - INT32 node = playernode[playernum]; - netnodes[node].numplayers--; - if (netnodes[node].numplayers <= 0) - { - netnodes[node].ingame = false; - Net_CloseConnection(node); - ResetNode(node); - } - } + if (server) + UnlinkPlayerFromNode(playernum); if (gametyperules & GTR_TEAMFLAGS) P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! @@ -2392,7 +2401,6 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) // remove avatar of player playeringame[playernum] = false; - playernode[playernum] = UINT8_MAX; while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) doomcom->numslots--; @@ -2920,20 +2928,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) } else if (keepbody) { - if (server && !demoplayback && playernode[pnum] != UINT8_MAX) - { - INT32 node = playernode[pnum]; - netnodes[node].numplayers--; - if (netnodes[node].numplayers <= 0) - { - netnodes[node].ingame = false; - Net_CloseConnection(node); - ResetNode(node); - } - } - - playernode[pnum] = UINT8_MAX; - + if (server) + UnlinkPlayerFromNode(pnum); players[pnum].quittime = 1; } else From dc3995753984f34b30b9973551b103ba6b9564e2 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 29 Dec 2022 21:28:53 +0100 Subject: [PATCH 15/89] Move sphere redistribution to its own function --- src/d_clisrv.c | 106 ++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1fee20a3f..eee820db2 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2316,6 +2316,61 @@ static void UnlinkPlayerFromNode(INT32 playernum) } } +// If in a special stage, redistribute the player's +// spheres across the remaining players. +// I feel like this shouldn't even be in this file at all, but well. +static void RedistributeSpecialStageSpheres(INT32 playernum) +{ + INT32 i, count, sincrement, spheres, rincrement, rings; + + if (!G_IsSpecialStage(gamemap)) + return; + + for (i = 0, count = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + count++; + } + + count--; + sincrement = spheres = players[playernum].spheres; + rincrement = rings = players[playernum].rings; + + if (count) + { + sincrement /= count; + rincrement /= count; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && i != playernum) + { + if (spheres < 2*sincrement) + { + P_GivePlayerSpheres(&players[i], spheres); + spheres = 0; + } + else + { + P_GivePlayerSpheres(&players[i], sincrement); + spheres -= sincrement; + } + + if (rings < 2*rincrement) + { + P_GivePlayerRings(&players[i], rings); + rings = 0; + } + else + { + P_GivePlayerRings(&players[i], rincrement); + rings -= rincrement; + } + } + } +} + // // CL_RemovePlayer // @@ -2334,56 +2389,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) if (gametyperules & GTR_TEAMFLAGS) P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! - // If in a special stage, redistribute the player's spheres across - // the remaining players. - if (G_IsSpecialStage(gamemap)) - { - INT32 i, count, sincrement, spheres, rincrement, rings; - - for (i = 0, count = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - count++; - } - - count--; - sincrement = spheres = players[playernum].spheres; - rincrement = rings = players[playernum].rings; - - if (count) - { - sincrement /= count; - rincrement /= count; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && i != playernum) - { - if (spheres < 2*sincrement) - { - P_GivePlayerSpheres(&players[i], spheres); - spheres = 0; - } - else - { - P_GivePlayerSpheres(&players[i], sincrement); - spheres -= sincrement; - } - - if (rings < 2*rincrement) - { - P_GivePlayerRings(&players[i], rings); - rings = 0; - } - else - { - P_GivePlayerRings(&players[i], rincrement); - rings -= rincrement; - } - } - } - } + RedistributeSpecialStageSpheres(playernum); LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting From 6806fbce4582f1f2e69afd95b3fda2203a3444f3 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 29 Dec 2022 22:16:08 +0100 Subject: [PATCH 16/89] Fix sphere redistribution --- src/d_clisrv.c | 54 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index eee820db2..432001b80 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2333,39 +2333,39 @@ static void RedistributeSpecialStageSpheres(INT32 playernum) } count--; - sincrement = spheres = players[playernum].spheres; - rincrement = rings = players[playernum].rings; + spheres = players[playernum].spheres; + rings = players[playernum].rings; - if (count) + while (count && (spheres || rings)) { - sincrement /= count; - rincrement /= count; - } + sincrement = max(spheres / count, 1); + rincrement = max(rings / count, 1); - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && i != playernum) + for (i = 0; i < MAXPLAYERS; i++) { - if (spheres < 2*sincrement) + if (playeringame[i] && i != playernum) { - P_GivePlayerSpheres(&players[i], spheres); - spheres = 0; - } - else - { - P_GivePlayerSpheres(&players[i], sincrement); - spheres -= sincrement; - } + if (spheres < sincrement) + { + P_GivePlayerSpheres(&players[i], spheres); + spheres = 0; + } + else + { + P_GivePlayerSpheres(&players[i], sincrement); + spheres -= sincrement; + } - if (rings < 2*rincrement) - { - P_GivePlayerRings(&players[i], rings); - rings = 0; - } - else - { - P_GivePlayerRings(&players[i], rincrement); - rings -= rincrement; + if (rings < rincrement) + { + P_GivePlayerRings(&players[i], rings); + rings = 0; + } + else + { + P_GivePlayerRings(&players[i], rincrement); + rings -= rincrement; + } } } } From 36613d58e9177f868a400a15d53f4600e8d6d36e Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 29 Dec 2022 22:30:26 +0100 Subject: [PATCH 17/89] Cleanup sphere redistribution --- src/d_clisrv.c | 57 ++++++++++++++++---------------------------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 432001b80..1b344a0de 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2321,52 +2321,31 @@ static void UnlinkPlayerFromNode(INT32 playernum) // I feel like this shouldn't even be in this file at all, but well. static void RedistributeSpecialStageSpheres(INT32 playernum) { - INT32 i, count, sincrement, spheres, rincrement, rings; - - if (!G_IsSpecialStage(gamemap)) + if (!G_IsSpecialStage(gamemap) || D_NumPlayers() <= 1) return; - for (i = 0, count = 0; i < MAXPLAYERS; i++) + INT32 count = D_NumPlayers() - 1; + INT32 spheres = players[playernum].spheres; + INT32 rings = players[playernum].rings; + + while (spheres || rings) { - if (playeringame[i]) - count++; - } - - count--; - spheres = players[playernum].spheres; - rings = players[playernum].rings; - - while (count && (spheres || rings)) - { - sincrement = max(spheres / count, 1); - rincrement = max(rings / count, 1); + INT32 sincrement = max(spheres / count, 1); + INT32 rincrement = max(rings / count, 1); + INT32 i, n; for (i = 0; i < MAXPLAYERS; i++) { - if (playeringame[i] && i != playernum) - { - if (spheres < sincrement) - { - P_GivePlayerSpheres(&players[i], spheres); - spheres = 0; - } - else - { - P_GivePlayerSpheres(&players[i], sincrement); - spheres -= sincrement; - } + if (!playeringame[i] || i == playernum) + continue; - if (rings < rincrement) - { - P_GivePlayerRings(&players[i], rings); - rings = 0; - } - else - { - P_GivePlayerRings(&players[i], rincrement); - rings -= rincrement; - } - } + n = min(spheres, sincrement); + P_GivePlayerSpheres(&players[i], n); + spheres -= n; + + n = min(rings, rincrement); + P_GivePlayerRings(&players[i], n); + rings -= n; } } } From ba91520eab98474c949c6937523bad2bb98b2fc3 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 00:24:06 +0100 Subject: [PATCH 18/89] Remove useless return value for SV_AddWaitingPlayers --- src/d_clisrv.c | 12 +++--------- src/d_clisrv.h | 2 +- src/d_netcmd.c | 3 +-- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1b344a0de..0c6960236 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3262,9 +3262,9 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) LUA_HookInt(newplayernum, HOOK(PlayerJoin)); } -static boolean SV_AddWaitingPlayers(const char *name, const char *name2) +static void SV_AddWaitingPlayers(const char *name, const char *name2) { - INT32 node, n, newplayer = false; + INT32 node, n; UINT8 buf[2 + MAXPLAYERNAME]; UINT8 *p; INT32 newplayernum; @@ -3274,8 +3274,6 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) // splitscreen can allow 2 player in one node for (; netnodes[node].numplayerswaiting > 0; netnodes[node].numplayerswaiting--) { - newplayer = true; - newplayernum = FindRejoinerNum(node); if (newplayernum == -1) { @@ -3320,8 +3318,6 @@ static boolean SV_AddWaitingPlayers(const char *name, const char *name2) DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); } } - - return newplayer; } void CL_AddSplitscreenPlayer(void) @@ -3344,7 +3340,7 @@ boolean Playing(void) return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); } -boolean SV_SpawnServer(void) +void SV_SpawnServer(void) { if (demoplayback) G_StopDemo(); // reset engine parameter @@ -3371,8 +3367,6 @@ boolean SV_SpawnServer(void) CL_ConnectToServer(); else doomcom->numslots = 1; } - - return SV_AddWaitingPlayers(cv_playername.zstring, cv_playername2.zstring); } void SV_StopServer(void) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index e07864122..b3ee95c0c 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -412,7 +412,7 @@ void SendKick(UINT8 playernum, UINT8 msg); void NetUpdate(void); void SV_StartSinglePlayerServer(void); -boolean SV_SpawnServer(void); +void SV_SpawnServer(void); void SV_StopServer(void); void SV_ResetServer(void); void CL_AddSplitscreenPlayer(void); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 5f02bc2de..243501ba4 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1850,8 +1850,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese // reset players if there is a new one if (!IsPlayerAdmin(consoleplayer)) { - if (SV_SpawnServer()) - buf[0] &= ~(1<<1); + SV_SpawnServer(); if (!Playing()) // you failed to start a server somehow, so cancel the map change return; } From f27de6c324cab56a3ac17117b39e014717b22f0e Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 01:48:26 +0100 Subject: [PATCH 19/89] Remove useless condition --- src/d_clisrv.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0c6960236..bc0992539 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3563,17 +3563,14 @@ static void HandleConnect(SINT8 node) DEBFILE("new node joined\n"); } #ifndef NONET - if (netnodes[node].numplayerswaiting) + if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) { - if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) - { - SV_SendSaveGame(node, false); // send a complete game state - DEBFILE("send savegame\n"); - } - SV_AddWaitingPlayers(names[0], names[1]); - joindelay += cv_joindelay.value * TICRATE; - player_joining = true; + SV_SendSaveGame(node, false); // send a complete game state + DEBFILE("send savegame\n"); } + SV_AddWaitingPlayers(names[0], names[1]); + joindelay += cv_joindelay.value * TICRATE; + player_joining = true; #endif } } From 388505db62ff627dcd25a2911c45a3188010861d Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 02:46:59 +0100 Subject: [PATCH 20/89] Remove unneeded numplayerswaiting field --- src/d_clisrv.c | 98 ++++++++++++++++++++++++-------------------------- src/d_net.h | 1 - 2 files changed, 47 insertions(+), 52 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index bc0992539..7aafdf539 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3262,62 +3262,54 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) LUA_HookInt(newplayernum, HOOK(PlayerJoin)); } -static void SV_AddWaitingPlayers(const char *name, const char *name2) +static void SV_AddPlayer(SINT8 node, const char *name) { - INT32 node, n; + INT32 n; UINT8 buf[2 + MAXPLAYERNAME]; UINT8 *p; INT32 newplayernum; - for (node = 0; node < MAXNETNODES; node++) + newplayernum = FindRejoinerNum(node); + if (newplayernum == -1) { - // splitscreen can allow 2 player in one node - for (; netnodes[node].numplayerswaiting > 0; netnodes[node].numplayerswaiting--) + // search for a free playernum + // we can't use playeringame since it is not updated here + for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) { - newplayernum = FindRejoinerNum(node); - if (newplayernum == -1) - { - // search for a free playernum - // we can't use playeringame since it is not updated here - for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) - { - if (playeringame[newplayernum]) - continue; - for (n = 0; n < MAXNETNODES; n++) - if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum) - break; - if (n == MAXNETNODES) - break; - } - } - - // should never happen since we check the playernum - // before accepting the join - I_Assert(newplayernum < MAXPLAYERS); - - playernode[newplayernum] = (UINT8)node; - - p = buf + 2; - buf[0] = (UINT8)node; - buf[1] = newplayernum; - if (netnodes[node].numplayers < 1) - { - netnodes[node].player = newplayernum; - WRITESTRINGN(p, name, MAXPLAYERNAME); - } - else - { - netnodes[node].player2 = newplayernum; - buf[1] |= 0x80; - WRITESTRINGN(p, name2, MAXPLAYERNAME); - } - netnodes[node].numplayers++; - - SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); - - DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); + if (playeringame[newplayernum]) + continue; + for (n = 0; n < MAXNETNODES; n++) + if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum) + break; + if (n == MAXNETNODES) + break; } } + + // should never happen since we check the playernum + // before accepting the join + I_Assert(newplayernum < MAXPLAYERS); + + playernode[newplayernum] = (UINT8)node; + + p = buf + 2; + buf[0] = (UINT8)node; + buf[1] = newplayernum; + if (netnodes[node].numplayers < 1) + { + netnodes[node].player = newplayernum; + } + else + { + netnodes[node].player2 = newplayernum; + buf[1] |= 0x80; + } + WRITESTRINGN(p, name, MAXPLAYERNAME); + netnodes[node].numplayers++; + + SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); + + DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); } void CL_AddSplitscreenPlayer(void) @@ -3522,11 +3514,12 @@ static void HandleConnect(SINT8 node) SV_SendRefuse(node, refuse); else { + INT32 numplayers = netbuffer->u.clientcfg.localplayers; #ifndef NONET boolean newnode = false; #endif - for (i = 0; i < netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers; i++) + for (i = 0; i < numplayers; i++) { strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) @@ -3537,7 +3530,6 @@ static void HandleConnect(SINT8 node) } // client authorised to join - netnodes[node].numplayerswaiting = (UINT8)(netbuffer->u.clientcfg.localplayers - netnodes[node].numplayers); if (!netnodes[node].ingame) { gamestate_t backupstate = gamestate; @@ -3568,7 +3560,12 @@ static void HandleConnect(SINT8 node) SV_SendSaveGame(node, false); // send a complete game state DEBFILE("send savegame\n"); } - SV_AddWaitingPlayers(names[0], names[1]); + + // Splitscreen can allow 2 players in one node + SV_AddPlayer(node, names[0]); + if (numplayers > 1) + SV_AddPlayer(node, names[1]); + joindelay += cv_joindelay.value * TICRATE; player_joining = true; #endif @@ -3993,7 +3990,6 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) // nodeingame will be put false in the execution of kick command // this allow to send some packets to the quitting client to have their ack back - netnodes[node].numplayerswaiting = 0; if (netconsole != -1 && playeringame[netconsole]) { UINT8 kickmsg; diff --git a/src/d_net.h b/src/d_net.h index 9eee53894..c6c9e2894 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -45,7 +45,6 @@ typedef struct netnode_s SINT8 player; SINT8 player2; // say the numplayer for this node if any (splitscreen) UINT8 numplayers; // used specialy for scplitscreen - UINT8 numplayerswaiting; tic_t tic; // what tic the client have received tic_t supposedtic; // nettics prevision for smaller packet From 6bbb032581828760585e22b64e9dbe395ea74ee1 Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Fri, 30 Dec 2022 11:19:29 +0100 Subject: [PATCH 21/89] UDMF: Use string values for the sector "triggerer" field --- extras/conf/udb/Includes/SRB222_misc.cfg | 10 ++------- src/p_setup.c | 26 ++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/extras/conf/udb/Includes/SRB222_misc.cfg b/extras/conf/udb/Includes/SRB222_misc.cfg index fcc24741e..ed0488a3f 100644 --- a/extras/conf/udb/Includes/SRB222_misc.cfg +++ b/extras/conf/udb/Includes/SRB222_misc.cfg @@ -265,14 +265,8 @@ universalfields triggerer { - type = 0; - default = 0; - enum - { - 0 = "Player"; - 1 = "All players"; - 2 = "Object"; - } + type = 2; + default = "Player"; } } diff --git a/src/p_setup.c b/src/p_setup.c index eedda1b08..f31eca076 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1761,7 +1761,14 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char else if (fastcmp(param, "triggertag")) sectors[i].triggertag = atol(val); else if (fastcmp(param, "triggerer")) - sectors[i].triggerer = atol(val); + { + if (fastcmp(val, "Player")) + sectors[i].triggerer = TO_PLAYER; + if (fastcmp(val, "AllPlayers")) + sectors[i].triggerer = TO_ALLPLAYERS; + if (fastcmp(val, "Mobj")) + sectors[i].triggerer = TO_MOBJ; + } } static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char *val) @@ -2633,7 +2640,22 @@ static void P_WriteTextmap(void) if (wsectors[i].triggertag != 0) fprintf(f, "triggertag = %d;\n", wsectors[i].triggertag); if (wsectors[i].triggerer != 0) - fprintf(f, "triggerer = %d;\n", wsectors[i].triggerer); + { + switch (wsectors[i].triggerer) + { + case TO_PLAYER: + fprintf(f, "triggerer = \"Player\";\n"); + break; + case TO_ALLPLAYERS: + fprintf(f, "triggerer = \"AllPlayers\";\n"); + break; + case TO_MOBJ: + fprintf(f, "triggerer = \"Mobj\";\n"); + break; + default: + break; + } + } fprintf(f, "}\n"); fprintf(f, "\n"); } From d561b5a0bf08a6dfa616640bf7c2eed74efed7e5 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 13:10:01 +0100 Subject: [PATCH 22/89] Remove broken joinnextround console variable --- src/d_clisrv.c | 6 ------ src/d_clisrv.h | 2 +- src/d_netcmd.c | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 7aafdf539..31a19001a 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2925,7 +2925,6 @@ static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, N consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); -consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; @@ -3532,17 +3531,13 @@ static void HandleConnect(SINT8 node) // client authorised to join if (!netnodes[node].ingame) { - gamestate_t backupstate = gamestate; #ifndef NONET newnode = true; #endif SV_AddNode(node); - if (cv_joinnextround.value && gameaction == ga_nothing) - G_SetGamestate(GS_WAITINGPLAYERS); if (!SV_SendServerConfig(node)) { - G_SetGamestate(backupstate); /// \note Shouldn't SV_SendRefuse be called before ResetNode? ResetNode(node); SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); @@ -3551,7 +3546,6 @@ static void HandleConnect(SINT8 node) } //if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc? // SV_SendPlayerConfigs(node); // send bare minimum player info - G_SetGamestate(backupstate); DEBFILE("new node joined\n"); } #ifndef NONET diff --git a/src/d_clisrv.h b/src/d_clisrv.h index b3ee95c0c..99d859924 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -394,7 +394,7 @@ extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; -extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout; +extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout; extern consvar_t cv_resynchattempts, cv_blamecfail; extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 243501ba4..64f478ff7 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -630,7 +630,6 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_downloadspeed); #ifndef NONET CV_RegisterVar(&cv_allownewplayer); - CV_RegisterVar(&cv_joinnextround); CV_RegisterVar(&cv_showjoinaddress); CV_RegisterVar(&cv_blamecfail); #endif From c9dc44c2f6f53d7a34d2decd89cbfa5624e51514 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 14:01:03 +0100 Subject: [PATCH 23/89] Cleanup HandleConnect --- src/d_clisrv.c | 96 +++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 31a19001a..699c372d7 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3501,69 +3501,55 @@ ConnectionRefused (SINT8 node, INT32 rejoinernum) static void HandleConnect(SINT8 node) { char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; + INT32 numplayers = netbuffer->u.clientcfg.localplayers; INT32 rejoinernum; INT32 i; - const char *refuse; rejoinernum = FindRejoinerNum(node); - refuse = ConnectionRefused(node, rejoinernum); - + const char *refuse = ConnectionRefused(node, rejoinernum); if (refuse) - SV_SendRefuse(node, refuse); - else { - INT32 numplayers = netbuffer->u.clientcfg.localplayers; -#ifndef NONET - boolean newnode = false; -#endif - - for (i = 0; i < numplayers; i++) - { - strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); - if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) - { - SV_SendRefuse(node, "Bad player name"); - return; - } - } - - // client authorised to join - if (!netnodes[node].ingame) - { -#ifndef NONET - newnode = true; -#endif - SV_AddNode(node); - - if (!SV_SendServerConfig(node)) - { - /// \note Shouldn't SV_SendRefuse be called before ResetNode? - ResetNode(node); - SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); - /// \todo fix this !!! - return; // restart the while - } - //if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc? - // SV_SendPlayerConfigs(node); // send bare minimum player info - DEBFILE("new node joined\n"); - } -#ifndef NONET - if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) - { - SV_SendSaveGame(node, false); // send a complete game state - DEBFILE("send savegame\n"); - } - - // Splitscreen can allow 2 players in one node - SV_AddPlayer(node, names[0]); - if (numplayers > 1) - SV_AddPlayer(node, names[1]); - - joindelay += cv_joindelay.value * TICRATE; - player_joining = true; -#endif + SV_SendRefuse(node, refuse); + return; } + + for (i = 0; i < numplayers; i++) + { + strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); + if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) + { + SV_SendRefuse(node, "Bad player name"); + return; + } + } + + SV_AddNode(node); + + if (!SV_SendServerConfig(node)) + { + /// \note Shouldn't SV_SendRefuse be called before ResetNode? + ResetNode(node); + SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); + /// \todo fix this !!! + return; // restart the while + } + DEBFILE("new node joined\n"); + +#ifndef NONET + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) + { + SV_SendSaveGame(node, false); // send a complete game state + DEBFILE("send savegame\n"); + } + + // Splitscreen can allow 2 players in one node + for (i = 0; i < numplayers; i++) + SV_AddPlayer(node, names[i]); + + joindelay += cv_joindelay.value * TICRATE; + player_joining = true; +#endif } /** Called when a PT_SERVERSHUTDOWN packet is received From 6ae44fe91d7fb5149db1bec4e255fdb17fb165fe Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 14:01:37 +0100 Subject: [PATCH 24/89] Rename ConnectionRefused to be more descriptive --- src/d_clisrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 699c372d7..d7d02f9bb 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3422,7 +3422,7 @@ static size_t TotalTextCmdPerTic(tic_t tic) } static const char * -ConnectionRefused (SINT8 node, INT32 rejoinernum) +GetRefuseMessage (SINT8 node, INT32 rejoinernum) { clientconfig_pak *cc = &netbuffer->u.clientcfg; @@ -3507,7 +3507,7 @@ static void HandleConnect(SINT8 node) rejoinernum = FindRejoinerNum(node); - const char *refuse = ConnectionRefused(node, rejoinernum); + const char *refuse = GetRefuseMessage(node, rejoinernum); if (refuse) { SV_SendRefuse(node, refuse); From e519f306fbc36b1f595defd8989ae37bb6672eb4 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 14:03:32 +0100 Subject: [PATCH 25/89] Ignore duplicate PT_CLIENTJOIN packets --- src/d_clisrv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d7d02f9bb..b25e34c56 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3505,6 +3505,10 @@ static void HandleConnect(SINT8 node) INT32 rejoinernum; INT32 i; + // Ignore duplicate packets + if (netnodes[node].ingame) + return; + rejoinernum = FindRejoinerNum(node); const char *refuse = GetRefuseMessage(node, rejoinernum); From 05d232c02909a8afca406a0b75a89bfe2fce1ef7 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 30 Dec 2022 15:07:57 +0100 Subject: [PATCH 26/89] Delete netcode Just kidding, I just deleted NONET --- src/Makefile | 1 - src/Makefile.d/features.mk | 4 +- src/Makefile.d/win32.mk | 2 - src/d_clisrv.c | 85 +------------ src/d_clisrv.h | 2 - src/d_net.c | 48 -------- src/d_net.h | 2 - src/d_netcmd.c | 2 - src/d_netfil.c | 6 - src/d_netfil.h | 2 - src/deh_tables.c | 2 +- src/doomdef.h | 3 +- src/hu_stuff.c | 31 ----- src/i_tcp.c | 238 ++++++++++++++----------------------- src/m_menu.c | 55 --------- src/m_menu.h | 2 +- src/mserv.c | 6 - src/sdl/i_system.c | 4 - 18 files changed, 96 insertions(+), 399 deletions(-) diff --git a/src/Makefile b/src/Makefile index 7571c8089..cec5dc88f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -64,7 +64,6 @@ # # Netplay incompatible # -------------------- -# NONET=1 - Disable online capability. # NOMD5=1 - Disable MD5 checksum (validation tool). # NOPOSTPROCESSING=1 - ? # MOBJCONSISTANCY=1 - ?? diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk index 8ba33383b..9acbb4d86 100644 --- a/src/Makefile.d/features.mk +++ b/src/Makefile.d/features.mk @@ -3,7 +3,7 @@ # passthru_opts+=\ - NONET NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ + NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ MOBJCONSISTANCY PACKETDROP ZDEBUG\ HAVE_MINIUPNPC\ @@ -46,13 +46,11 @@ sources+=apng.c endif endif -ifndef NONET ifndef NOCURL CURLCONFIG?=curl-config $(eval $(call Configure,CURL,$(CURLCONFIG))) opts+=-DHAVE_CURL endif -endif ifdef HAVE_MINIUPNPC libs+=-lminiupnpc diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk index 0e48ed683..e7269e1e7 100644 --- a/src/Makefile.d/win32.mk +++ b/src/Makefile.d/win32.mk @@ -35,12 +35,10 @@ libs+=-lws2_32 endif endif -ifndef NONET ifndef MINGW64 # miniupnc is broken with MINGW64 opts+=-I../libs -DSTATIC_MINIUPNPC libs+=-L../libs/miniupnpc/mingw$(32) -lws2_32 -liphlpapi endif -endif ifndef MINGW64 32=32 diff --git a/src/d_clisrv.c b/src/d_clisrv.c index b25e34c56..cb1ccd539 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -53,12 +53,10 @@ // aaaaaa #include "i_gamepad.h" -#ifndef NONET // cl loading screen #include "v_video.h" #include "f_finale.h" #include "snake.h" -#endif // // NETWORKING @@ -535,7 +533,6 @@ static cl_mode_t cl_mode = CL_SEARCHING; static UINT16 cl_lastcheckedfilecount = 0; // used for full file list -#ifndef NONET static void *snake = NULL; static void CL_DrawConnectionStatusBox(void) @@ -716,7 +713,6 @@ static inline void CL_DrawConnectionStatus(void) } } } -#endif static boolean CL_AskFileList(INT32 firstfile) { @@ -983,7 +979,6 @@ static boolean SV_SendServerConfig(INT32 node) return waspacketsent; } -#ifndef NONET #define SAVEGAMESIZE (768*1024) static boolean SV_ResendingSavegameToAnyone(void) @@ -1210,9 +1205,7 @@ static void CL_ReloadReceivedSavegame(void) CONS_Printf(M_GetText("Game state reloaded\n")); } -#endif -#ifndef NONET static void SendAskInfo(INT32 node) { const tic_t asktime = I_GetTime(); @@ -1430,12 +1423,8 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif/*MASTERSERVER*/ } -#endif // ifndef NONET - static void M_ConfirmConnect(event_t *ev) { -#ifndef NONET - if (ev->type == ev_keydown || ev->type == ev_gamepad_down) { if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A)) @@ -1459,9 +1448,6 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } } -#else - (void)ev; -#endif } static boolean CL_FinishedFileList(void) @@ -1541,7 +1527,6 @@ static boolean CL_FinishedFileList(void) return false; } -#ifndef NONET downloadcompletednum = 0; downloadcompletedsize = 0; totalfilesrequestednum = 0; @@ -1561,7 +1546,6 @@ static boolean CL_FinishedFileList(void) downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20)); else downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10)); -#endif if (serverisfull) M_StartMessage(va(M_GetText( @@ -1586,7 +1570,6 @@ static boolean CL_FinishedFileList(void) return true; } -#ifndef NONET static const char * InvalidServerReason (serverinfo_pak *info) { #define EOT "\nPress ESC\n" @@ -1650,7 +1633,6 @@ static const char * InvalidServerReason (serverinfo_pak *info) #undef EOT } -#endif // ifndef NONET /** Called by CL_ServerConnectionTicker * @@ -1662,7 +1644,6 @@ static const char * InvalidServerReason (serverinfo_pak *info) */ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) { -#ifndef NONET INT32 i; // serverlist is updated by GetPacket function @@ -1732,11 +1713,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) SendAskInfo(servernode); *asksent = I_GetTime(); } -#else - (void)asksent; - // No netgames, so we skip this state. - cl_mode = CL_ASKJOIN; -#endif // ifndef NONET/else return true; } @@ -1756,10 +1732,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic boolean waitmore; INT32 i; -#ifdef NONET - (void)tmpsave; -#endif - switch (cl_mode) { case CL_SEARCHING: @@ -1824,12 +1796,12 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic ), NULL, MM_NOTHING); return false; } -#ifndef NONET + // 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 (I_GetTime() >= *asksent && CL_SendJoin()) { *asksent = I_GetTime() + NEWTICRATE*3; @@ -1842,7 +1814,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic 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) @@ -1853,7 +1824,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic } // don't break case continue to CL_CONNECTED else break; -#endif case CL_CONNECTED: case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect @@ -1898,10 +1868,8 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic memset(gamekeydown, 0, NUMKEYS); return false; } -#ifndef NONET else if (cl_mode == CL_DOWNLOADFILES && snake) Snake_Update(snake); -#endif if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) FileReceiveTicker(); @@ -1912,7 +1880,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic //FileSendTicker(); *oldtic = I_GetTime(); -#ifndef NONET if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) { if (!snake) @@ -1935,10 +1902,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic S_UpdateSounds(); S_UpdateClosedCaptions(); } -#else - CON_Drawer(); - I_UpdateNoVsync(); -#endif } else { @@ -1958,22 +1921,18 @@ static void CL_ConnectToServer(void) { INT32 pnumnodes, nodewaited = doomcom->numnodes, i; tic_t oldtic; -#ifndef NONET tic_t asksent; char tmpsave[256]; sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); lastfilenum = -1; -#endif cl_mode = CL_SEARCHING; -#ifndef NONET // Don't get a corrupt savegame error because tmpsave already exists if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) I_Error("Can't delete %s\n", tmpsave); -#endif if (netgame) { @@ -1994,7 +1953,6 @@ static void CL_ConnectToServer(void) pnumnodes = 1; oldtic = I_GetTime() - 1; -#ifndef NONET asksent = (tic_t) - TICRATE; firstconnectattempttime = I_GetTime(); @@ -2010,16 +1968,11 @@ static void CL_ConnectToServer(void) serverlist[i].info.version%100, serverlist[i].info.subversion); } SL_ClearServerList(servernode); -#endif do { // If the connection was aborted for some reason, leave -#ifndef NONET if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) -#else - if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL)) -#endif return; if (server) @@ -2037,7 +1990,6 @@ static void CL_ConnectToServer(void) displayplayer = consoleplayer; } -#ifndef NONET typedef struct banreason_s { char *reason; @@ -2276,7 +2228,6 @@ static void Command_connect(void) botskin = 0; CL_ConnectToServer(); } -#endif static void ResetNode(INT32 node) { @@ -2437,10 +2388,8 @@ void CL_Reset(void) FreeFileNeeded(); fileneedednum = 0; -#ifndef NONET totalfilesrequestednum = 0; totalfilesrequestedsize = 0; -#endif firstconnectattempttime = 0; serverisfull = false; connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack @@ -2448,7 +2397,6 @@ void CL_Reset(void) // D_StartTitle should get done now, but the calling function will handle it } -#ifndef NONET static void Command_GetPlayerNum(void) { INT32 i; @@ -2726,7 +2674,6 @@ static void Command_ResendGamestate(void) return; } } -#endif static void Got_KickCmd(UINT8 **p, INT32 playernum) { @@ -2810,10 +2757,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) { if (I_Ban && !I_Ban(playernode[(INT32)pnum])) CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); -#ifndef NONET else Ban_Add(reason); -#endif } switch (msg) @@ -2953,7 +2898,6 @@ void D_ClientServerInit(void) DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n", VERSION/100, VERSION%100, SUBVERSION)); -#ifndef NONET COM_AddCommand("getplayernum", Command_GetPlayerNum); COM_AddCommand("kick", Command_Kick); COM_AddCommand("ban", Command_Ban); @@ -2970,17 +2914,14 @@ void D_ClientServerInit(void) #endif #ifdef _DEBUG COM_AddCommand("numnodes", Command_Numnodes); -#endif #endif RegisterNetXCmd(XD_KICK, Got_KickCmd); RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); -#ifndef NONET #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif Ban_Load_File(false); -#endif gametic = 0; localgametic = 0; @@ -3540,7 +3481,6 @@ static void HandleConnect(SINT8 node) } DEBFILE("new node joined\n"); -#ifndef NONET if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) { SV_SendSaveGame(node, false); // send a complete game state @@ -3553,7 +3493,6 @@ static void HandleConnect(SINT8 node) joindelay += cv_joindelay.value * TICRATE; player_joining = true; -#endif } /** Called when a PT_SERVERSHUTDOWN packet is received @@ -3586,7 +3525,6 @@ static void HandleTimeout(SINT8 node) M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); } -#ifndef NONET /** Called when a PT_SERVERINFO packet is received * * \param node The packet sender @@ -3608,7 +3546,6 @@ static void HandleServerInfo(SINT8 node) SL_InsertServer(&netbuffer->u.serverinfo, node); } -#endif // Helper function for packets that should only be sent by the server // If it is NOT from the server, bail out and close the connection! @@ -3744,14 +3681,9 @@ static void PT_ServerCFG(SINT8 node) playernode[(UINT8)serverplayer] = servernode; if (netgame) -#ifndef NONET CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); -#else - CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); -#endif DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); -#ifndef NONET /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? /// Shouldn't them be downloaded even at intermission time? /// Also, according to HandleConnect, the server will send the savegame even during intermission... @@ -3759,7 +3691,6 @@ static void PT_ServerCFG(SINT8 node) netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) cl_mode = CL_DOWNLOADSAVEGAME; else -#endif cl_mode = CL_CONNECTED; } @@ -3823,9 +3754,7 @@ static void PT_ClientCmd(SINT8 node, INT32 netconsole) // Check player consistancy during the level if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) -#ifndef NONET && !SV_ResendingSavegameToAnyone() -#endif && !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime()) { if (cv_resynchattempts.value) @@ -4000,7 +3929,6 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) static void PT_CanReceiveGamestate(SINT8 node) { -#ifndef NONET if (client || netnodes[node].sendingsavegame) return; @@ -4008,9 +3936,6 @@ static void PT_CanReceiveGamestate(SINT8 node) SV_SendSaveGame(node, true); // Resend a complete game state netnodes[node].resendingsavegame = true; -#else - (void)node; -#endif } static void PT_AskLuaFile(SINT8 node) @@ -4134,7 +4059,6 @@ static void PT_WillResendGamestate(SINT8 node) { (void)node; -#ifndef NONET char tmpsave[256]; if (server || cl_redownloadinggamestate) @@ -4157,7 +4081,6 @@ static void PT_WillResendGamestate(SINT8 node) CL_PrepareDownloadSaveGame(tmpsave); cl_redownloadinggamestate = true; -#endif } static void PT_SendingLuaFile(SINT8 node) @@ -4296,13 +4219,11 @@ static void GetPackets(void) } } -#ifndef NONET if (netbuffer->packettype == PT_SERVERINFO) { HandleServerInfo(node); continue; } -#endif if (netbuffer->packettype == PT_PLAYERINFO) continue; // We do nothing with PLAYERINFO, that's for the MS browser. @@ -4906,11 +4827,9 @@ void NetUpdate(void) if (client) { -#ifndef NONET // If the client just finished redownloading the game state, load it if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) CL_ReloadReceivedSavegame(); -#endif CL_SendClientCmd(); // Send tic cmd hu_redownloadinggamestate = cl_redownloadinggamestate; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 99d859924..0d6add13a 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -437,11 +437,9 @@ boolean TryRunTics(tic_t realtic); /*boolean AddLmpExtradata(UINT8 **demo_p, INT32 playernum); void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum);*/ -#ifndef NONET // translate a playername in a player number return -1 if not found and // print a error message in the console SINT8 nametonum(const char *name); -#endif extern char motd[254], server_context[8]; extern UINT8 playernode[MAXPLAYERS]; diff --git a/src/d_net.c b/src/d_net.c index a7e1eb16d..9e3759d32 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -138,7 +138,6 @@ boolean Net_GetNetStat(void) #define URGENTFREESLOTNUM 10 #define ACKTOSENDTIMEOUT (TICRATE/11) -#ifndef NONET typedef struct { UINT8 acknum; @@ -152,7 +151,6 @@ typedef struct doomdata_t data; } pak; } ackpak_t; -#endif typedef enum { @@ -160,10 +158,8 @@ typedef enum NF_TIMEOUT = 2, // Flag is set when the node got a timeout } node_flags_t; -#ifndef NONET // Table of packets that were not acknowleged can be resent (the sender window) static ackpak_t ackpak[MAXACKPACKETS]; -#endif typedef struct { @@ -191,7 +187,6 @@ typedef struct static node_t nodes[MAXNETNODES]; #define NODETIMEOUT 14 -#ifndef NONET // return <0 if a < b (mod 256) // 0 if a = n (mod 256) // >0 if a > b (mod 256) @@ -426,21 +421,15 @@ static boolean Processackpak(void) } return goodpacket; } -#endif // send special packet with only ack on it void Net_SendAcks(INT32 node) { -#ifdef NONET - (void)node; -#else netbuffer->packettype = PT_NOTHING; M_Memcpy(netbuffer->u.textcmd, nodes[node].acktosend, MAXACKTOSEND); HSendPacket(node, false, 0, MAXACKTOSEND); -#endif } -#ifndef NONET static void GotAcks(void) { INT32 i, j; @@ -463,7 +452,6 @@ static void GotAcks(void) } } } -#endif void Net_ConnectionTimeout(INT32 node) { @@ -489,7 +477,6 @@ void Net_ConnectionTimeout(INT32 node) // Resend the data if needed void Net_AckTicker(void) { -#ifndef NONET INT32 i; for (i = 0; i < MAXACKPACKETS; i++) @@ -536,16 +523,12 @@ void Net_AckTicker(void) } } } -#endif } // Remove last packet received ack before resending the ackreturn // (the higher layer doesn't have room, or something else ....) void Net_UnAcknowledgePacket(INT32 node) { -#ifdef NONET - (void)node; -#else INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND; DEBFILE(va("UnAcknowledge node %d\n", node)); if (!node) @@ -577,10 +560,8 @@ void Net_UnAcknowledgePacket(INT32 node) if (!nodes[node].firstacktosend) nodes[node].firstacktosend = 1; } -#endif } -#ifndef NONET /** Checks if all acks have been received * * \return True if all acks have been received @@ -596,7 +577,6 @@ static boolean Net_AllAcksReceived(void) return true; } -#endif /** Waits for all ackreturns * @@ -605,9 +585,6 @@ static boolean Net_AllAcksReceived(void) */ void Net_WaitAllAckReceived(UINT32 timeout) { -#ifdef NONET - (void)timeout; -#else tic_t tictac = I_GetTime(); timeout = tictac + timeout*NEWTICRATE; @@ -623,7 +600,6 @@ void Net_WaitAllAckReceived(UINT32 timeout) HGetPacket(); Net_AckTicker(); } -#endif } static void InitNode(node_t *node) @@ -639,10 +615,8 @@ static void InitAck(void) { INT32 i; -#ifndef NONET for (i = 0; i < MAXACKPACKETS; i++) ackpak[i].acknum = 0; -#endif for (i = 0; i < MAXNETNODES; i++) InitNode(&nodes[i]); @@ -655,9 +629,6 @@ static void InitAck(void) */ void Net_AbortPacketType(UINT8 packettype) { -#ifdef NONET - (void)packettype; -#else INT32 i; for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && (ackpak[i].pak.data.packettype == packettype @@ -665,7 +636,6 @@ void Net_AbortPacketType(UINT8 packettype) { ackpak[i].acknum = 0; } -#endif } // ----------------------------------------------------------------- @@ -675,9 +645,6 @@ void Net_AbortPacketType(UINT8 packettype) // remove a node, clear all ack from this node and reset askret void Net_CloseConnection(INT32 node) { -#ifdef NONET - (void)node; -#else INT32 i; boolean forceclose = (node & FORCECLOSE) != 0; @@ -722,10 +689,8 @@ void Net_CloseConnection(INT32 node) if (server) SV_AbortLuaFileTransfer(node); I_NetFreeNodenum(node); -#endif } -#ifndef NONET // // Checksum // @@ -741,7 +706,6 @@ static UINT32 NetbufferChecksum(void) return LONG(c); } -#endif #ifdef DEBUGFILE @@ -983,14 +947,12 @@ void Command_Droprate(void) packetdroprate = droprate; } -#ifndef NONET static boolean ShouldDropPacket(void) { return (packetdropquantity[netbuffer->packettype]) || (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100; } #endif -#endif // // HSendPacket @@ -1025,11 +987,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen if (!netgame) I_Error("Tried to transmit to another node"); -#ifdef NONET - (void)node; - (void)reliable; - (void)acknum; -#else // do this before GetFreeAcknum because this function backups // the current packet doomcom->remotenode = (INT16)node; @@ -1090,8 +1047,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen } #endif -#endif // ndef NONET - return true; } @@ -1125,8 +1080,6 @@ boolean HGetPacket(void) if (!netgame) return false; -#ifndef NONET - while(true) { //nodejustjoined = I_NetGet(); @@ -1186,7 +1139,6 @@ boolean HGetPacket(void) } break; } -#endif // ndef NONET return true; } diff --git a/src/d_net.h b/src/d_net.h index c6c9e2894..803b49d74 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -66,9 +66,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength); boolean HGetPacket(void); void D_SetDoomcom(void); -#ifndef NONET void D_SaveBan(void); -#endif boolean D_CheckNetGame(void); void D_CloseConnection(void); void Net_UnAcknowledgePacket(INT32 node); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 64f478ff7..69eed90de 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -628,11 +628,9 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_maxsend); CV_RegisterVar(&cv_noticedownload); CV_RegisterVar(&cv_downloadspeed); -#ifndef NONET CV_RegisterVar(&cv_allownewplayer); CV_RegisterVar(&cv_showjoinaddress); CV_RegisterVar(&cv_blamecfail); -#endif COM_AddCommand("ping", Command_Ping_f); CV_RegisterVar(&cv_nettimeout); diff --git a/src/d_netfil.c b/src/d_netfil.c index 1fee1df81..bf3952cc9 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -103,14 +103,12 @@ typedef struct } pauseddownload_t; 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 luafiletransfer_t *luafiletransfers = NULL; boolean waitingforluafiletransfer = false; @@ -250,9 +248,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi void CL_PrepareDownloadSaveGame(const char *tmpsave) { -#ifndef NONET lastfilenum = -1; -#endif FreeFileNeeded(); AllocFileNeeded(1); @@ -1466,9 +1462,7 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s); } -#ifndef NONET lastfilenum = filenum; -#endif } /** \brief Checks if a node is downloading a file diff --git a/src/d_netfil.h b/src/d_netfil.h index 5c7f4ef49..ec53be587 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -70,13 +70,11 @@ extern INT32 fileneedednum; extern fileneeded_t *fileneeded; extern char downloaddir[512]; -#ifndef NONET extern INT32 lastfilenum; extern INT32 downloadcompletednum; extern UINT32 downloadcompletedsize; extern INT32 totalfilesrequestednum; extern UINT32 totalfilesrequestedsize; -#endif void AllocFileNeeded(INT32 size); void FreeFileNeeded(void); diff --git a/src/deh_tables.c b/src/deh_tables.c index a2cc9732d..97b6373cd 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4831,7 +4831,7 @@ const char *const MENUTYPES_LIST[] = { "MP_SERVER", "MP_CONNECT", "MP_ROOM", - "MP_PLAYERSETUP", // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET + "MP_PLAYERSETUP", "MP_SERVER_OPTIONS", // Options diff --git a/src/doomdef.h b/src/doomdef.h index 24b4fa980..b70f0a47e 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -56,7 +56,6 @@ #endif #ifdef _WINDOWS -#define NONET #if !defined (HWRENDER) && !defined (NOHW) #define HWRENDER #endif @@ -657,7 +656,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Maintain compatibility with older 2.2 demos #define OLD22DEMOCOMPAT -#if defined (HAVE_CURL) && ! defined (NONET) +#ifdef HAVE_CURL #define MASTERSERVER #else #undef UPDATE_ALERT diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 805aa694f..37c3f0dc3 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -175,14 +175,12 @@ static huddrawlist_h luahuddrawlist_scores; static tic_t resynch_ticker = 0; -#ifndef NONET // just after static void Command_Say_f(void); static void Command_Sayto_f(void); static void Command_Sayteam_f(void); static void Command_CSay_f(void); static void Got_Saycmd(UINT8 **p, INT32 playernum); -#endif void HU_LoadGraphics(void) { @@ -327,13 +325,11 @@ void HU_LoadGraphics(void) // void HU_Init(void) { -#ifndef NONET COM_AddCommand("say", Command_Say_f); COM_AddCommand("sayto", Command_Sayto_f); COM_AddCommand("sayteam", Command_Sayteam_f); COM_AddCommand("csay", Command_CSay_f); RegisterNetXCmd(XD_SAY, Got_Saycmd); -#endif // set shift translation table shiftxform = english_shiftxform; @@ -363,8 +359,6 @@ void HU_Start(void) // EXECUTION //====================================================================== -#ifndef NONET - // EVERY CHANGE IN THIS SCRIPT IS LOL XD! BY VINCYTM static UINT32 chat_nummsg_log = 0; @@ -412,11 +406,9 @@ static void HU_removeChatText_Log(void) } chat_nummsg_log--; // lost 1 msg. } -#endif void HU_AddChatText(const char *text, boolean playsound) { -#ifndef NONET if (playsound && cv_consolechat.value != 2) // Don't play the sound if we're using hidden chat. S_StartSound(NULL, sfx_radio); // reguardless of our preferences, put all of this in the chat buffer in case we decide to change from oldchat mid-game. @@ -438,14 +430,8 @@ void HU_AddChatText(const char *text, boolean playsound) CONS_Printf("%s\n", text); else // if we aren't, still save the message to log.txt CON_LogMessage(va("%s\n", text)); -#else - (void)playsound; - CONS_Printf("%s\n", text); -#endif } -#ifndef NONET - /** Runs a say command, sending an ::XD_SAY message. * A say command consists of a signed 8-bit integer for the target, an * unsigned 8-bit flag variable, and then the message itself. @@ -865,8 +851,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) #endif } -#endif - // // void HU_Ticker(void) @@ -882,7 +866,6 @@ void HU_Ticker(void) else hu_showscores = false; -#ifndef NONET if (chat_on) { // count down the scroll timer. @@ -910,7 +893,6 @@ void HU_Ticker(void) HU_removeChatText_Mini(); } } -#endif if (cechotimer > 0) --cechotimer; @@ -918,8 +900,6 @@ void HU_Ticker(void) resynch_ticker++; } -#ifndef NONET - static boolean teamtalk = false; static boolean justscrolleddown; static boolean justscrolledup; @@ -1027,8 +1007,6 @@ static void HU_sendChatMessage(void) } } -#endif - void HU_clearChatChars(void) { memset(w_chat, '\0', sizeof(w_chat)); @@ -1043,16 +1021,13 @@ void HU_clearChatChars(void) // boolean HU_Responder(event_t *ev) { -#ifndef NONET INT32 c=0; -#endif if (ev->type != ev_keydown) return false; // only KeyDown events now... -#ifndef NONET c = (INT32)ev->key; if (!chat_on) @@ -1202,7 +1177,6 @@ boolean HU_Responder(event_t *ev) return true; } -#endif return false; } @@ -1212,8 +1186,6 @@ boolean HU_Responder(event_t *ev) // HEADS UP DRAWING //====================================================================== -#ifndef NONET - // Precompile a wordwrapped string to any given width. // This is a muuuch better method than V_WORDWRAP. // again stolen and modified a bit from video.c, don't mind me, will need to rearrange this one day. @@ -1793,7 +1765,6 @@ static void HU_DrawChat_Old(void) if (hu_tick < 4) V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, true); } -#endif // Draw crosshairs at the exact center of the view. // In splitscreen, crosshairs are stretched vertically to compensate for V_PERPLAYER squishing them. @@ -1933,7 +1904,6 @@ static void HU_DrawDemoInfo(void) // void HU_Drawer(void) { -#ifndef NONET // draw chat string plus cursor if (chat_on) { @@ -1950,7 +1920,6 @@ void HU_Drawer(void) if (!OLDCHAT && cv_consolechat.value < 2 && netgame) // Don't display minimized chat if you set the mode to Window (Hidden) HU_drawMiniChat(); // draw messages in a cool fashion. } -#endif if (cechotimer) HU_DrawCEcho(); diff --git a/src/i_tcp.c b/src/i_tcp.c index 00aebddd3..ec3d1ae93 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -38,96 +38,87 @@ #include "doomdef.h" -#if defined (NOMD5) && !defined (NONET) - //#define NONET +#ifdef USE_WINSOCK1 + #include +#else + #ifndef USE_WINSOCK + #include + #ifdef __APPLE_CC__ + #ifndef _BSD_SOCKLEN_T_ + #define _BSD_SOCKLEN_T_ + #endif //_BSD_SOCKLEN_T_ + #endif //__APPLE_CC__ + #include + #include + #include + #include + #endif //normal BSD API + + #include + #include + + #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) + #include + #endif // UNIXCOMMON #endif -#ifdef NONET - #undef HAVE_MINIUPNPC -#else - #ifdef USE_WINSOCK1 - #include - #else - #ifndef USE_WINSOCK - #include - #ifdef __APPLE_CC__ - #ifndef _BSD_SOCKLEN_T_ - #define _BSD_SOCKLEN_T_ - #endif //_BSD_SOCKLEN_T_ - #endif //__APPLE_CC__ - #include - #include - #include - #include - #endif //normal BSD API - - #include - #include - - #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) - #include - #endif // UNIXCOMMON +#ifdef USE_WINSOCK + // some undefined under win32 + #undef errno + //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? + #define errno h_errno // some very strange things happen when not using h_error?!? + #ifdef EWOULDBLOCK + #undef EWOULDBLOCK #endif - - #ifdef USE_WINSOCK - // some undefined under win32 - #undef errno - //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? - #define errno h_errno // some very strange things happen when not using h_error?!? - #ifdef EWOULDBLOCK - #undef EWOULDBLOCK - #endif - #define EWOULDBLOCK WSAEWOULDBLOCK - #ifdef EMSGSIZE - #undef EMSGSIZE - #endif - #define EMSGSIZE WSAEMSGSIZE - #ifdef ECONNREFUSED - #undef ECONNREFUSED - #endif - #define ECONNREFUSED WSAECONNREFUSED - #ifdef ETIMEDOUT - #undef ETIMEDOUT - #endif - #define ETIMEDOUT WSAETIMEDOUT - #ifndef IOC_VENDOR - #define IOC_VENDOR 0x18000000 - #endif - #ifndef _WSAIOW - #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) - #endif - #ifndef SIO_UDP_CONNRESET - #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) - #endif - #ifndef AI_ADDRCONFIG - #define AI_ADDRCONFIG 0x00000400 - #endif - #ifndef STATUS_INVALID_PARAMETER - #define STATUS_INVALID_PARAMETER 0xC000000D - #endif - #endif // USE_WINSOCK - - typedef union - { - struct sockaddr any; - struct sockaddr_in ip4; - #ifdef HAVE_IPV6 - struct sockaddr_in6 ip6; + #define EWOULDBLOCK WSAEWOULDBLOCK + #ifdef EMSGSIZE + #undef EMSGSIZE #endif - } mysockaddr_t; + #define EMSGSIZE WSAEMSGSIZE + #ifdef ECONNREFUSED + #undef ECONNREFUSED + #endif + #define ECONNREFUSED WSAECONNREFUSED + #ifdef ETIMEDOUT + #undef ETIMEDOUT + #endif + #define ETIMEDOUT WSAETIMEDOUT + #ifndef IOC_VENDOR + #define IOC_VENDOR 0x18000000 + #endif + #ifndef _WSAIOW + #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) + #endif + #ifndef SIO_UDP_CONNRESET + #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) + #endif + #ifndef AI_ADDRCONFIG + #define AI_ADDRCONFIG 0x00000400 + #endif + #ifndef STATUS_INVALID_PARAMETER + #define STATUS_INVALID_PARAMETER 0xC000000D + #endif +#endif // USE_WINSOCK - #ifdef HAVE_MINIUPNPC - #ifdef STATIC_MINIUPNPC - #define STATICLIB - #endif - #include "miniupnpc/miniwget.h" - #include "miniupnpc/miniupnpc.h" - #include "miniupnpc/upnpcommands.h" - #undef STATICLIB - static UINT8 UPNP_support = TRUE; - #endif // HAVE_MINIUPNC +typedef union +{ + struct sockaddr any; + struct sockaddr_in ip4; +#ifdef HAVE_IPV6 + struct sockaddr_in6 ip6; +#endif +} mysockaddr_t; -#endif // !NONET +#ifdef HAVE_MINIUPNPC + #ifdef STATIC_MINIUPNPC + #define STATICLIB + #endif + #include "miniupnpc/miniwget.h" + #include "miniupnpc/miniupnpc.h" + #include "miniupnpc/upnpcommands.h" + #undef STATICLIB + static UINT8 UPNP_support = TRUE; +#endif // HAVE_MINIUPNC #define MAXBANS 100 @@ -151,7 +142,7 @@ #define SELECTTEST #define DEFAULTPORT "5029" -#if defined (USE_WINSOCK) && !defined (NONET) +#ifdef USE_WINSOCK typedef SOCKET SOCKET_TYPE; #define ERRSOCKET (SOCKET_ERROR) #else @@ -163,22 +154,20 @@ #define ERRSOCKET (-1) #endif -#ifndef NONET - // define socklen_t in DOS/Windows if it is not already defined - #ifdef USE_WINSOCK1 - typedef int socklen_t; - #endif - static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; - static size_t mysocketses = 0; - static int myfamily[MAXNETNODES+1] = {0}; - static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET}; - static mysockaddr_t clientaddress[MAXNETNODES+1]; - static mysockaddr_t broadcastaddress[MAXNETNODES+1]; - static size_t broadcastaddresses = 0; - static boolean nodeconnected[MAXNETNODES+1]; - static mysockaddr_t banned[MAXBANS]; - static UINT8 bannedmask[MAXBANS]; +// define socklen_t in DOS/Windows if it is not already defined +#ifdef USE_WINSOCK1 + typedef int socklen_t; #endif +static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; +static size_t mysocketses = 0; +static int myfamily[MAXNETNODES+1] = {0}; +static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET}; +static mysockaddr_t clientaddress[MAXNETNODES+1]; +static mysockaddr_t broadcastaddress[MAXNETNODES+1]; +static size_t broadcastaddresses = 0; +static boolean nodeconnected[MAXNETNODES+1]; +static mysockaddr_t banned[MAXBANS]; +static UINT8 bannedmask[MAXBANS]; static size_t numbans = 0; static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? @@ -187,7 +176,6 @@ static boolean init_tcp_driver = false; static const char *serverport_name = DEFAULTPORT; static const char *clientport_name;/* any port */ -#ifndef NONET #ifdef USE_WINSOCK // stupid microsoft makes things complicated static char *get_WSAErrorStr(int e) @@ -374,47 +362,33 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk) #endif return s; } -#endif static const char *SOCK_GetNodeAddress(INT32 node) { if (node == 0) return "self"; -#ifdef NONET - return NULL; -#else if (!nodeconnected[node]) return NULL; return SOCK_AddrToStr(&clientaddress[node]); -#endif } static const char *SOCK_GetBanAddress(size_t ban) { if (ban >= numbans) return NULL; -#ifdef NONET - return NULL; -#else return SOCK_AddrToStr(&banned[ban]); -#endif } static const char *SOCK_GetBanMask(size_t ban) { -#ifdef NONET - (void)ban; -#else static char s[16]; //255.255.255.255 netmask? no, just CDIR for only if (ban >= numbans) return NULL; if (sprintf(s,"%d",bannedmask[ban]) > 0) return s; -#endif return NULL; } -#ifndef NONET static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { UINT32 bitmask = INADDR_NONE; @@ -518,9 +492,7 @@ void Command_Numnodes(void) connected, ingame); } #endif -#endif -#ifndef NONET // Returns true if a packet was received from a new node, false in all other cases static boolean SOCK_Get(void) { @@ -583,10 +555,8 @@ static boolean SOCK_Get(void) doomcom->remotenode = -1; // no packet return false; } -#endif // check if we can send (do not go over the buffer) -#ifndef NONET static fd_set masterset; @@ -636,9 +606,7 @@ static boolean SOCK_CanGet(void) return false; } #endif -#endif -#ifndef NONET static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr) { socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in); @@ -701,9 +669,7 @@ static void SOCK_Send(void) SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e)); } } -#endif -#ifndef NONET static void SOCK_FreeNodenum(INT32 numnode) { // can't disconnect from self :) @@ -718,12 +684,10 @@ static void SOCK_FreeNodenum(INT32 numnode) // put invalid address memset(&clientaddress[numnode], 0, sizeof (clientaddress[numnode])); } -#endif // // UDPsocket // -#ifndef NONET // allocate a socket static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen) @@ -1044,12 +1008,10 @@ static boolean UDP_Socket(void) return true; } -#endif boolean I_InitTcpDriver(void) { boolean tcp_was_up = init_tcp_driver; -#ifndef NONET if (!init_tcp_driver) { #ifdef USE_WINSOCK @@ -1104,7 +1066,7 @@ boolean I_InitTcpDriver(void) #endif init_tcp_driver = true; } -#endif + if (!tcp_was_up && init_tcp_driver) { I_AddExitFunc(I_ShutdownTcpDriver); @@ -1118,7 +1080,6 @@ boolean I_InitTcpDriver(void) return init_tcp_driver; } -#ifndef NONET static void SOCK_CloseSocket(void) { size_t i; @@ -1133,11 +1094,9 @@ static void SOCK_CloseSocket(void) mysockets[i] = ERRSOCKET; } } -#endif void I_ShutdownTcpDriver(void) { -#ifndef NONET SOCK_CloseSocket(); CONS_Printf("I_ShutdownTcpDriver: "); @@ -1147,10 +1106,8 @@ void I_ShutdownTcpDriver(void) #endif CONS_Printf("shut down\n"); init_tcp_driver = false; -#endif } -#ifndef NONET static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) { SINT8 newnode = -1; @@ -1194,11 +1151,9 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) I_freeaddrinfo(ai); return newnode; } -#endif static boolean SOCK_OpenSocket(void) { -#ifndef NONET size_t i; memset(clientaddress, 0, sizeof (clientaddress)); @@ -1222,18 +1177,12 @@ static boolean SOCK_OpenSocket(void) // build the socket but close it first SOCK_CloseSocket(); return UDP_Socket(); -#else - return false; -#endif } static boolean SOCK_Ban(INT32 node) { if (node > MAXNETNODES) return false; -#ifdef NONET - return false; -#else if (numbans == MAXBANS) return false; @@ -1252,16 +1201,10 @@ static boolean SOCK_Ban(INT32 node) #endif numbans++; return true; -#endif } static boolean SOCK_SetBanAddress(const char *address, const char *mask) { -#ifdef NONET - (void)address; - (void)mask; - return false; -#else struct my_addrinfo *ai, *runp, hints; int gaie; @@ -1306,7 +1249,6 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) I_freeaddrinfo(ai); return true; -#endif } static void SOCK_ClearBans(void) diff --git a/src/m_menu.c b/src/m_menu.c index 7c67cf2da..824ae5c46 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -147,9 +147,7 @@ static struct INT32 index; } gamepadInfo[MAX_CONNECTED_GAMEPADS + 1]; -#ifndef NONET static UINT32 serverlistpage; -#endif static UINT8 numsaves = 0; static saveinfo_t* savegameinfo = NULL; // Extra info about the save games. @@ -189,10 +187,8 @@ static void M_GoBack(INT32 choice); static void M_StopMessage(INT32 choice); static boolean stopstopmessage = false; -#ifndef NONET static void M_HandleServerPage(INT32 choice); static void M_RoomMenu(INT32 choice); -#endif // Prototyping is fun, innit? // ========================================================================== @@ -294,7 +290,6 @@ static void M_SetupMultiPlayer2(INT32 choice); static void M_StartSplitServerMenu(INT32 choice); static void M_StartServer(INT32 choice); static void M_ServerOptions(INT32 choice); -#ifndef NONET static void M_StartServerMenu(INT32 choice); static void M_ConnectMenu(INT32 choice); static void M_ConnectMenuModChecks(INT32 choice); @@ -302,7 +297,6 @@ static void M_Refresh(INT32 choice); static void M_Connect(INT32 choice); static void M_ChooseRoom(INT32 choice); menu_t MP_MainDef; -#endif // Options // Split into multiple parts due to size @@ -381,11 +375,9 @@ static void M_DrawVideoMode(void); static void M_DrawColorMenu(void); static void M_DrawScreenshotMenu(void); static void M_DrawMonitorToggles(void); -#ifndef NONET static void M_DrawConnectMenu(void); static void M_DrawMPMainMenu(void); static void M_DrawRoomMenu(void); -#endif static void M_DrawGamepadList(void); static void M_DrawSetupMultiPlayerMenu(void); @@ -399,10 +391,8 @@ static void M_HandleImageDef(INT32 choice); static void M_HandleLoadSave(INT32 choice); static void M_HandleLevelStats(INT32 choice); static void M_HandlePlaystyleMenu(INT32 choice); -#ifndef NONET static boolean M_CancelConnect(void); static void M_HandleConnectIP(INT32 choice); -#endif static void M_HandleSetupMultiPlayer(INT32 choice); static void M_HandleVideoMode(INT32 choice); @@ -501,11 +491,7 @@ consvar_t cv_dummyloadless = CVAR_INIT ("dummyloadless", "In-game", CV_HIDEN, lo static menuitem_t MainMenu[] = { {IT_STRING|IT_CALL, NULL, "1 Player", M_SinglePlayerMenu, 76}, -#ifndef NONET {IT_STRING|IT_SUBMENU, NULL, "Multiplayer", &MP_MainDef, 84}, -#else - {IT_STRING|IT_CALL, NULL, "Multiplayer", M_StartSplitServerMenu, 84}, -#endif {IT_STRING|IT_CALL, NULL, "Extras", M_SecretsMenu, 92}, {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 100}, {IT_STRING|IT_CALL, NULL, "Options", M_Options, 108}, @@ -956,16 +942,10 @@ static menuitem_t SP_PlayerMenu[] = static menuitem_t MP_SplitServerMenu[] = { {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_MapChange, 100}, -#ifdef NONET // In order to keep player setup accessible. - {IT_STRING|IT_CALL, NULL, "Player 1 setup...", M_SetupMultiPlayer, 110}, - {IT_STRING|IT_CALL, NULL, "Player 2 setup...", M_SetupMultiPlayer2, 120}, -#endif {IT_STRING|IT_CALL, NULL, "More Options...", M_ServerOptions, 130}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 140}, }; -#ifndef NONET - static menuitem_t MP_MainMenu[] = { {IT_HEADER, NULL, "Join a game", NULL, 0}, @@ -1052,8 +1032,6 @@ menuitem_t MP_RoomMenu[] = {IT_DISABLED, NULL, "", M_ChooseRoom, 162}, }; -#endif - static menuitem_t MP_PlayerSetupMenu[] = { {IT_KEYHANDLER, NULL, "", M_HandleSetupMultiPlayer, 0}, // name @@ -1612,14 +1590,12 @@ enum static menuitem_t OP_ServerOptionsMenu[] = { {IT_HEADER, NULL, "General", NULL, 0}, -#ifndef NONET {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Server name", &cv_servername, 7}, {IT_STRING | IT_CVAR, NULL, "Max Players", &cv_maxplayers, 21}, {IT_STRING | IT_CVAR, NULL, "Allow Add-on Downloading", &cv_downloading, 26}, {IT_STRING | IT_CVAR, NULL, "Allow players to join", &cv_allownewplayer, 31}, {IT_STRING | IT_CVAR, NULL, "Minutes for reconnecting", &cv_rejointimeout, 36}, -#endif {IT_STRING | IT_CVAR, NULL, "Map progression", &cv_advancemap, 41}, {IT_STRING | IT_CVAR, NULL, "Intermission Timer", &cv_inttime, 46}, @@ -1658,7 +1634,6 @@ static menuitem_t OP_ServerOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Autobalance sizes", &cv_autobalance, 216}, {IT_STRING | IT_CVAR, NULL, "Scramble on Map Change", &cv_scrambleonchange, 221}, -#ifndef NONET {IT_HEADER, NULL, "Advanced", NULL, 230}, {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 236}, @@ -1666,7 +1641,6 @@ static menuitem_t OP_ServerOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 256}, {IT_STRING | IT_CVAR, NULL, "Show IP Address of Joiners", &cv_showjoinaddress, 261}, -#endif }; static menuitem_t OP_MonitorToggleMenu[] = @@ -1976,11 +1950,7 @@ menu_t MP_SplitServerDef = MTREE2(MN_MP_MAIN, MN_MP_SPLITSCREEN), "M_MULTI", sizeof (MP_SplitServerMenu)/sizeof (menuitem_t), -#ifndef NONET &MP_MainDef, -#else - &MainDef, -#endif MP_SplitServerMenu, M_DrawServerMenu, 27, 30 - 50, @@ -1988,8 +1958,6 @@ menu_t MP_SplitServerDef = NULL }; -#ifndef NONET - menu_t MP_MainDef = { MN_MP_MAIN, @@ -2041,15 +2009,10 @@ menu_t MP_RoomDef = 0, NULL }; -#endif menu_t MP_PlayerSetupDef = { -#ifdef NONET - MTREE2(MN_MP_MAIN, MN_MP_PLAYERSETUP), -#else MTREE3(MN_MP_MAIN, MN_MP_SPLITSCREEN, MN_MP_PLAYERSETUP), -#endif "M_SPLAYR", sizeof (MP_PlayerSetupMenu)/sizeof (menuitem_t), &MainDef, // doesn't matter @@ -3991,9 +3954,7 @@ void M_Init(void) OP_GamepadSetMenu[i].itemaction = M_AssignGamepad; } -#ifndef NONET CV_RegisterVar(&cv_serversort); -#endif } void M_InitCharacterTables(void) @@ -11006,7 +10967,6 @@ static void M_EndGame(INT32 choice) #define S_LINEY(n) currentMenu->y + SERVERHEADERHEIGHT + (n * SERVERLINEHEIGHT) -#ifndef NONET static UINT32 localservercount; static void M_HandleServerPage(INT32 choice) @@ -11278,11 +11238,9 @@ static int ServerListEntryComparator_modified(const void *entry1, const void *en // Default to strcmp. return strcmp(sa->info.servername, sb->info.servername); } -#endif void M_SortServerList(void) { -#ifndef NONET switch(cv_serversort.value) { case 0: // Ping. @@ -11304,10 +11262,8 @@ void M_SortServerList(void) qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename); break; } -#endif } -#ifndef NONET #ifdef UPDATE_ALERT static boolean M_CheckMODVersion(int id) { @@ -11506,7 +11462,6 @@ static void M_ChooseRoom(INT32 choice) if (currentMenu == &MP_ConnectDef) M_Refresh(0); } -#endif //NONET //=========================================================================== // Start Server Menu @@ -11554,7 +11509,6 @@ static void M_DrawServerMenu(void) { M_DrawGenericMenu(); -#ifndef NONET // Room name if (currentMenu == &MP_ServerDef) { @@ -11566,15 +11520,10 @@ static void M_DrawServerMenu(void) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, V_YELLOWMAP, room_list[menuRoomIndex].name); } -#endif if (cv_nextmap.value) { -#ifndef NONET #define imgheight MP_ServerMenu[mp_server_levelgt].alphaKey -#else -#define imgheight 100 -#endif patch_t *PictureOfLevel; lumpnum_t lumpnum; char headerstr[40]; @@ -11626,7 +11575,6 @@ static void M_ServerOptions(INT32 choice) { (void)choice; -#ifndef NONET if ((splitscreen && !netgame) || currentMenu == &MP_SplitServerDef) { OP_ServerOptionsMenu[ 1].status = IT_GRAYEDOUT; // Server name @@ -11647,7 +11595,6 @@ static void M_ServerOptions(INT32 choice) OP_ServerOptionsMenu[37].status = IT_STRING | IT_CVAR; OP_ServerOptionsMenu[38].status = IT_STRING | IT_CVAR; } -#endif /* Disable fading because of different menu head. */ if (currentMenu == &OP_MainDef)/* from Options menu */ @@ -11659,7 +11606,6 @@ static void M_ServerOptions(INT32 choice) M_SetupNextMenu(&OP_ServerOptionsDef); } -#ifndef NONET static void M_StartServerMenu(INT32 choice) { (void)choice; @@ -11870,7 +11816,6 @@ static void M_HandleConnectIP(INT32 choice) M_ClearMenus(true); } } -#endif //!NONET // ======================== // MULTIPLAYER PLAYER SETUP diff --git a/src/m_menu.h b/src/m_menu.h index 8d023811d..8eb183163 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -74,7 +74,7 @@ typedef enum MN_MP_SERVER, MN_MP_CONNECT, MN_MP_ROOM, - MN_MP_PLAYERSETUP, // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET + MN_MP_PLAYERSETUP, MN_MP_SERVER_OPTIONS, // Options diff --git a/src/mserv.c b/src/mserv.c index bff562c95..90091d241 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -45,9 +45,7 @@ static I_cond MSCond; # define Unlock_state() #endif/*HAVE_THREADS*/ -#ifndef NONET static void Command_Listserv_f(void); -#endif #endif/*MASTERSERVER*/ @@ -89,7 +87,6 @@ msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test */ void AddMServCommands(void) { -#ifndef NONET CV_RegisterVar(&cv_masterserver); CV_RegisterVar(&cv_masterserver_update_rate); CV_RegisterVar(&cv_masterserver_timeout); @@ -100,7 +97,6 @@ void AddMServCommands(void) COM_AddCommand("listserv", Command_Listserv_f); COM_AddCommand("masterserver_update", Update_parameters); // allows people to updates manually in case you were delisted by accident #endif -#endif } #ifdef MASTERSERVER @@ -189,7 +185,6 @@ void GetMODVersion_Console(void) } #endif -#ifndef NONET /** Gets a list of game servers. Called from console. */ static void Command_Listserv_f(void) @@ -200,7 +195,6 @@ static void Command_Listserv_f(void) HMS_list_servers(); } } -#endif static void Finish_registration (void) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index ee082fd5d..d08f3ff33 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1592,9 +1592,7 @@ void I_Quit(void) SDLforceUngrabMouse(); quiting = SDL_FALSE; M_SaveConfig(NULL); //save game config, cvars.. -#ifndef NONET D_SaveBan(); // save the ban list -#endif G_SaveGameData(); // Tails 12-08-2002 //added:16-02-98: when recording a demo, should exit using 'q' key, // but sometimes we forget and use 'F10'.. so save here too. @@ -1709,9 +1707,7 @@ void I_Error(const char *error, ...) // --- M_SaveConfig(NULL); // save game config, cvars.. -#ifndef NONET D_SaveBan(); // save the ban list -#endif G_SaveGameData(); // Tails 12-08-2002 // Shutdown. Here might be other errors. From 717e0d5a17ca22cfa56b6893c8b799dce1d568f3 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 31 Dec 2022 14:10:19 +0100 Subject: [PATCH 27/89] Move netcode files to a new folder --- src/Makefile | 1 + src/Sourcefile | 7 ---- src/android/i_net.c | 2 +- src/blua/liolib.c | 2 +- src/command.c | 4 +- src/d_main.c | 6 +-- src/deh_soc.c | 2 +- src/deh_soc.h | 2 +- src/doomstat.h | 2 +- src/dummy/i_net.c | 2 +- src/f_finale.c | 2 +- src/filesrch.c | 2 +- src/filesrch.h | 2 +- src/g_demo.c | 2 +- src/g_game.c | 2 +- src/g_input.c | 2 +- src/hardware/hw_main.c | 6 +-- src/hu_stuff.c | 2 +- src/i_time.c | 2 +- src/lua_baselib.c | 4 +- src/lua_hooklib.c | 2 +- src/lua_script.c | 2 +- src/m_cheat.c | 2 +- src/m_menu.c | 6 +-- src/m_menu.h | 2 +- src/m_perfstats.c | 2 +- src/netcode/Sourcefile | 7 ++++ src/{ => netcode}/d_clisrv.c | 68 +++++++++++++++++----------------- src/{ => netcode}/d_clisrv.h | 6 +-- src/{ => netcode}/d_net.c | 16 ++++---- src/{ => netcode}/d_net.h | 0 src/{ => netcode}/d_netcmd.c | 66 ++++++++++++++++----------------- src/{ => netcode}/d_netcmd.h | 2 +- src/{ => netcode}/d_netfil.c | 30 +++++++-------- src/{ => netcode}/d_netfil.h | 2 +- src/{ => netcode}/http-mserv.c | 10 ++--- src/{ => netcode}/i_addrinfo.c | 0 src/{ => netcode}/i_addrinfo.h | 0 src/{ => netcode}/i_net.h | 4 +- src/{ => netcode}/i_tcp.c | 8 ++-- src/{ => netcode}/i_tcp.h | 0 src/{ => netcode}/mserv.c | 12 +++--- src/{ => netcode}/mserv.h | 2 +- src/p_ceilng.c | 2 +- src/p_haptic.c | 2 +- src/p_lights.c | 2 +- src/p_user.c | 2 +- src/r_segs.c | 2 +- src/r_things.c | 2 +- src/screen.c | 2 +- src/sdl/i_gamepad.c | 2 +- src/sdl/i_net.c | 6 +-- src/sdl/i_system.c | 4 +- src/sdl/i_ttf.c | 2 +- src/w_wad.c | 4 +- src/y_inter.c | 2 +- 56 files changed, 170 insertions(+), 169 deletions(-) create mode 100644 src/netcode/Sourcefile rename src/{ => netcode}/d_clisrv.c (99%) mode change 100755 => 100644 rename src/{ => netcode}/d_clisrv.h (99%) rename src/{ => netcode}/d_net.c (99%) rename src/{ => netcode}/d_net.h (100%) rename src/{ => netcode}/d_netcmd.c (99%) rename src/{ => netcode}/d_netcmd.h (99%) rename src/{ => netcode}/d_netfil.c (99%) rename src/{ => netcode}/d_netfil.h (99%) rename src/{ => netcode}/http-mserv.c (98%) rename src/{ => netcode}/i_addrinfo.c (100%) rename src/{ => netcode}/i_addrinfo.h (100%) rename src/{ => netcode}/i_net.h (98%) rename src/{ => netcode}/i_tcp.c (99%) rename src/{ => netcode}/i_tcp.h (100%) rename src/{ => netcode}/mserv.c (98%) rename src/{ => netcode}/mserv.h (99%) diff --git a/src/Makefile b/src/Makefile index cec5dc88f..750e8ead5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -207,6 +207,7 @@ objdir:=$(makedir)/objs sources+=\ $(call List,Sourcefile)\ $(call List,blua/Sourcefile)\ + $(call List,netcode/Sourcefile)\ depends:=$(basename $(filter %.c %.s,$(sources))) objects:=$(basename $(filter %.c %.s %.nas,$(sources))) diff --git a/src/Sourcefile b/src/Sourcefile index 83e9ebc3b..7d2146411 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -1,9 +1,5 @@ string.c d_main.c -d_clisrv.c -d_net.c -d_netfil.c -d_netcmd.c dehacked.c deh_soc.c deh_lua.c @@ -77,9 +73,6 @@ s_sound.c sounds.c w_wad.c filesrch.c -mserv.c -http-mserv.c -i_tcp.c lzf.c vid_copy.s b_bot.c diff --git a/src/android/i_net.c b/src/android/i_net.c index f6e642022..4c30dc767 100644 --- a/src/android/i_net.c +++ b/src/android/i_net.c @@ -1,4 +1,4 @@ -#include "../i_net.h" +#include "../netcode/i_net.h" boolean I_InitNetwork(void) { diff --git a/src/blua/liolib.c b/src/blua/liolib.c index e029650c0..8a6354121 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -19,7 +19,7 @@ #include "lualib.h" #include "../i_system.h" #include "../g_game.h" -#include "../d_netfil.h" +#include "../netcode/d_netfil.h" #include "../lua_libs.h" #include "../byteptr.h" #include "../lua_script.h" diff --git a/src/command.c b/src/command.c index 9be081fb7..4e8989fd3 100644 --- a/src/command.c +++ b/src/command.c @@ -28,11 +28,11 @@ #include "byteptr.h" #include "p_saveg.h" #include "g_game.h" // for player_names -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" #include "hu_stuff.h" #include "p_setup.h" #include "lua_script.h" -#include "d_netfil.h" // findfile +#include "netcode/d_netfil.h" // findfile #include "r_data.h" // Color_cons_t //======== diff --git a/src/d_main.c b/src/d_main.c index 1af8d090c..8b13dba89 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -34,7 +34,7 @@ #include "doomdef.h" #include "am_map.h" #include "console.h" -#include "d_net.h" +#include "netcode/d_net.h" #include "f_finale.h" #include "g_game.h" #include "hu_stuff.h" @@ -57,11 +57,11 @@ #include "w_wad.h" #include "z_zone.h" #include "d_main.h" -#include "d_netfil.h" +#include "netcode/d_netfil.h" #include "m_cheat.h" #include "y_inter.h" #include "p_local.h" // chasecam -#include "mserv.h" // ms_RoomId +#include "netcode/mserv.h" // ms_RoomId #include "m_misc.h" // screenshot functionality #include "deh_tables.h" // Dehacked list test #include "m_cond.h" // condition initialization diff --git a/src/deh_soc.c b/src/deh_soc.c index 583776ee7..f13dd53a2 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -34,7 +34,7 @@ #include "r_sky.h" #include "fastcmp.h" #include "lua_script.h" // Reluctantly included for LUA_EvalMath -#include "d_clisrv.h" +#include "netcode/d_clisrv.h" #ifdef HWRENDER #include "hardware/hw_light.h" diff --git a/src/deh_soc.h b/src/deh_soc.h index f972ec26e..cd55b665b 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -35,7 +35,7 @@ #include "r_sky.h" #include "fastcmp.h" #include "lua_script.h" // Reluctantly included for LUA_EvalMath -#include "d_clisrv.h" +#include "netcode/d_clisrv.h" #ifdef HWRENDER #include "hardware/hw_light.h" diff --git a/src/doomstat.h b/src/doomstat.h index bce43416b..344a3daa4 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -683,7 +683,7 @@ extern boolean singletics; // Netgame stuff // ============= -#include "d_clisrv.h" +#include "netcode/d_clisrv.h" extern consvar_t cv_timetic; // display high resolution timer extern consvar_t cv_powerupdisplay; // display powerups diff --git a/src/dummy/i_net.c b/src/dummy/i_net.c index f6e642022..4c30dc767 100644 --- a/src/dummy/i_net.c +++ b/src/dummy/i_net.c @@ -1,4 +1,4 @@ -#include "../i_net.h" +#include "../netcode/i_net.h" boolean I_InitNetwork(void) { diff --git a/src/f_finale.c b/src/f_finale.c index 307e00aaa..2122cf34d 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -14,7 +14,7 @@ #include "doomdef.h" #include "doomstat.h" #include "d_main.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" #include "f_finale.h" #include "g_game.h" #include "hu_stuff.h" diff --git a/src/filesrch.c b/src/filesrch.c index 23b8e7f40..d68a7d5ed 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -26,7 +26,7 @@ #include #include "filesrch.h" -#include "d_netfil.h" +#include "netcode/d_netfil.h" #include "m_misc.h" #include "z_zone.h" #include "m_menu.h" // Addons_option_Onchange diff --git a/src/filesrch.h b/src/filesrch.h index 59ef5269b..a934c48d6 100644 --- a/src/filesrch.h +++ b/src/filesrch.h @@ -5,7 +5,7 @@ #define __FILESRCH_H__ #include "doomdef.h" -#include "d_netfil.h" +#include "netcode/d_netfil.h" #include "m_menu.h" // MAXSTRINGLENGTH #include "w_wad.h" diff --git a/src/g_demo.c b/src/g_demo.c index 9099adc71..6167b3a09 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -15,7 +15,7 @@ #include "console.h" #include "d_main.h" #include "d_player.h" -#include "d_clisrv.h" +#include "netcode/d_clisrv.h" #include "p_setup.h" #include "i_time.h" #include "i_system.h" diff --git a/src/g_game.c b/src/g_game.c index b4a127a73..93b8eb936 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -15,7 +15,7 @@ #include "console.h" #include "d_main.h" #include "d_player.h" -#include "d_clisrv.h" +#include "netcode/d_clisrv.h" #include "f_finale.h" #include "p_setup.h" #include "p_saveg.h" diff --git a/src/g_input.c b/src/g_input.c index 465db0316..5ae5ae0cf 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -18,7 +18,7 @@ #include "i_gamepad.h" #include "keys.h" #include "hu_stuff.h" // need HUFONT start & end -#include "d_net.h" +#include "netcode/d_net.h" #include "console.h" #define MAXMOUSESENSITIVITY 100 // sensitivity steps diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 3cb7275a0..191ee68bb 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -29,7 +29,7 @@ #include "../r_patch.h" #include "../r_picformats.h" #include "../r_bsp.h" -#include "../d_clisrv.h" +#include "../netcode/d_clisrv.h" #include "../w_wad.h" #include "../z_zone.h" #include "../r_splats.h" @@ -5234,7 +5234,7 @@ static void HWR_ProjectSprite(mobj_t *thing) { interpmobjstate_t casterinterp = {}; fixed_t groundz; - fixed_t floordiff; + fixed_t floordiff; if (R_UsingFrameInterpolation() && !paused) { @@ -5244,7 +5244,7 @@ static void HWR_ProjectSprite(mobj_t *thing) { R_InterpolateMobjState(caster, FRACUNIT, &casterinterp); } - + groundz = R_GetShadowZ(thing, NULL); floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + casterinterp.z - groundz); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 37c3f0dc3..e5cf5aeb1 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -19,7 +19,7 @@ #include "m_cond.h" // emblems #include "m_misc.h" // word jumping -#include "d_clisrv.h" +#include "netcode/d_clisrv.h" #include "g_game.h" #include "g_input.h" diff --git a/src/i_time.c b/src/i_time.c index c1cc9dfd4..9303d7be7 100644 --- a/src/i_time.c +++ b/src/i_time.c @@ -17,7 +17,7 @@ #include "command.h" #include "doomtype.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" #include "m_fixed.h" #include "i_system.h" diff --git a/src/lua_baselib.c b/src/lua_baselib.c index c94e9e91e..ea519be55 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -27,11 +27,11 @@ #include "y_inter.h" #include "hu_stuff.h" // HU_AddChatText #include "console.h" -#include "d_netcmd.h" // IsPlayerAdmin +#include "netcode/d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff #include "m_misc.h" // M_MapNumber #include "b_bot.h" // B_UpdateBotleader -#include "d_clisrv.h" // CL_RemovePlayer +#include "netcode/d_clisrv.h" // CL_RemovePlayer #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "lua_script.h" diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 6506d3dc6..e8e6ebea9 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -25,7 +25,7 @@ #include "lua_hud.h" // hud_running errors #include "m_perfstats.h" -#include "d_netcmd.h" // for cv_perfstats +#include "netcode/d_netcmd.h" // for cv_perfstats #include "i_system.h" // I_GetPreciseTime /* ========================================================================= diff --git a/src/lua_script.c b/src/lua_script.c index 8d8fb295c..f1c800df5 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -28,7 +28,7 @@ #include "p_slopes.h" // for P_SlopeById and slopelist #include "p_polyobj.h" // polyobj_t, PolyObjects #ifdef LUA_ALLOW_BYTECODE -#include "d_netfil.h" // for LUA_DumpFile +#include "netcode/d_netfil.h" // for LUA_DumpFile #endif #include "lua_script.h" diff --git a/src/m_cheat.c b/src/m_cheat.c index f94377450..36b2d1907 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -19,7 +19,7 @@ #include "r_local.h" #include "p_local.h" #include "p_setup.h" -#include "d_net.h" +#include "netcode/d_net.h" #include "m_cheat.h" #include "m_menu.h" diff --git a/src/m_menu.c b/src/m_menu.c index 824ae5c46..d3ae4da2a 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -20,7 +20,7 @@ #include "doomdef.h" #include "d_main.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" #include "console.h" #include "r_fps.h" #include "r_local.h" @@ -55,8 +55,8 @@ #include "hardware/hw_main.h" #endif -#include "d_net.h" -#include "mserv.h" +#include "netcode/d_net.h" +#include "netcode/mserv.h" #include "m_misc.h" #include "m_anigif.h" #include "byteptr.h" diff --git a/src/m_menu.h b/src/m_menu.h index 8eb183163..809186a17 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -20,7 +20,7 @@ #include "command.h" #include "f_finale.h" // for ttmode_enum #include "i_threads.h" -#include "mserv.h" +#include "netcode/mserv.h" #include "r_things.h" // for SKINNAMESIZE // Compatibility with old-style named NiGHTS replay files. diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 9f65a7616..cca4c62ca 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -12,7 +12,7 @@ #include "m_perfstats.h" #include "v_video.h" #include "i_video.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" #include "r_main.h" #include "i_system.h" #include "z_zone.h" diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile new file mode 100644 index 000000000..9087917d0 --- /dev/null +++ b/src/netcode/Sourcefile @@ -0,0 +1,7 @@ +d_clisrv.c +d_net.c +d_netcmd.c +d_netfil.c +http-mserv.c +i_tcp.c +mserv.c diff --git a/src/d_clisrv.c b/src/netcode/d_clisrv.c old mode 100755 new mode 100644 similarity index 99% rename from src/d_clisrv.c rename to src/netcode/d_clisrv.c index cb1ccd539..9b11e23ff --- a/src/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -15,48 +15,48 @@ #include //for unlink #endif -#include "i_time.h" +#include "../i_time.h" #include "i_net.h" -#include "i_system.h" -#include "i_video.h" +#include "../i_system.h" +#include "../i_video.h" #include "d_net.h" -#include "d_main.h" -#include "g_game.h" -#include "st_stuff.h" -#include "hu_stuff.h" -#include "keys.h" -#include "g_input.h" -#include "i_gamepad.h" -#include "m_menu.h" -#include "console.h" +#include "../d_main.h" +#include "../g_game.h" +#include "../st_stuff.h" +#include "../hu_stuff.h" +#include "../keys.h" +#include "../g_input.h" +#include "../i_gamepad.h" +#include "../m_menu.h" +#include "../console.h" #include "d_netfil.h" -#include "byteptr.h" -#include "p_saveg.h" -#include "z_zone.h" -#include "p_local.h" -#include "p_haptic.h" -#include "m_misc.h" -#include "am_map.h" -#include "m_random.h" +#include "../byteptr.h" +#include "../p_saveg.h" +#include "../z_zone.h" +#include "../p_local.h" +#include "../p_haptic.h" +#include "../m_misc.h" +#include "../am_map.h" +#include "../m_random.h" #include "mserv.h" -#include "y_inter.h" -#include "r_local.h" -#include "m_argv.h" -#include "p_setup.h" -#include "lzf.h" -#include "lua_script.h" -#include "lua_hook.h" -#include "lua_libs.h" -#include "md5.h" -#include "m_perfstats.h" +#include "../y_inter.h" +#include "../r_local.h" +#include "../m_argv.h" +#include "../p_setup.h" +#include "../lzf.h" +#include "../lua_script.h" +#include "../lua_hook.h" +#include "../lua_libs.h" +#include "../md5.h" +#include "../m_perfstats.h" // aaaaaa -#include "i_gamepad.h" +#include "../i_gamepad.h" // cl loading screen -#include "v_video.h" -#include "f_finale.h" -#include "snake.h" +#include "../v_video.h" +#include "../f_finale.h" +#include "../snake.h" // // NETWORKING diff --git a/src/d_clisrv.h b/src/netcode/d_clisrv.h similarity index 99% rename from src/d_clisrv.h rename to src/netcode/d_clisrv.h index 0d6add13a..50b86e9f0 100644 --- a/src/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -13,12 +13,12 @@ #ifndef __D_CLISRV__ #define __D_CLISRV__ -#include "d_ticcmd.h" +#include "../d_ticcmd.h" #include "d_net.h" #include "d_netcmd.h" #include "d_net.h" -#include "tables.h" -#include "d_player.h" +#include "../tables.h" +#include "../d_player.h" #include "mserv.h" /* diff --git a/src/d_net.c b/src/netcode/d_net.c similarity index 99% rename from src/d_net.c rename to src/netcode/d_net.c index 9e3759d32..ae0605001 100644 --- a/src/d_net.c +++ b/src/netcode/d_net.c @@ -16,19 +16,19 @@ /// This protocol uses a mix of "goback n" and "selective repeat" implementation /// The NOTHING packet is sent when connection is idle to acknowledge packets -#include "doomdef.h" -#include "g_game.h" -#include "i_time.h" +#include "../doomdef.h" +#include "../g_game.h" +#include "../i_time.h" #include "i_net.h" -#include "i_system.h" -#include "m_argv.h" +#include "../i_system.h" +#include "../m_argv.h" #include "d_net.h" -#include "w_wad.h" +#include "../w_wad.h" #include "d_netfil.h" #include "d_clisrv.h" -#include "z_zone.h" +#include "../z_zone.h" #include "i_tcp.h" -#include "d_main.h" // srb2home +#include "../d_main.h" // srb2home // // NETWORKING diff --git a/src/d_net.h b/src/netcode/d_net.h similarity index 100% rename from src/d_net.h rename to src/netcode/d_net.h diff --git a/src/d_netcmd.c b/src/netcode/d_netcmd.c similarity index 99% rename from src/d_netcmd.c rename to src/netcode/d_netcmd.c index 69eed90de..ed310805d 100644 --- a/src/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -12,44 +12,44 @@ /// commands are executed through the command buffer /// like console commands, other miscellaneous commands (at the end) -#include "doomdef.h" +#include "../doomdef.h" -#include "console.h" -#include "command.h" -#include "i_time.h" -#include "i_system.h" -#include "g_game.h" -#include "hu_stuff.h" -#include "g_input.h" -#include "i_gamepad.h" -#include "m_menu.h" -#include "r_local.h" -#include "r_skins.h" -#include "p_local.h" -#include "p_setup.h" -#include "s_sound.h" -#include "i_sound.h" -#include "m_misc.h" -#include "am_map.h" -#include "byteptr.h" +#include "../console.h" +#include "../command.h" +#include "../i_time.h" +#include "../i_system.h" +#include "../g_game.h" +#include "../hu_stuff.h" +#include "../g_input.h" +#include "../i_gamepad.h" +#include "../m_menu.h" +#include "../r_local.h" +#include "../r_skins.h" +#include "../p_local.h" +#include "../p_setup.h" +#include "../s_sound.h" +#include "../i_sound.h" +#include "../m_misc.h" +#include "../am_map.h" +#include "../byteptr.h" #include "d_netfil.h" -#include "p_spec.h" -#include "m_cheat.h" +#include "../p_spec.h" +#include "../m_cheat.h" #include "d_clisrv.h" #include "d_net.h" -#include "v_video.h" -#include "d_main.h" -#include "m_random.h" -#include "f_finale.h" -#include "filesrch.h" +#include "../v_video.h" +#include "../d_main.h" +#include "../m_random.h" +#include "../f_finale.h" +#include "../filesrch.h" #include "mserv.h" -#include "z_zone.h" -#include "lua_script.h" -#include "lua_hook.h" -#include "m_cond.h" -#include "m_anigif.h" -#include "md5.h" -#include "m_perfstats.h" +#include "../z_zone.h" +#include "../lua_script.h" +#include "../lua_hook.h" +#include "../m_cond.h" +#include "../m_anigif.h" +#include "../md5.h" +#include "../m_perfstats.h" #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR diff --git a/src/d_netcmd.h b/src/netcode/d_netcmd.h similarity index 99% rename from src/d_netcmd.h rename to src/netcode/d_netcmd.h index 47f68a17e..797a686a7 100644 --- a/src/d_netcmd.h +++ b/src/netcode/d_netcmd.h @@ -15,7 +15,7 @@ #ifndef __D_NETCMD__ #define __D_NETCMD__ -#include "command.h" +#include "../command.h" // console vars extern consvar_t cv_playername; diff --git a/src/d_netfil.c b/src/netcode/d_netfil.c similarity index 99% rename from src/d_netfil.c rename to src/netcode/d_netfil.c index bf3952cc9..80fa06852 100644 --- a/src/d_netfil.c +++ b/src/netcode/d_netfil.c @@ -31,24 +31,24 @@ #include #endif -#include "doomdef.h" -#include "doomstat.h" -#include "d_main.h" -#include "g_game.h" -#include "i_time.h" +#include "../doomdef.h" +#include "../doomstat.h" +#include "../d_main.h" +#include "../g_game.h" +#include "../i_time.h" #include "i_net.h" -#include "i_system.h" -#include "m_argv.h" +#include "../i_system.h" +#include "../m_argv.h" #include "d_net.h" -#include "w_wad.h" +#include "../w_wad.h" #include "d_netfil.h" -#include "z_zone.h" -#include "byteptr.h" -#include "p_setup.h" -#include "m_misc.h" -#include "m_menu.h" -#include "md5.h" -#include "filesrch.h" +#include "../z_zone.h" +#include "../byteptr.h" +#include "../p_setup.h" +#include "../m_misc.h" +#include "../m_menu.h" +#include "../md5.h" +#include "../filesrch.h" #include diff --git a/src/d_netfil.h b/src/netcode/d_netfil.h similarity index 99% rename from src/d_netfil.h rename to src/netcode/d_netfil.h index ec53be587..732efcd5e 100644 --- a/src/d_netfil.h +++ b/src/netcode/d_netfil.h @@ -15,7 +15,7 @@ #include "d_net.h" #include "d_clisrv.h" -#include "w_wad.h" +#include "../w_wad.h" typedef enum { diff --git a/src/http-mserv.c b/src/netcode/http-mserv.c similarity index 98% rename from src/http-mserv.c rename to src/netcode/http-mserv.c index b0ef37fa1..72dc1cafb 100644 --- a/src/http-mserv.c +++ b/src/netcode/http-mserv.c @@ -18,14 +18,14 @@ Documentation available here. #include #endif -#include "doomdef.h" +#include "../doomdef.h" #include "d_clisrv.h" -#include "command.h" -#include "m_argv.h" -#include "m_menu.h" +#include "../command.h" +#include "../m_argv.h" +#include "../m_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ -#include "i_threads.h" +#include "../i_threads.h" /* reasonable default I guess?? */ #define DEFAULT_BUFFER_SIZE (4096) diff --git a/src/i_addrinfo.c b/src/netcode/i_addrinfo.c similarity index 100% rename from src/i_addrinfo.c rename to src/netcode/i_addrinfo.c diff --git a/src/i_addrinfo.h b/src/netcode/i_addrinfo.h similarity index 100% rename from src/i_addrinfo.h rename to src/netcode/i_addrinfo.h diff --git a/src/i_net.h b/src/netcode/i_net.h similarity index 98% rename from src/i_net.h rename to src/netcode/i_net.h index 62b7528d5..66126d050 100644 --- a/src/i_net.h +++ b/src/netcode/i_net.h @@ -18,8 +18,8 @@ #pragma interface #endif -#include "doomdef.h" -#include "command.h" +#include "../doomdef.h" +#include "../command.h" /// \brief program net id #define DOOMCOM_ID (INT32)0x12345678l diff --git a/src/i_tcp.c b/src/netcode/i_tcp.c similarity index 99% rename from src/i_tcp.c rename to src/netcode/i_tcp.c index ec3d1ae93..6baba6275 100644 --- a/src/i_tcp.c +++ b/src/netcode/i_tcp.c @@ -36,7 +36,7 @@ #include #endif -#include "doomdef.h" +#include "../doomdef.h" #ifdef USE_WINSOCK1 #include @@ -122,14 +122,14 @@ typedef union #define MAXBANS 100 -#include "i_system.h" +#include "../i_system.h" #include "i_net.h" #include "d_net.h" #include "d_netfil.h" #include "i_tcp.h" -#include "m_argv.h" +#include "../m_argv.h" -#include "doomstat.h" +#include "../doomstat.h" // win32 #ifdef USE_WINSOCK diff --git a/src/i_tcp.h b/src/netcode/i_tcp.h similarity index 100% rename from src/i_tcp.h rename to src/netcode/i_tcp.h diff --git a/src/mserv.c b/src/netcode/mserv.c similarity index 98% rename from src/mserv.c rename to src/netcode/mserv.c index 90091d241..78301f4d9 100644 --- a/src/mserv.c +++ b/src/netcode/mserv.c @@ -15,13 +15,13 @@ #include #endif -#include "doomstat.h" -#include "doomdef.h" -#include "command.h" -#include "i_threads.h" +#include "../doomstat.h" +#include "../doomdef.h" +#include "../command.h" +#include "../i_threads.h" #include "mserv.h" -#include "m_menu.h" -#include "z_zone.h" +#include "../m_menu.h" +#include "../z_zone.h" #ifdef MASTERSERVER diff --git a/src/mserv.h b/src/netcode/mserv.h similarity index 99% rename from src/mserv.h rename to src/netcode/mserv.h index 23b26fbc5..7fdf3ed1b 100644 --- a/src/mserv.h +++ b/src/netcode/mserv.h @@ -14,7 +14,7 @@ #ifndef _MSERV_H_ #define _MSERV_H_ -#include "i_threads.h" +#include "../i_threads.h" // lowered from 32 due to menu changes #define NUM_LIST_ROOMS 16 diff --git a/src/p_ceilng.c b/src/p_ceilng.c index 66f2dd58e..949eeddf2 100644 --- a/src/p_ceilng.c +++ b/src/p_ceilng.c @@ -17,7 +17,7 @@ #include "r_main.h" #include "s_sound.h" #include "z_zone.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" // ========================================================================== // CEILINGS diff --git a/src/p_haptic.c b/src/p_haptic.c index dbfa58737..4d22fbd77 100644 --- a/src/p_haptic.c +++ b/src/p_haptic.c @@ -11,7 +11,7 @@ #include "p_haptic.h" #include "g_game.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" #include "i_gamepad.h" #include "doomstat.h" diff --git a/src/p_lights.c b/src/p_lights.c index 4c783f884..a1f4de853 100644 --- a/src/p_lights.c +++ b/src/p_lights.c @@ -17,7 +17,7 @@ #include "r_state.h" #include "z_zone.h" #include "m_random.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" /** Removes any active lighting effects in a sector. * diff --git a/src/p_user.c b/src/p_user.c index 6fd24eb13..1e8dee885 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -18,7 +18,7 @@ #include "doomdef.h" #include "i_system.h" #include "d_event.h" -#include "d_net.h" +#include "netcode/d_net.h" #include "g_game.h" #include "p_local.h" #include "r_fps.h" diff --git a/src/r_segs.c b/src/r_segs.c index 43a7f945f..704f33334 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -20,7 +20,7 @@ #include "w_wad.h" #include "z_zone.h" -#include "d_netcmd.h" +#include "netcode/d_netcmd.h" #include "m_misc.h" #include "p_local.h" // Camera... #include "p_slopes.h" diff --git a/src/r_things.c b/src/r_things.c index fed873fd6..a466def70 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -34,7 +34,7 @@ #include "p_tick.h" #include "p_local.h" #include "p_slopes.h" -#include "d_netfil.h" // blargh. for nameonly(). +#include "netcode/d_netfil.h" // blargh. for nameonly(). #include "m_cheat.h" // objectplace #ifdef HWRENDER #include "hardware/hw_md2.h" diff --git a/src/screen.c b/src/screen.c index e984f9ee5..00895f013 100644 --- a/src/screen.c +++ b/src/screen.c @@ -27,7 +27,7 @@ #include "hu_stuff.h" #include "z_zone.h" #include "d_main.h" -#include "d_clisrv.h" +#include "netcode/d_clisrv.h" #include "f_finale.h" #include "y_inter.h" // usebuffer #include "i_sound.h" // closed captions diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index ecde251fb..f27504f76 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -15,7 +15,7 @@ #include "../i_system.h" #include "../doomdef.h" #include "../d_main.h" -#include "../d_netcmd.h" +#include "../netcode/d_netcmd.h" #include "../g_game.h" #include "../m_argv.h" #include "../m_menu.h" diff --git a/src/sdl/i_net.c b/src/sdl/i_net.c index ee4a34c13..515a85568 100644 --- a/src/sdl/i_net.c +++ b/src/sdl/i_net.c @@ -21,16 +21,16 @@ #include "../i_system.h" #include "../d_event.h" -#include "../d_net.h" +#include "../netcode/d_net.h" #include "../m_argv.h" #include "../doomstat.h" -#include "../i_net.h" +#include "../netcode/i_net.h" #include "../z_zone.h" -#include "../i_tcp.h" +#include "../netcode/i_tcp.h" #ifdef HAVE_SDL diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index d08f3ff33..7684045fc 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -188,7 +188,7 @@ static char returnWadPath[256]; #include "../i_gamepad.h" #include "../i_threads.h" #include "../screen.h" //vid.WndParent -#include "../d_net.h" +#include "../netcode/d_net.h" #include "../g_game.h" #include "../filesrch.h" #include "endtxt.h" @@ -207,7 +207,7 @@ static char returnWadPath[256]; #if !defined(NOMUMBLE) && defined(HAVE_MUMBLE) // Mumble context string -#include "../d_clisrv.h" +#include "../netcode/d_clisrv.h" #include "../byteptr.h" #endif diff --git a/src/sdl/i_ttf.c b/src/sdl/i_ttf.c index f2cd497ee..1f838e9b4 100644 --- a/src/sdl/i_ttf.c +++ b/src/sdl/i_ttf.c @@ -21,7 +21,7 @@ #include "SDL_ttf.h" #include "../doomdef.h" #include "../doomstat.h" -#include "../d_netfil.h" +#include "../netcode/d_netfil.h" #include "../filesrch.h" #include "i_ttf.h" diff --git a/src/w_wad.c b/src/w_wad.c index 42c6bf83b..1e64cf093 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -51,8 +51,8 @@ #include "filesrch.h" #include "d_main.h" -#include "d_netfil.h" -#include "d_clisrv.h" +#include "netcode/d_netfil.h" +#include "netcode/d_clisrv.h" #include "dehacked.h" #include "r_defs.h" #include "r_data.h" diff --git a/src/y_inter.c b/src/y_inter.c index 7faceff50..de757ec70 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -15,7 +15,7 @@ #include "f_finale.h" #include "g_game.h" #include "hu_stuff.h" -#include "i_net.h" +#include "netcode/i_net.h" #include "i_video.h" #include "p_tick.h" #include "r_defs.h" From 4660d3cab62567f20763b3be90a64c2a2dde178a Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 31 Dec 2022 21:37:35 +0100 Subject: [PATCH 28/89] Move client connection handling to a new file --- src/m_menu.c | 1 + src/netcode/Sourcefile | 1 + src/netcode/client_connection.c | 1181 ++++++++++++++++++++++++++++++ src/netcode/client_connection.h | 61 ++ src/netcode/d_clisrv.c | 1184 +------------------------------ src/netcode/d_clisrv.h | 16 +- src/netcode/http-mserv.c | 1 + src/netcode/mserv.c | 1 + 8 files changed, 1256 insertions(+), 1190 deletions(-) create mode 100644 src/netcode/client_connection.c create mode 100644 src/netcode/client_connection.h diff --git a/src/m_menu.c b/src/m_menu.c index d3ae4da2a..06d1b1b96 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -57,6 +57,7 @@ #include "netcode/d_net.h" #include "netcode/mserv.h" +#include "netcode/client_connection.h" #include "m_misc.h" #include "m_anigif.h" #include "byteptr.h" diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile index 9087917d0..c11b3d6c1 100644 --- a/src/netcode/Sourcefile +++ b/src/netcode/Sourcefile @@ -1,4 +1,5 @@ d_clisrv.c +client_connection.c d_net.c d_netcmd.c d_netfil.c diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c new file mode 100644 index 000000000..bfaea2e81 --- /dev/null +++ b/src/netcode/client_connection.c @@ -0,0 +1,1181 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 client_connection.h +/// \brief Client connection handling + +#include "client_connection.h" +#include "d_clisrv.h" +#include "d_netfil.h" +#include "../d_main.h" +#include "../f_finale.h" +#include "../g_game.h" +#include "../i_gamepad.h" +#include "i_net.h" +#include "../i_system.h" +#include "../i_time.h" +#include "../i_video.h" +#include "../m_menu.h" +#include "../m_misc.h" +#include "../snake.h" +#include "../s_sound.h" +#include "../v_video.h" +#include "../y_inter.h" +#include "../z_zone.h" +#include "../doomtype.h" +#include "../doomstat.h" + +cl_mode_t cl_mode = CL_SEARCHING; +static UINT16 cl_lastcheckedfilecount = 0; // used for full file list +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; +UINT8 mynode; // my address pointofview server +static void *snake = NULL; + +static void CL_DrawConnectionStatusBox(void) +{ + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); + if (cl_mode != CL_CONFIRMCONNECT) + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); +} + +// +// CL_DrawConnectionStatus +// +// Keep the local client informed of our status. +// +static inline void CL_DrawConnectionStatus(void) +{ + INT32 ccstime = I_GetTime(); + + // Draw background fade + V_DrawFadeScreen(0xFF00, 16); // force default + + if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES) + { + INT32 i, animtime = ((ccstime / 4) & 15) + 16; + UINT8 palstart; + const char *cltext; + + // Draw the bottom box. + CL_DrawConnectionStatusBox(); + + if (cl_mode == CL_SEARCHING) + palstart = 32; // Red + else if (cl_mode == CL_CONFIRMCONNECT) + palstart = 48; // Orange + else + palstart = 96; // Green + + if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) + for (i = 0; i < 16; ++i) // 15 pal entries total. + V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); + + switch (cl_mode) + { + case CL_DOWNLOADSAVEGAME: + if (fileneeded && lastfilenum != -1) + { + UINT32 currentsize = fileneeded[lastfilenum].currentsize; + UINT32 totalsize = fileneeded[lastfilenum].totalsize; + INT32 dldlength; + + cltext = M_GetText("Downloading game state..."); + Net_GetNetStat(); + + dldlength = (INT32)((currentsize/(double)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_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va(" %4uK/%4uK",currentsize>>10,totalsize>>10)); + + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + cltext = M_GetText("Waiting to download game state..."); + break; + case CL_ASKFULLFILELIST: + 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: + if (serverisfull) + cltext = M_GetText("Server full, waiting for a slot..."); + else + cltext = M_GetText("Requesting to join..."); + break; + default: + cltext = M_GetText("Connecting to server..."); + break; + } + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext); + } + else + { + if (cl_mode == CL_LOADFILES) + { + INT32 totalfileslength; + INT32 loadcompletednum = 0; + INT32 i; + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); + + //ima just count files here + if (fileneeded) + { + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_OPEN) + loadcompletednum++; + } + + // Loading progress + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, "Loading server addons..."); + totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256); + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va(" %2u/%2u Files",loadcompletednum,fileneedednum)); + } + else if (lastfilenum != -1) + { + INT32 dldlength; + static char tempname[28]; + fileneeded_t *file; + char *filename; + + if (snake) + Snake_Draw(snake); + + // Draw the bottom box. + CL_DrawConnectionStatusBox(); + + if (fileneeded) + { + file = &fileneeded[lastfilenum]; + filename = file->filename; + } + else + return; + + 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); + + memset(tempname, 0, sizeof(tempname)); + // offset filename to just the name only part + filename += strlen(filename) - nameonlylength(filename); + + if (strlen(filename) > sizeof(tempname)-1) // too long to display fully + { + size_t endhalfpos = strlen(filename)-10; + // display as first 14 chars + ... + last 10 chars + // which should add up to 27 if our math(s) is correct + snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos); + } + else // we can copy the whole thing in safely + { + strncpy(tempname, filename, sizeof(tempname)-1); + } + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, + va(M_GetText("Downloading \"%s\""), tempname)); + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 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, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + { + if (snake) + Snake_Draw(snake); + + CL_DrawConnectionStatusBox(); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, + M_GetText("Waiting to download files...")); + } + } +} + +static boolean CL_AskFileList(INT32 firstfile) +{ + netbuffer->packettype = PT_TELLFILESNEEDED; + netbuffer->u.filesneedednum = firstfile; + + return HSendPacket(servernode, false, 0, sizeof (INT32)); +} + +/** Sends a special packet to declare how many players in local + * Used only in arbitratrenetstart() + * Sends a PT_CLIENTJOIN packet to the server + * + * \return True if the packet was successfully sent + * \todo Improve the description... + * + */ +boolean CL_SendJoin(void) +{ + UINT8 localplayers = 1; + if (netgame) + CONS_Printf(M_GetText("Sending join request...\n")); + netbuffer->packettype = PT_CLIENTJOIN; + + netbuffer->u.clientcfg.modversion = MODVERSION; + strncpy(netbuffer->u.clientcfg.application, + SRB2APPLICATION, + sizeof netbuffer->u.clientcfg.application); + + if (splitscreen || botingame) + localplayers++; + netbuffer->u.clientcfg.localplayers = localplayers; + + CleanupPlayerName(consoleplayer, cv_playername.zstring); + if (splitscreen) + CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */ + + strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); + strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); + + return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); +} + +static void SendAskInfo(INT32 node) +{ + const tic_t asktime = I_GetTime(); + netbuffer->packettype = PT_ASKINFO; + netbuffer->u.askinfo.version = VERSION; + netbuffer->u.askinfo.time = (tic_t)LONG(asktime); + + // Even if this never arrives due to the host being firewalled, we've + // now allowed traffic from the host to us in, so once the MS relays + // our address to the host, it'll be able to speak to us. + HSendPacket(node, false, 0, sizeof (askinfo_pak)); +} + +serverelem_t serverlist[MAXSERVERLIST]; +UINT32 serverlistcount = 0; + +#define FORCECLOSE 0x8000 + +static void SL_ClearServerList(INT32 connectedserver) +{ + UINT32 i; + + for (i = 0; i < serverlistcount; i++) + if (connectedserver != serverlist[i].node) + { + Net_CloseConnection(serverlist[i].node|FORCECLOSE); + serverlist[i].node = 0; + } + serverlistcount = 0; +} + +static UINT32 SL_SearchServer(INT32 node) +{ + UINT32 i; + for (i = 0; i < serverlistcount; i++) + if (serverlist[i].node == node) + return i; + + return UINT32_MAX; +} + +static void SL_InsertServer(serverinfo_pak* info, SINT8 node) +{ + UINT32 i; + + // search if not already on it + i = SL_SearchServer(node); + if (i == UINT32_MAX) + { + // not found add it + if (serverlistcount >= MAXSERVERLIST) + return; // list full + + /* check it later if connecting to this one */ + if (node != servernode) + { + if (info->_255 != 255) + return;/* old packet format */ + + if (info->packetversion != PACKETVERSION) + return;/* old new packet format */ + + if (info->version != VERSION) + return; // Not same version. + + if (info->subversion != SUBVERSION) + return; // Close, but no cigar. + + if (strcmp(info->application, SRB2APPLICATION)) + return;/* that's a different mod */ + } + + i = serverlistcount++; + } + + serverlist[i].info = *info; + serverlist[i].node = node; + + // resort server list + M_SortServerList(); +} + +#if defined (MASTERSERVER) && defined (HAVE_THREADS) +struct Fetch_servers_ctx +{ + int room; + int id; +}; + +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) + { + I_lock_mutex(&ms_QueryId_mutex); + { + 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); + } + } + + free(ctx); +} +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ + +void CL_QueryServerList (msg_server_t *server_list) +{ + INT32 i; + + for (i = 0; server_list[i].header.buffer[0]; i++) + { + // Make sure MS version matches our own, to + // thwart nefarious servers who lie to the MS. + + /* lol bruh, that version COMES from the servers */ + //if (strcmp(version, server_list[i].version) == 0) + { + INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); + if (node == -1) + break; // no more node free + SendAskInfo(node); + // Force close the connection so that servers can't eat + // up nodes forever if we never get a reply back from them + // (usually when they've not forwarded their ports). + // + // Don't worry, we'll get in contact with the working + // servers again when they send SERVERINFO to us later! + // + // (Note: as a side effect this probably means every + // server in the list will probably be using the same node (e.g. node 1), + // not that it matters which nodes they use when + // the connections are closed afterwards anyway) + // -- Monster Iestyn 12/11/18 + Net_CloseConnection(node|FORCECLOSE); + } + } +} + +void CL_UpdateServerList(boolean internetsearch, INT32 room) +{ + (void)internetsearch; + (void)room; + + SL_ClearServerList(0); + + if (!netgame && I_NetOpenSocket) + { + if (I_NetOpenSocket()) + { + netgame = true; + multiplayer = true; + } + } + + // search for local servers + if (netgame) + SendAskInfo(BROADCASTADDR); + +#ifdef MASTERSERVER + if (internetsearch) + { +#ifdef HAVE_THREADS + struct Fetch_servers_ctx *ctx; + + 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/*MASTERSERVER*/ +} + +static void M_ConfirmConnect(event_t *ev) +{ + if (ev->type == ev_keydown || ev->type == ev_gamepad_down) + { + if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A)) + { + if (totalfilesrequestednum > 0) + { + if (CL_SendFileRequest()) + { + cl_mode = CL_DOWNLOADFILES; + Snake_Allocate(&snake); + } + } + else + cl_mode = CL_LOADFILES; + + M_ClearMenus(true); + } + else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B)) + { + cl_mode = CL_ABORTED; + M_ClearMenus(true); + } + } +} + +static boolean CL_FinishedFileList(void) +{ + INT32 i; + char *downloadsize = NULL; + + //CONS_Printf(M_GetText("Checking files...\n")); + i = CL_CheckFiles(); + if (i == 4) // still checking ... + { + return true; + } + else if (i == 3) // too many files + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have too many WAD files loaded\n" + "to add ones the server is using.\n" + "Please restart SRB2 before connecting.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 2) // cannot join for some reason + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have the wrong addons loaded.\n\n" + "To play on this server, restart\n" + "the game and don't load any addons.\n" + "SRB2 will automatically add\n" + "everything you need when you join.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 1) + { + 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 ENTER to continue\nor ESC to cancel.\n\n" + ), M_ConfirmConnect, MM_EVENTHANDLER); + cl_mode = CL_CONFIRMCONNECT; + curfadevalue = 0; + } + else + cl_mode = CL_LOADFILES; + } + else + { + // must download something + // can we, though? + if (!CL_CheckDownloadable()) // nope! + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "An error occured when trying to\n" + "download missing addons.\n" + "(This is almost always a problem\n" + "with the server, not your game.)\n\n" + "See the console or log file\n" + "for additional details.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + + downloadcompletednum = 0; + downloadcompletedsize = 0; + totalfilesrequestednum = 0; + totalfilesrequestedsize = 0; + + if (fileneeded == NULL) + I_Error("CL_FinishedFileList: fileneeded == NULL"); + + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) + { + totalfilesrequestednum++; + totalfilesrequestedsize += fileneeded[i].totalsize; + } + + if (totalfilesrequestedsize>>20 >= 100) + downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20)); + else + downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10)); + + if (serverisfull) + M_StartMessage(va(M_GetText( + "This server is full!\n" + "Download of %s additional content\nis required to join.\n" + "\n" + "You may download, load server addons,\nand wait for a slot.\n" + "\n" + "Press ENTER to continue\nor ESC to cancel.\n" + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + else + M_StartMessage(va(M_GetText( + "Download of %s additional content\nis required to join.\n" + "\n" + "Press ENTER to continue\nor ESC to cancel.\n" + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + + Z_Free(downloadsize); + cl_mode = CL_CONFIRMCONNECT; + curfadevalue = 0; + } + return true; +} + +static const char * InvalidServerReason (serverinfo_pak *info) +{ +#define EOT "\nPress ESC\n" + + /* magic number for new packet format */ + if (info->_255 != 255) + { + return + "Outdated server (version unknown).\n" EOT; + } + + if (strncmp(info->application, SRB2APPLICATION, sizeof + info->application)) + { + return va( + "%s cannot connect\n" + "to %s servers.\n" EOT, + SRB2APPLICATION, + info->application); + } + + if ( + info->packetversion != PACKETVERSION || + info->version != VERSION || + info->subversion != SUBVERSION + ){ + return va( + "Incompatible %s versions.\n" + "(server version %d.%d.%d)\n" EOT, + SRB2APPLICATION, + info->version / 100, + info->version % 100, + info->subversion); + } + + switch (info->refusereason) + { + case REFUSE_BANNED: + return + "You have been banned\n" + "from the server.\n" EOT; + case REFUSE_JOINS_DISABLED: + return + "The server is not accepting\n" + "joins for the moment.\n" EOT; + case REFUSE_SLOTS_FULL: + return va( + "Maximum players reached: %d\n" EOT, + info->maxplayer); + default: + if (info->refusereason) + { + return + "You can't join.\n" + "I don't know why,\n" + "but you can't join.\n" EOT; + } + } + + return NULL; + +#undef EOT +} + +/** Called by CL_ServerConnectionTicker + * + * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. + * \return False if the connection was aborted + * \sa CL_ServerConnectionTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) +{ + INT32 i; + + // serverlist is updated by GetPacket function + if (serverlistcount > 0) + { + // this can be a responce to our broadcast request + if (servernode == -1 || servernode >= MAXNETNODES) + { + i = 0; + servernode = serverlist[i].node; + CONS_Printf(M_GetText("Found, ")); + } + else + { + i = SL_SearchServer(servernode); + if (i < 0) + return true; + } + + if (client) + { + serverinfo_pak *info = &serverlist[i].info; + + if (info->refusereason == REFUSE_SLOTS_FULL) + serverisfull = true; + else + { + const char *reason = InvalidServerReason(info); + + // Quit here rather than downloading files + // and being refused later. + if (reason) + { + char *message = Z_StrDup(reason); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(message, NULL, MM_NOTHING); + Z_Free(message); + return false; + } + } + + D_ParseFileneeded(info->fileneedednum, info->fileneeded, 0); + + if (info->flags & SV_LOTSOFADDONS) + { + cl_mode = CL_ASKFULLFILELIST; + cl_lastcheckedfilecount = 0; + return true; + } + + cl_mode = CL_CHECKFILES; + } + else + { + cl_mode = CL_ASKJOIN; // files need not be checked for the server. + *asksent = 0; + } + + return true; + } + + // Ask the info to the server (askinfo packet) + if (*asksent + NEWTICRATE < I_GetTime()) + { + SendAskInfo(servernode); + *asksent = I_GetTime(); + } + + return true; +} + +/** Called by CL_ConnectToServer + * + * \param tmpsave The name of the gamestate file??? + * \param oldtic Used for knowing when to poll events and redraw + * \param asksent ??? + * \return False if the connection was aborted + * \sa CL_ServerConnectionSearchTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) +{ + boolean waitmore; + INT32 i; + + switch (cl_mode) + { + case CL_SEARCHING: + if (!CL_ServerConnectionSearchTicker(asksent)) + return false; + break; + + case CL_ASKFULLFILELIST: + if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved + cl_mode = CL_CHECKFILES; + else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent) + { + if (CL_AskFileList(fileneedednum)) + { + cl_lastcheckedfilecount = fileneedednum; + *asksent = I_GetTime() + NEWTICRATE; + } + } + break; + case CL_CHECKFILES: + if (!CL_FinishedFileList()) + return false; + break; + case CL_DOWNLOADFILES: + waitmore = false; + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_DOWNLOADING + || fileneeded[i].status == FS_REQUESTED) + { + waitmore = true; + break; + } + if (waitmore) + break; // exit the case + + Snake_Free(&snake); + + cl_mode = CL_LOADFILES; + break; + case CL_LOADFILES: + if (CL_LoadServerFiles()) + { + FreeFileNeeded(); + *asksent = 0; //This ensure the first join ask is right away + firstconnectattempttime = I_GetTime(); + cl_mode = CL_ASKJOIN; + } + break; + case CL_ASKJOIN: + 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); + + 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; + case CL_DOWNLOADSAVEGAME: + // At this state, the first (and only) needed file is the gamestate + if (fileneeded[0].status == FS_FOUND) + { + // Gamestate is now handled within CL_LoadReceivedSavegame() + CL_LoadReceivedSavegame(false); + cl_mode = CL_CONNECTED; + } // don't break case continue to CL_CONNECTED + else + break; + + case CL_CONNECTED: + case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect + default: + break; + + // Connection closed by cancel, timeout or refusal. + case CL_ABORTED: + cl_mode = CL_SEARCHING; + return false; + } + + GetPackets(); + Net_AckTicker(); + + // Call it only once by tic + if (*oldtic != I_GetTime()) + { + I_OsPolling(); + + if (cl_mode == CL_CONFIRMCONNECT) + D_ProcessEvents(); //needed for menu system to receive inputs + else + { + // my hand has been forced and I am dearly sorry for this awful hack :vomit: + for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) + { + G_MapEventsToControls(&events[eventtail]); + } + } + + if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || 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); + + Snake_Free(&snake); + + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + memset(gamekeydown, 0, NUMKEYS); + return false; + } + else if (cl_mode == CL_DOWNLOADFILES && snake) + Snake_Update(snake); + + if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) + FileReceiveTicker(); + + // why are these here? this is for servers, we're a client + //if (key == 's' && server) + // doomcom->numnodes = (INT16)pnumnodes; + //FileSendTicker(); + *oldtic = I_GetTime(); + + if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) + { + if (!snake) + { + F_MenuPresTicker(true); // title sky + F_TitleScreenTicker(true); + 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(); + S_UpdateSounds(); + S_UpdateClosedCaptions(); + } + } + else + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } + + return true; +} + +#define TMPSAVENAME "$$$.sav" + +/** Use adaptive send using net_bandwidth and stat.sendbytes + * + * \todo Better description... + * + */ +void CL_ConnectToServer(void) +{ + INT32 pnumnodes, nodewaited = doomcom->numnodes, i; + tic_t oldtic; + tic_t asksent; + char tmpsave[256]; + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + lastfilenum = -1; + + cl_mode = CL_SEARCHING; + + // Don't get a corrupt savegame error because tmpsave already exists + if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) + I_Error("Can't delete %s\n", tmpsave); + + if (netgame) + { + if (servernode < 0 || servernode >= MAXNETNODES) + CONS_Printf(M_GetText("Searching for a server...\n")); + else + CONS_Printf(M_GetText("Contacting the server...\n")); + } + + if (gamestate == GS_INTERMISSION) + Y_EndIntermission(); // clean up intermission graphics etc + + DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); + G_SetGamestate(GS_WAITINGPLAYERS); + wipegamestate = GS_WAITINGPLAYERS; + + ClearAdminPlayers(); + pnumnodes = 1; + oldtic = I_GetTime() - 1; + + asksent = (tic_t) - TICRATE; + firstconnectattempttime = I_GetTime(); + + i = SL_SearchServer(servernode); + + if (i != -1) + { + char *gametypestr = serverlist[i].info.gametypename; + CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); + gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0'; + CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); + CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, + serverlist[i].info.version%100, serverlist[i].info.subversion); + } + SL_ClearServerList(servernode); + + do + { + // If the connection was aborted for some reason, leave + if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) + return; + + if (server) + { + pnumnodes = 0; + for (i = 0; i < MAXNETNODES; i++) + if (netnodes[i].ingame) + pnumnodes++; + } + } + while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); + + DEBFILE(va("Synchronisation Finished\n")); + + displayplayer = consoleplayer; +} + +/** Called when a PT_SERVERINFO packet is received + * + * \param node The packet sender + * \note What happens if the packet comes from a client or something like that? + * + */ +void HandleServerInfo(SINT8 node) +{ + // compute ping in ms + const tic_t ticnow = I_GetTime(); + const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time); + const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE; + netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff); + netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; + netbuffer->u.serverinfo.application + [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; + netbuffer->u.serverinfo.gametypename + [sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0'; + + SL_InsertServer(&netbuffer->u.serverinfo, node); +} + +// Helper function for packets that should only be sent by the server +// If it is NOT from the server, bail out and close the connection! +static boolean ServerOnly(SINT8 node) +{ + if (node == servernode) + return false; + + Net_CloseConnection(node); + return true; +} + +void PT_MoreFilesNeeded(SINT8 node) +{ + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) + { + D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); + if (!netbuffer->u.filesneededcfg.more) + cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list + } +} + +// Negative response of client join request +void PT_ServerRefuse(SINT8 node) +{ + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + if (cl_mode == CL_WAITJOINRESPONSE) + { + // Save the reason so it can be displayed after quitting the netgame + char *reason = strdup(netbuffer->u.serverrefuse.reason); + if (!reason) + I_Error("Out of memory!\n"); + + 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); + return; + } + + 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. + cl_mode = CL_ABORTED; + } +} + +// Positive response of client join request +void PT_ServerCFG(SINT8 node) +{ + if (server && serverrunning && node != servernode) + { // but wait I thought I'm the server? + Net_CloseConnection(node); + return; + } + if (ServerOnly(node)) + return; + /// \note how would this happen? and is it doing the right thing if it does? + if (cl_mode != CL_WAITJOINRESPONSE) + return; + + if (client) + { + maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); + G_SetGametype(netbuffer->u.servercfg.gametype); + modifiedgame = netbuffer->u.servercfg.modifiedgame; + memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + } + + netnodes[(UINT8)servernode].ingame = true; + serverplayer = netbuffer->u.servercfg.serverplayer; + doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); + mynode = netbuffer->u.servercfg.clientnode; + if (serverplayer >= 0) + playernode[(UINT8)serverplayer] = servernode; + + if (netgame) + CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); + DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); + + /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? + /// Shouldn't them be downloaded even at intermission time? + /// Also, according to HandleConnect, the server will send the savegame even during intermission... + if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || + netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) + cl_mode = CL_DOWNLOADSAVEGAME; + else + cl_mode = CL_CONNECTED; +} diff --git a/src/netcode/client_connection.h b/src/netcode/client_connection.h new file mode 100644 index 000000000..a76411ba6 --- /dev/null +++ b/src/netcode/client_connection.h @@ -0,0 +1,61 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 client_connection.h +/// \brief Client connection handling + +#ifndef __D_CLIENT_CONNECTION__ +#define __D_CLIENT_CONNECTION__ + +#include "../doomtype.h" +#include "d_clisrv.h" + +#define MAXSERVERLIST (MAXNETNODES-1) + +typedef struct +{ + SINT8 node; + serverinfo_pak info; +} serverelem_t; + +typedef enum +{ + CL_SEARCHING, + CL_CHECKFILES, + CL_DOWNLOADFILES, + CL_ASKJOIN, + CL_LOADFILES, + CL_WAITJOINRESPONSE, + CL_DOWNLOADSAVEGAME, + CL_CONNECTED, + CL_ABORTED, + CL_ASKFULLFILELIST, + CL_CONFIRMCONNECT +} cl_mode_t; + +extern serverelem_t serverlist[MAXSERVERLIST]; +extern UINT32 serverlistcount; + +extern cl_mode_t cl_mode; +extern boolean serverisfull; //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 +extern tic_t firstconnectattempttime; +extern UINT8 mynode; // my address pointofview server + +void CL_QueryServerList(msg_server_t *list); +void CL_UpdateServerList(boolean internetsearch, INT32 room); + +void CL_ConnectToServer(void); +boolean CL_SendJoin(void); + +void HandleServerInfo(SINT8 node); +void PT_MoreFilesNeeded(SINT8 node); +void PT_ServerRefuse(SINT8 node); +void PT_ServerCFG(SINT8 node); + +#endif diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 9b11e23ff..f7a9260e1 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -49,14 +49,7 @@ #include "../lua_libs.h" #include "../md5.h" #include "../m_perfstats.h" - -// aaaaaa -#include "../i_gamepad.h" - -// cl loading screen -#include "../v_video.h" -#include "../f_finale.h" -#include "../snake.h" +#include "client_connection.h" // // NETWORKING @@ -103,7 +96,7 @@ tic_t servermaxping = 800; // server's max ping. Defaults to 800 static tic_t firstticstosend; // min of the nettics static tic_t tictoclear = 0; // optimize d_clearticcmd -static tic_t maketic; +tic_t maketic; static INT16 consistancy[BACKUPTICS]; @@ -121,21 +114,17 @@ static ticcmd_t localcmds; static ticcmd_t localcmds2; static boolean cl_packetmissed; // here it is for the secondary local player (splitscreen) -static UINT8 mynode; // my address pointofview server static boolean cl_redownloadinggamestate = false; static UINT8 localtextcmd[MAXTEXTCMD]; static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen -static tic_t neededtic; +tic_t neededtic; SINT8 servernode = 0; // the number of the server node /// \brief do we accept new players? /// \todo WORK! boolean acceptnewnode = true; -static 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 -static tic_t firstconnectattempttime = 0; - // engine // Must be a power of two @@ -512,250 +501,6 @@ void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum) 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 -} cl_mode_t; - -static void GetPackets(void); - -static cl_mode_t cl_mode = CL_SEARCHING; - -static UINT16 cl_lastcheckedfilecount = 0; // used for full file list - -static void *snake = NULL; - -static void CL_DrawConnectionStatusBox(void) -{ - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); - if (cl_mode != CL_CONFIRMCONNECT) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); -} - -// -// CL_DrawConnectionStatus -// -// Keep the local client informed of our status. -// -static inline void CL_DrawConnectionStatus(void) -{ - INT32 ccstime = I_GetTime(); - - // Draw background fade - V_DrawFadeScreen(0xFF00, 16); // force default - - if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES) - { - INT32 i, animtime = ((ccstime / 4) & 15) + 16; - UINT8 palstart; - const char *cltext; - - // Draw the bottom box. - CL_DrawConnectionStatusBox(); - - if (cl_mode == CL_SEARCHING) - palstart = 32; // Red - else if (cl_mode == CL_CONFIRMCONNECT) - palstart = 48; // Orange - else - palstart = 96; // Green - - if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) - for (i = 0; i < 16; ++i) // 15 pal entries total. - V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); - - switch (cl_mode) - { - case CL_DOWNLOADSAVEGAME: - if (fileneeded && lastfilenum != -1) - { - UINT32 currentsize = fileneeded[lastfilenum].currentsize; - UINT32 totalsize = fileneeded[lastfilenum].totalsize; - INT32 dldlength; - - cltext = M_GetText("Downloading game state..."); - Net_GetNetStat(); - - dldlength = (INT32)((currentsize/(double)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_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %4uK/%4uK",currentsize>>10,totalsize>>10)); - - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); - } - else - cltext = M_GetText("Waiting to download game state..."); - break; - case CL_ASKFULLFILELIST: - 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: - if (serverisfull) - cltext = M_GetText("Server full, waiting for a slot..."); - else - cltext = M_GetText("Requesting to join..."); - break; - default: - cltext = M_GetText("Connecting to server..."); - break; - } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext); - } - else - { - if (cl_mode == CL_LOADFILES) - { - INT32 totalfileslength; - INT32 loadcompletednum = 0; - INT32 i; - - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); - - //ima just count files here - if (fileneeded) - { - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_OPEN) - loadcompletednum++; - } - - // Loading progress - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, "Loading server addons..."); - totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256); - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %2u/%2u Files",loadcompletednum,fileneedednum)); - } - else if (lastfilenum != -1) - { - INT32 dldlength; - static char tempname[28]; - fileneeded_t *file; - char *filename; - - if (snake) - Snake_Draw(snake); - - // Draw the bottom box. - CL_DrawConnectionStatusBox(); - - if (fileneeded) - { - file = &fileneeded[lastfilenum]; - filename = file->filename; - } - else - return; - - 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); - - memset(tempname, 0, sizeof(tempname)); - // offset filename to just the name only part - filename += strlen(filename) - nameonlylength(filename); - - if (strlen(filename) > sizeof(tempname)-1) // too long to display fully - { - size_t endhalfpos = strlen(filename)-10; - // display as first 14 chars + ... + last 10 chars - // which should add up to 27 if our math(s) is correct - snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos); - } - else // we can copy the whole thing in safely - { - strncpy(tempname, filename, sizeof(tempname)-1); - } - - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, - va(M_GetText("Downloading \"%s\""), tempname)); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 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, - va("%3.1fK/s ", ((double)getbps)/1024)); - } - else - { - if (snake) - Snake_Draw(snake); - - CL_DrawConnectionStatusBox(); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, - M_GetText("Waiting to download files...")); - } - } -} - -static boolean CL_AskFileList(INT32 firstfile) -{ - netbuffer->packettype = PT_TELLFILESNEEDED; - netbuffer->u.filesneedednum = firstfile; - - return HSendPacket(servernode, false, 0, sizeof (INT32)); -} - -/** Sends a special packet to declare how many players in local - * Used only in arbitratrenetstart() - * Sends a PT_CLIENTJOIN packet to the server - * - * \return True if the packet was successfully sent - * \todo Improve the description... - * - */ -static boolean CL_SendJoin(void) -{ - UINT8 localplayers = 1; - if (netgame) - CONS_Printf(M_GetText("Sending join request...\n")); - netbuffer->packettype = PT_CLIENTJOIN; - - netbuffer->u.clientcfg.modversion = MODVERSION; - strncpy(netbuffer->u.clientcfg.application, - SRB2APPLICATION, - sizeof netbuffer->u.clientcfg.application); - - if (splitscreen || botingame) - localplayers++; - netbuffer->u.clientcfg.localplayers = localplayers; - - CleanupPlayerName(consoleplayer, cv_playername.zstring); - if (splitscreen) - CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */ - - strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); - strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); - - return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); -} - static INT32 FindRejoinerNum(SINT8 node) { char strippednodeaddress[64]; @@ -1105,7 +850,7 @@ static void SV_SavedGame(void) #define TMPSAVENAME "$$$.sav" -static void CL_LoadReceivedSavegame(boolean reloading) +void CL_LoadReceivedSavegame(boolean reloading) { UINT8 *savebuffer = NULL; size_t length, decompressedlen; @@ -1206,790 +951,6 @@ static void CL_ReloadReceivedSavegame(void) CONS_Printf(M_GetText("Game state reloaded\n")); } -static void SendAskInfo(INT32 node) -{ - const tic_t asktime = I_GetTime(); - netbuffer->packettype = PT_ASKINFO; - netbuffer->u.askinfo.version = VERSION; - netbuffer->u.askinfo.time = (tic_t)LONG(asktime); - - // Even if this never arrives due to the host being firewalled, we've - // now allowed traffic from the host to us in, so once the MS relays - // our address to the host, it'll be able to speak to us. - HSendPacket(node, false, 0, sizeof (askinfo_pak)); -} - -serverelem_t serverlist[MAXSERVERLIST]; -UINT32 serverlistcount = 0; - -#define FORCECLOSE 0x8000 - -static void SL_ClearServerList(INT32 connectedserver) -{ - UINT32 i; - - for (i = 0; i < serverlistcount; i++) - if (connectedserver != serverlist[i].node) - { - Net_CloseConnection(serverlist[i].node|FORCECLOSE); - serverlist[i].node = 0; - } - serverlistcount = 0; -} - -static UINT32 SL_SearchServer(INT32 node) -{ - UINT32 i; - for (i = 0; i < serverlistcount; i++) - if (serverlist[i].node == node) - return i; - - return UINT32_MAX; -} - -static void SL_InsertServer(serverinfo_pak* info, SINT8 node) -{ - UINT32 i; - - // search if not already on it - i = SL_SearchServer(node); - if (i == UINT32_MAX) - { - // not found add it - if (serverlistcount >= MAXSERVERLIST) - return; // list full - - /* check it later if connecting to this one */ - if (node != servernode) - { - if (info->_255 != 255) - return;/* old packet format */ - - if (info->packetversion != PACKETVERSION) - return;/* old new packet format */ - - if (info->version != VERSION) - return; // Not same version. - - if (info->subversion != SUBVERSION) - return; // Close, but no cigar. - - if (strcmp(info->application, SRB2APPLICATION)) - return;/* that's a different mod */ - } - - i = serverlistcount++; - } - - serverlist[i].info = *info; - serverlist[i].node = node; - - // resort server list - M_SortServerList(); -} - -#if defined (MASTERSERVER) && defined (HAVE_THREADS) -struct Fetch_servers_ctx -{ - int room; - int id; -}; - -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) - { - I_lock_mutex(&ms_QueryId_mutex); - { - 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); - } - } - - free(ctx); -} -#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ - -void CL_QueryServerList (msg_server_t *server_list) -{ - INT32 i; - - for (i = 0; server_list[i].header.buffer[0]; i++) - { - // Make sure MS version matches our own, to - // thwart nefarious servers who lie to the MS. - - /* lol bruh, that version COMES from the servers */ - //if (strcmp(version, server_list[i].version) == 0) - { - INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); - if (node == -1) - break; // no more node free - SendAskInfo(node); - // Force close the connection so that servers can't eat - // up nodes forever if we never get a reply back from them - // (usually when they've not forwarded their ports). - // - // Don't worry, we'll get in contact with the working - // servers again when they send SERVERINFO to us later! - // - // (Note: as a side effect this probably means every - // server in the list will probably be using the same node (e.g. node 1), - // not that it matters which nodes they use when - // the connections are closed afterwards anyway) - // -- Monster Iestyn 12/11/18 - Net_CloseConnection(node|FORCECLOSE); - } - } -} - -void CL_UpdateServerList(boolean internetsearch, INT32 room) -{ - (void)internetsearch; - (void)room; - - SL_ClearServerList(0); - - if (!netgame && I_NetOpenSocket) - { - if (I_NetOpenSocket()) - { - netgame = true; - multiplayer = true; - } - } - - // search for local servers - if (netgame) - SendAskInfo(BROADCASTADDR); - -#ifdef MASTERSERVER - if (internetsearch) - { -#ifdef HAVE_THREADS - struct Fetch_servers_ctx *ctx; - - 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/*MASTERSERVER*/ -} - -static void M_ConfirmConnect(event_t *ev) -{ - if (ev->type == ev_keydown || ev->type == ev_gamepad_down) - { - if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A)) - { - if (totalfilesrequestednum > 0) - { - if (CL_SendFileRequest()) - { - cl_mode = CL_DOWNLOADFILES; - Snake_Allocate(&snake); - } - } - else - cl_mode = CL_LOADFILES; - - M_ClearMenus(true); - } - else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B)) - { - cl_mode = CL_ABORTED; - M_ClearMenus(true); - } - } -} - -static boolean CL_FinishedFileList(void) -{ - INT32 i; - char *downloadsize = NULL; - - //CONS_Printf(M_GetText("Checking files...\n")); - i = CL_CheckFiles(); - if (i == 4) // still checking ... - { - return true; - } - else if (i == 3) // too many files - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have too many WAD files loaded\n" - "to add ones the server is using.\n" - "Please restart SRB2 before connecting.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - else if (i == 2) // cannot join for some reason - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have the wrong addons loaded.\n\n" - "To play on this server, restart\n" - "the game and don't load any addons.\n" - "SRB2 will automatically add\n" - "everything you need when you join.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - else if (i == 1) - { - 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 ENTER to continue\nor ESC to cancel.\n\n" - ), M_ConfirmConnect, MM_EVENTHANDLER); - cl_mode = CL_CONFIRMCONNECT; - curfadevalue = 0; - } - else - cl_mode = CL_LOADFILES; - } - else - { - // must download something - // can we, though? - if (!CL_CheckDownloadable()) // nope! - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "An error occured when trying to\n" - "download missing addons.\n" - "(This is almost always a problem\n" - "with the server, not your game.)\n\n" - "See the console or log file\n" - "for additional details.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - - downloadcompletednum = 0; - downloadcompletedsize = 0; - totalfilesrequestednum = 0; - totalfilesrequestedsize = 0; - - if (fileneeded == NULL) - I_Error("CL_FinishedFileList: fileneeded == NULL"); - - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) - { - totalfilesrequestednum++; - totalfilesrequestedsize += fileneeded[i].totalsize; - } - - if (totalfilesrequestedsize>>20 >= 100) - downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20)); - else - downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10)); - - if (serverisfull) - M_StartMessage(va(M_GetText( - "This server is full!\n" - "Download of %s additional content\nis required to join.\n" - "\n" - "You may download, load server addons,\nand wait for a slot.\n" - "\n" - "Press ENTER to continue\nor ESC to cancel.\n" - ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); - else - M_StartMessage(va(M_GetText( - "Download of %s additional content\nis required to join.\n" - "\n" - "Press ENTER to continue\nor ESC to cancel.\n" - ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); - - Z_Free(downloadsize); - cl_mode = CL_CONFIRMCONNECT; - curfadevalue = 0; - } - return true; -} - -static const char * InvalidServerReason (serverinfo_pak *info) -{ -#define EOT "\nPress ESC\n" - - /* magic number for new packet format */ - if (info->_255 != 255) - { - return - "Outdated server (version unknown).\n" EOT; - } - - if (strncmp(info->application, SRB2APPLICATION, sizeof - info->application)) - { - return va( - "%s cannot connect\n" - "to %s servers.\n" EOT, - SRB2APPLICATION, - info->application); - } - - if ( - info->packetversion != PACKETVERSION || - info->version != VERSION || - info->subversion != SUBVERSION - ){ - return va( - "Incompatible %s versions.\n" - "(server version %d.%d.%d)\n" EOT, - SRB2APPLICATION, - info->version / 100, - info->version % 100, - info->subversion); - } - - switch (info->refusereason) - { - case REFUSE_BANNED: - return - "You have been banned\n" - "from the server.\n" EOT; - case REFUSE_JOINS_DISABLED: - return - "The server is not accepting\n" - "joins for the moment.\n" EOT; - case REFUSE_SLOTS_FULL: - return va( - "Maximum players reached: %d\n" EOT, - info->maxplayer); - default: - if (info->refusereason) - { - return - "You can't join.\n" - "I don't know why,\n" - "but you can't join.\n" EOT; - } - } - - return NULL; - -#undef EOT -} - -/** Called by CL_ServerConnectionTicker - * - * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. - * \return False if the connection was aborted - * \sa CL_ServerConnectionTicker - * \sa CL_ConnectToServer - * - */ -static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) -{ - INT32 i; - - // serverlist is updated by GetPacket function - if (serverlistcount > 0) - { - // this can be a responce to our broadcast request - if (servernode == -1 || servernode >= MAXNETNODES) - { - i = 0; - servernode = serverlist[i].node; - CONS_Printf(M_GetText("Found, ")); - } - else - { - i = SL_SearchServer(servernode); - if (i < 0) - return true; - } - - if (client) - { - serverinfo_pak *info = &serverlist[i].info; - - if (info->refusereason == REFUSE_SLOTS_FULL) - serverisfull = true; - else - { - const char *reason = InvalidServerReason(info); - - // Quit here rather than downloading files - // and being refused later. - if (reason) - { - char *message = Z_StrDup(reason); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(message, NULL, MM_NOTHING); - Z_Free(message); - return false; - } - } - - D_ParseFileneeded(info->fileneedednum, info->fileneeded, 0); - - if (info->flags & SV_LOTSOFADDONS) - { - cl_mode = CL_ASKFULLFILELIST; - cl_lastcheckedfilecount = 0; - return true; - } - - cl_mode = CL_CHECKFILES; - } - else - { - cl_mode = CL_ASKJOIN; // files need not be checked for the server. - *asksent = 0; - } - - return true; - } - - // Ask the info to the server (askinfo packet) - if (*asksent + NEWTICRATE < I_GetTime()) - { - SendAskInfo(servernode); - *asksent = I_GetTime(); - } - - return true; -} - -/** Called by CL_ConnectToServer - * - * \param tmpsave The name of the gamestate file??? - * \param oldtic Used for knowing when to poll events and redraw - * \param asksent ??? - * \return False if the connection was aborted - * \sa CL_ServerConnectionSearchTicker - * \sa CL_ConnectToServer - * - */ -static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) -{ - boolean waitmore; - INT32 i; - - switch (cl_mode) - { - case CL_SEARCHING: - if (!CL_ServerConnectionSearchTicker(asksent)) - return false; - break; - - case CL_ASKFULLFILELIST: - if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved - cl_mode = CL_CHECKFILES; - else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent) - { - if (CL_AskFileList(fileneedednum)) - { - cl_lastcheckedfilecount = fileneedednum; - *asksent = I_GetTime() + NEWTICRATE; - } - } - break; - case CL_CHECKFILES: - if (!CL_FinishedFileList()) - return false; - break; - case CL_DOWNLOADFILES: - waitmore = false; - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_DOWNLOADING - || fileneeded[i].status == FS_REQUESTED) - { - waitmore = true; - break; - } - if (waitmore) - break; // exit the case - - Snake_Free(&snake); - - cl_mode = CL_LOADFILES; - break; - case CL_LOADFILES: - if (CL_LoadServerFiles()) - { - FreeFileNeeded(); - *asksent = 0; //This ensure the first join ask is right away - firstconnectattempttime = I_GetTime(); - cl_mode = CL_ASKJOIN; - } - break; - case CL_ASKJOIN: - 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); - - 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; - case CL_DOWNLOADSAVEGAME: - // At this state, the first (and only) needed file is the gamestate - if (fileneeded[0].status == FS_FOUND) - { - // Gamestate is now handled within CL_LoadReceivedSavegame() - CL_LoadReceivedSavegame(false); - cl_mode = CL_CONNECTED; - } // don't break case continue to CL_CONNECTED - else - break; - - case CL_CONNECTED: - case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect - default: - break; - - // Connection closed by cancel, timeout or refusal. - case CL_ABORTED: - cl_mode = CL_SEARCHING; - return false; - } - - GetPackets(); - Net_AckTicker(); - - // Call it only once by tic - if (*oldtic != I_GetTime()) - { - I_OsPolling(); - - if (cl_mode == CL_CONFIRMCONNECT) - D_ProcessEvents(); //needed for menu system to receive inputs - else - { - // my hand has been forced and I am dearly sorry for this awful hack :vomit: - for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) - { - G_MapEventsToControls(&events[eventtail]); - } - } - - if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || 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); - - Snake_Free(&snake); - - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - memset(gamekeydown, 0, NUMKEYS); - return false; - } - else if (cl_mode == CL_DOWNLOADFILES && snake) - Snake_Update(snake); - - if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) - FileReceiveTicker(); - - // why are these here? this is for servers, we're a client - //if (key == 's' && server) - // doomcom->numnodes = (INT16)pnumnodes; - //FileSendTicker(); - *oldtic = I_GetTime(); - - if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) - { - if (!snake) - { - F_MenuPresTicker(true); // title sky - F_TitleScreenTicker(true); - 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(); - S_UpdateSounds(); - S_UpdateClosedCaptions(); - } - } - else - { - I_Sleep(cv_sleep.value); - I_UpdateTime(cv_timescale.value); - } - - return true; -} - -/** Use adaptive send using net_bandwidth and stat.sendbytes - * - * \todo Better description... - * - */ -static void CL_ConnectToServer(void) -{ - INT32 pnumnodes, nodewaited = doomcom->numnodes, i; - tic_t oldtic; - tic_t asksent; - char tmpsave[256]; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - lastfilenum = -1; - - cl_mode = CL_SEARCHING; - - // Don't get a corrupt savegame error because tmpsave already exists - if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) - I_Error("Can't delete %s\n", tmpsave); - - if (netgame) - { - if (servernode < 0 || servernode >= MAXNETNODES) - CONS_Printf(M_GetText("Searching for a server...\n")); - else - CONS_Printf(M_GetText("Contacting the server...\n")); - } - - if (gamestate == GS_INTERMISSION) - Y_EndIntermission(); // clean up intermission graphics etc - - DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); - G_SetGamestate(GS_WAITINGPLAYERS); - wipegamestate = GS_WAITINGPLAYERS; - - ClearAdminPlayers(); - pnumnodes = 1; - oldtic = I_GetTime() - 1; - - asksent = (tic_t) - TICRATE; - firstconnectattempttime = I_GetTime(); - - i = SL_SearchServer(servernode); - - if (i != -1) - { - char *gametypestr = serverlist[i].info.gametypename; - CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); - gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0'; - CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); - CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, - serverlist[i].info.version%100, serverlist[i].info.subversion); - } - SL_ClearServerList(servernode); - - do - { - // If the connection was aborted for some reason, leave - if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) - return; - - if (server) - { - pnumnodes = 0; - for (i = 0; i < MAXNETNODES; i++) - if (netnodes[i].ingame) - pnumnodes++; - } - } - while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); - - DEBFILE(va("Synchronisation Finished\n")); - - displayplayer = consoleplayer; -} - typedef struct banreason_s { char *reason; @@ -3525,39 +2486,6 @@ static void HandleTimeout(SINT8 node) M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); } -/** Called when a PT_SERVERINFO packet is received - * - * \param node The packet sender - * \note What happens if the packet comes from a client or something like that? - * - */ -static void HandleServerInfo(SINT8 node) -{ - // compute ping in ms - const tic_t ticnow = I_GetTime(); - const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time); - const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE; - netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff); - netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; - netbuffer->u.serverinfo.application - [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; - netbuffer->u.serverinfo.gametypename - [sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0'; - - SL_InsertServer(&netbuffer->u.serverinfo, node); -} - -// Helper function for packets that should only be sent by the server -// If it is NOT from the server, bail out and close the connection! -static boolean ServerOnly(SINT8 node) -{ - if (node == servernode) - return false; - - Net_CloseConnection(node); - return true; -} - static void PT_AskInfoViaMS(SINT8 node) { Net_CloseConnection(node); @@ -3582,23 +2510,6 @@ static void PT_TellFilesNeeded(SINT8 node) Net_CloseConnection(node); } -static void PT_MoreFilesNeeded(SINT8 node) -{ - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - return; - } - if (ServerOnly(node)) - return; - if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) - { - D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); - if (!netbuffer->u.filesneededcfg.more) - cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list - } -} - static void PT_AskInfo(SINT8 node) { if (server && serverrunning) @@ -3609,91 +2520,6 @@ static void PT_AskInfo(SINT8 node) Net_CloseConnection(node); } -// Negative response of client join request -static void PT_ServerRefuse(SINT8 node) -{ - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - return; - } - if (ServerOnly(node)) - return; - if (cl_mode == CL_WAITJOINRESPONSE) - { - // Save the reason so it can be displayed after quitting the netgame - char *reason = strdup(netbuffer->u.serverrefuse.reason); - if (!reason) - I_Error("Out of memory!\n"); - - 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); - return; - } - - 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. - cl_mode = CL_ABORTED; - } -} - -// Positive response of client join request -static void PT_ServerCFG(SINT8 node) -{ - if (server && serverrunning && node != servernode) - { // but wait I thought I'm the server? - Net_CloseConnection(node); - return; - } - if (ServerOnly(node)) - return; - /// \note how would this happen? and is it doing the right thing if it does? - if (cl_mode != CL_WAITJOINRESPONSE) - return; - - if (client) - { - maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); - G_SetGametype(netbuffer->u.servercfg.gametype); - modifiedgame = netbuffer->u.servercfg.modifiedgame; - memcpy(server_context, netbuffer->u.servercfg.server_context, 8); - } - - netnodes[(UINT8)servernode].ingame = true; - serverplayer = netbuffer->u.servercfg.serverplayer; - doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); - mynode = netbuffer->u.servercfg.clientnode; - if (serverplayer >= 0) - playernode[(UINT8)serverplayer] = servernode; - - if (netgame) - CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); - DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); - - /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? - /// Shouldn't them be downloaded even at intermission time? - /// Also, according to HandleConnect, the server will send the savegame even during intermission... - if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || - netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) - cl_mode = CL_DOWNLOADSAVEGAME; - else - cl_mode = CL_CONNECTED; -} - static void PT_ClientCmd(SINT8 node, INT32 netconsole) { tic_t realend, realstart; @@ -4190,7 +3016,7 @@ static void HandlePacketFromPlayer(SINT8 node) * \todo Add details to this description (lol) * */ -static void GetPackets(void) +void GetPackets(void) { SINT8 node; // The packet sender diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 50b86e9f0..3b0690a79 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -332,15 +332,6 @@ typedef struct #pragma pack() #endif -#define MAXSERVERLIST (MAXNETNODES-1) -typedef struct -{ - SINT8 node; - serverinfo_pak info; -} serverelem_t; - -extern serverelem_t serverlist[MAXSERVERLIST]; -extern UINT32 serverlistcount; extern INT32 mapchangepending; // Points inside doomcom @@ -385,6 +376,8 @@ extern boolean dedicated; // For dedicated server extern UINT16 software_MAXPACKETLENGTH; extern boolean acceptnewnode; extern SINT8 servernode; +extern tic_t maketic; +extern tic_t neededtic; void Command_Ping_f(void); extern tic_t connectiontimeout; @@ -411,6 +404,8 @@ void SendKick(UINT8 playernum, UINT8 msg); // Create any new ticcmds and broadcast to other players. void NetUpdate(void); +void GetPackets(void); + void SV_StartSinglePlayerServer(void); void SV_SpawnServer(void); void SV_StopServer(void); @@ -419,9 +414,8 @@ void CL_AddSplitscreenPlayer(void); void CL_RemoveSplitscreenPlayer(void); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); -void CL_QueryServerList(msg_server_t *list); -void CL_UpdateServerList(boolean internetsearch, INT32 room); void CL_RemovePlayer(INT32 playernum, kickreason_t reason); +void CL_LoadReceivedSavegame(boolean reloading); // Is there a game running boolean Playing(void); diff --git a/src/netcode/http-mserv.c b/src/netcode/http-mserv.c index 72dc1cafb..68e46b52a 100644 --- a/src/netcode/http-mserv.c +++ b/src/netcode/http-mserv.c @@ -20,6 +20,7 @@ Documentation available here. #include "../doomdef.h" #include "d_clisrv.h" +#include "client_connection.h" #include "../command.h" #include "../m_argv.h" #include "../m_menu.h" diff --git a/src/netcode/mserv.c b/src/netcode/mserv.c index 78301f4d9..f603d78e5 100644 --- a/src/netcode/mserv.c +++ b/src/netcode/mserv.c @@ -20,6 +20,7 @@ #include "../command.h" #include "../i_threads.h" #include "mserv.h" +#include "client_connection.h" #include "../m_menu.h" #include "../z_zone.h" From 449d27749f6c245653db7d2846f12ab2dca34a40 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 31 Dec 2022 20:30:39 -0500 Subject: [PATCH 29/89] Fixes Issue #912 --- src/p_user.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index 4ca4e6c8a..0ef787641 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11266,6 +11266,11 @@ static void P_DoTailsOverlay(player_t *player, mobj_t *tails) tails->y = player->mo->y + P_ReturnThrustY(tails, tails->angle, FixedMul(backwards, tails->scale)); tails->z = player->mo->z + zoffs; P_SetThingPosition(tails); + + if (player->mo->flags2 & MF2_SHADOW) + tails->flags2 |= MF2_SHADOW; + else + tails->flags2 &= ~MF2_SHADOW; } // Metal Sonic's jet fume From e909f8ec12a707e90d83f380e60a8e6c46a2f4e3 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sat, 31 Dec 2022 21:06:39 -0500 Subject: [PATCH 30/89] Fixes Issue #711 --- src/p_map.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_map.c b/src/p_map.c index 5c8ccbb19..f738cb97d 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -264,11 +264,13 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) { INT32 pflags = object->player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // Not identical to below... UINT8 secondjump = object->player->secondjump; + UINT16 tailsfly = object->player->powers[pw_tailsfly]; if (object->player->pflags & PF_GLIDING) P_SetPlayerMobjState(object, S_PLAY_FALL); P_ResetPlayer(object->player); object->player->pflags |= pflags; object->player->secondjump = secondjump; + object->player->powers[pw_tailsfly] = tailsfly; } } From e472c551e112595f0836b34808a542c028763651 Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Sun, 1 Jan 2023 11:31:44 +0100 Subject: [PATCH 31/89] Re-add P_ThingOnSpecial3DFloor for Lua backwards compatibility --- src/lua_baselib.c | 13 +++++++++++++ src/p_spec.c | 23 +++++++++++++++++++++++ src/p_spec.h | 1 + 3 files changed, 37 insertions(+) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index c94e9e91e..92ba95f99 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2335,6 +2335,18 @@ static int lib_pMobjTouchingSectorSpecial(lua_State *L) return 1; } +static int lib_pThingOnSpecial3DFloor(lua_State *L) +{ + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + NOHUD + INLEVEL + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); + LUA_Deprecated(L, "P_ThingOnSpecial3DFloor", "P_MobjTouchingSectorSpecial\" or \"P_MobjTouchingSectorSpecialFlag"); + LUA_PushUserdata(L, P_ThingOnSpecial3DFloor(mo), META_SECTOR); + return 1; +} + static int lib_pMobjTouchingSectorSpecialFlag(lua_State *L) { mobj_t *mo = *((mobj_t**)luaL_checkudata(L, 1, META_MOBJ)); @@ -4213,6 +4225,7 @@ static luaL_Reg lib[] = { {"P_DoSuperTransformation",lib_pDoSuperTransformation}, {"P_ExplodeMissile",lib_pExplodeMissile}, {"P_MobjTouchingSectorSpecial",lib_pMobjTouchingSectorSpecial}, + {"P_ThingOnSpecial3DFloor",lib_pThingOnSpecial3DFloor}, {"P_MobjTouchingSectorSpecialFlag",lib_pMobjTouchingSectorSpecialFlag}, {"P_PlayerTouchingSectorSpecial",lib_pPlayerTouchingSectorSpecial}, {"P_PlayerTouchingSectorSpecialFlag",lib_pPlayerTouchingSectorSpecialFlag}, diff --git a/src/p_spec.c b/src/p_spec.c index 5c9caa82f..0bf11c53f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4180,6 +4180,29 @@ sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number) return NULL; } +// Deprecated in favor of P_MobjTouchingSectorSpecial +// Kept for Lua backwards compatibility only +sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo) +{ + ffloor_t *rover; + + for (rover = mo->subsector->sector->ffloors; rover; rover = rover->next) + { + if (!rover->master->frontsector->special) + continue; + + if (!(rover->fofflags & FOF_EXISTS)) + continue; + + if (!P_IsMobjTouching3DFloor(mo, rover, mo->subsector->sector)) + continue; + + return rover->master->frontsector; + } + + return NULL; +} + sector_t *P_MobjTouchingSectorSpecialFlag(mobj_t *mo, sectorspecialflags_t flag) { msecnode_t *node; diff --git a/src/p_spec.h b/src/p_spec.h index cd97efa1a..779afdd05 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -496,6 +496,7 @@ void P_SpawnSpecials(boolean fromnetsave); // every tic void P_UpdateSpecials(void); sector_t *P_MobjTouchingSectorSpecial(mobj_t *mo, INT32 section, INT32 number); +sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo); sector_t *P_MobjTouchingSectorSpecialFlag(mobj_t *mo, sectorspecialflags_t flag); sector_t *P_PlayerTouchingSectorSpecial(player_t *player, INT32 section, INT32 number); sector_t *P_PlayerTouchingSectorSpecialFlag(player_t *player, sectorspecialflags_t flag); From b461cb919bf37ae5a33b57c62d264538576b01a8 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 1 Jan 2023 11:45:09 +0100 Subject: [PATCH 32/89] Move server-side connection handling to a new file --- src/netcode/Sourcefile | 1 + src/netcode/d_clisrv.c | 475 +----------------------------- src/netcode/d_clisrv.h | 3 +- src/netcode/d_netcmd.c | 1 + src/netcode/server_connection.c | 497 ++++++++++++++++++++++++++++++++ src/netcode/server_connection.h | 29 ++ 6 files changed, 533 insertions(+), 473 deletions(-) create mode 100644 src/netcode/server_connection.c create mode 100644 src/netcode/server_connection.h diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile index c11b3d6c1..4177166a4 100644 --- a/src/netcode/Sourcefile +++ b/src/netcode/Sourcefile @@ -1,4 +1,5 @@ d_clisrv.c +server_connection.c client_connection.c d_net.c d_netcmd.c diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index f7a9260e1..c1264768e 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -49,6 +49,7 @@ #include "../lua_libs.h" #include "../md5.h" #include "../m_perfstats.h" +#include "server_connection.h" #include "client_connection.h" // @@ -79,15 +80,6 @@ netnode_t netnodes[MAXNETNODES]; // Server specific vars UINT8 playernode[MAXPLAYERS]; -char playeraddress[MAXPLAYERS][64]; - -// Minimum timeout for sending the savegame -// The actual timeout will be longer depending on the savegame length -tic_t jointimeout = (10*TICRATE); - -// Incremented by cv_joindelay when a client joins, decremented each tic. -// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. -static tic_t joindelay = 0; UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. @@ -100,7 +92,6 @@ tic_t maketic; static INT16 consistancy[BACKUPTICS]; -static UINT8 player_joining = false; UINT8 hu_redownloadinggamestate = 0; // true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks @@ -501,229 +492,6 @@ void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum) static INT16 Consistancy(void); -static INT32 FindRejoinerNum(SINT8 node) -{ - char strippednodeaddress[64]; - const char *nodeaddress; - char *port; - INT32 i; - - // Make sure there is no dead dress before proceeding to the stripping - if (!I_GetNodeAddress) - return -1; - nodeaddress = I_GetNodeAddress(node); - if (!nodeaddress) - return -1; - - // Strip the address of its port - strcpy(strippednodeaddress, nodeaddress); - port = strchr(strippednodeaddress, ':'); - if (port) - *port = '\0'; - - // Check if any player matches the stripped address - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX - && !strcmp(playeraddress[i], strippednodeaddress)) - return i; - } - - return -1; -} - -static UINT8 -GetRefuseReason (INT32 node) -{ - if (!node || FindRejoinerNum(node) != -1) - return 0; - else if (bannednode && bannednode[node]) - return REFUSE_BANNED; - else if (!cv_allownewplayer.value) - return REFUSE_JOINS_DISABLED; - else if (D_NumPlayers() >= cv_maxplayers.value) - return REFUSE_SLOTS_FULL; - else - return 0; -} - -static void SV_SendServerInfo(INT32 node, tic_t servertime) -{ - UINT8 *p; - - netbuffer->packettype = PT_SERVERINFO; - netbuffer->u.serverinfo._255 = 255; - netbuffer->u.serverinfo.packetversion = PACKETVERSION; - netbuffer->u.serverinfo.version = VERSION; - netbuffer->u.serverinfo.subversion = SUBVERSION; - strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION, - sizeof netbuffer->u.serverinfo.application); - // return back the time value so client can compute their ping - netbuffer->u.serverinfo.time = (tic_t)LONG(servertime); - netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); - - netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); - netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; - - netbuffer->u.serverinfo.refusereason = GetRefuseReason(node); - - strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], - sizeof netbuffer->u.serverinfo.gametypename); - netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; - netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); - netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0); - strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, - MAXSERVERNAME); - strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); - - M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); - - memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle); - - if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl) - { - char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle; - while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0') - { - if (!(*read & 0x80)) - { - *writ = toupper(*read); - writ++; - } - read++; - } - *writ = '\0'; - //strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); - } - else - strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32); - - if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - netbuffer->u.serverinfo.iszone = 1; - else - netbuffer->u.serverinfo.iszone = 0; - - if (mapheaderinfo[gamemap-1]) - netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum; - - p = PutFileNeeded(0); - - HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); -} - -static void SV_SendPlayerInfo(INT32 node) -{ - UINT8 i; - netbuffer->packettype = PT_PLAYERINFO; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - { - netbuffer->u.playerinfo[i].num = 255; // This slot is empty. - continue; - } - - netbuffer->u.playerinfo[i].num = i; - strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); - netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; - - //fetch IP address - //No, don't do that, you fuckface. - memset(netbuffer->u.playerinfo[i].address, 0, 4); - - if (G_GametypeHasTeams()) - { - if (!players[i].ctfteam) - netbuffer->u.playerinfo[i].team = 255; - else - netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam; - } - else - { - if (players[i].spectator) - netbuffer->u.playerinfo[i].team = 255; - else - netbuffer->u.playerinfo[i].team = 0; - } - - netbuffer->u.playerinfo[i].score = LONG(players[i].score); - netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); - netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin -#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself - % 3 -#endif - ); - - // Extra data - netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; - - if (players[i].pflags & PF_TAGIT) - netbuffer->u.playerinfo[i].data |= 0x20; - - if (players[i].gotflag) - netbuffer->u.playerinfo[i].data |= 0x40; - - if (players[i].powers[pw_super]) - netbuffer->u.playerinfo[i].data |= 0x80; - } - - HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); -} - -/** Sends a PT_SERVERCFG packet - * - * \param node The destination - * \return True if the packet was successfully sent - * - */ -static boolean SV_SendServerConfig(INT32 node) -{ - boolean waspacketsent; - - netbuffer->packettype = PT_SERVERCFG; - - netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; - netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); - netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); - netbuffer->u.servercfg.clientnode = (UINT8)node; - netbuffer->u.servercfg.gamestate = (UINT8)gamestate; - netbuffer->u.servercfg.gametype = (UINT8)gametype; - netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; - - memcpy(netbuffer->u.servercfg.server_context, server_context, 8); - - { - const size_t len = sizeof (serverconfig_pak); - -#ifdef DEBUGFILE - if (debugfile) - { - fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n", - sizeu1(len), node); - } -#endif - - waspacketsent = HSendPacket(node, true, 0, len); - } - -#ifdef DEBUGFILE - if (debugfile) - { - if (waspacketsent) - { - fprintf(debugfile, "ServerConfig Packet was sent\n"); - } - else - { - fprintf(debugfile, "ServerConfig Packet could not be sent right now\n"); - } - } -#endif - - return waspacketsent; -} - #define SAVEGAMESIZE (768*1024) static boolean SV_ResendingSavegameToAnyone(void) @@ -736,7 +504,7 @@ static boolean SV_ResendingSavegameToAnyone(void) return false; } -static void SV_SendSaveGame(INT32 node, boolean resending) +void SV_SendSaveGame(INT32 node, boolean resending) { size_t length, compressedlen; UINT8 *savebuffer; @@ -1190,7 +958,7 @@ static void Command_connect(void) CL_ConnectToServer(); } -static void ResetNode(INT32 node) +void ResetNode(INT32 node) { memset(&netnodes[node], 0, sizeof(*netnodes)); netnodes[node].player = -1; @@ -2020,17 +1788,6 @@ void D_QuitNetGame(void) #endif } -// Adds a node to the game (player will follow at map change or at savegame....) -static inline void SV_AddNode(INT32 node) -{ - netnodes[node].tic = gametic; - netnodes[node].supposedtic = gametic; - // little hack because the server connects to itself and puts - // nodeingame when connected not here - if (node) - netnodes[node].ingame = true; -} - // Xcmd XD_ADDPLAYER static void Got_AddPlayer(UINT8 **p, INT32 playernum) { @@ -2163,56 +1920,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) LUA_HookInt(newplayernum, HOOK(PlayerJoin)); } -static void SV_AddPlayer(SINT8 node, const char *name) -{ - INT32 n; - UINT8 buf[2 + MAXPLAYERNAME]; - UINT8 *p; - INT32 newplayernum; - - newplayernum = FindRejoinerNum(node); - if (newplayernum == -1) - { - // search for a free playernum - // we can't use playeringame since it is not updated here - for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) - { - if (playeringame[newplayernum]) - continue; - for (n = 0; n < MAXNETNODES; n++) - if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum) - break; - if (n == MAXNETNODES) - break; - } - } - - // should never happen since we check the playernum - // before accepting the join - I_Assert(newplayernum < MAXPLAYERS); - - playernode[newplayernum] = (UINT8)node; - - p = buf + 2; - buf[0] = (UINT8)node; - buf[1] = newplayernum; - if (netnodes[node].numplayers < 1) - { - netnodes[node].player = newplayernum; - } - else - { - netnodes[node].player2 = newplayernum; - buf[1] |= 0x80; - } - WRITESTRINGN(p, name, MAXPLAYERNAME); - netnodes[node].numplayers++; - - SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); - - DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); -} - void CL_AddSplitscreenPlayer(void) { if (cl_mode == CL_CONNECTED) @@ -2298,15 +2005,6 @@ void SV_StartSinglePlayerServer(void) multiplayer = true; } -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); - Net_CloseConnection(node); -} - // used at txtcmds received to check packetsize bound static size_t TotalTextCmdPerTic(tic_t tic) { @@ -2323,139 +2021,6 @@ static size_t TotalTextCmdPerTic(tic_t tic) return total; } -static const char * -GetRefuseMessage (SINT8 node, INT32 rejoinernum) -{ - clientconfig_pak *cc = &netbuffer->u.clientcfg; - - boolean rejoining = (rejoinernum != -1); - - if (!node)/* server connecting to itself */ - return NULL; - - if ( - cc->modversion != MODVERSION || - strncmp(cc->application, SRB2APPLICATION, - sizeof cc->application) - ){ - return/* this is probably client's fault */ - "Incompatible."; - } - else if (bannednode && bannednode[node]) - { - return - "You have been banned\n" - "from the server."; - } - else if (cc->localplayers != 1) - { - return - "Wrong player count."; - } - - if (!rejoining) - { - if (!cv_allownewplayer.value) - { - return - "The server is not accepting\n" - "joins for the moment."; - } - else if (D_NumPlayers() >= cv_maxplayers.value) - { - return va( - "Maximum players reached: %d", - cv_maxplayers.value); - } - } - - if (luafiletransfers) - { - return - "The serveris broadcasting a file\n" - "requested by a Lua script.\n" - "Please wait a bit and then\n" - "try rejoining."; - } - - if (netgame) - { - const tic_t th = 2 * cv_joindelay.value * TICRATE; - - if (joindelay > th) - { - return va( - "Too many people are connecting.\n" - "Please wait %d seconds and then\n" - "try rejoining.", - (joindelay - th) / TICRATE); - } - } - - return NULL; -} - -/** Called when a PT_CLIENTJOIN packet is received - * - * \param node The packet sender - * - */ -static void HandleConnect(SINT8 node) -{ - char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; - INT32 numplayers = netbuffer->u.clientcfg.localplayers; - INT32 rejoinernum; - INT32 i; - - // Ignore duplicate packets - if (netnodes[node].ingame) - return; - - rejoinernum = FindRejoinerNum(node); - - const char *refuse = GetRefuseMessage(node, rejoinernum); - if (refuse) - { - SV_SendRefuse(node, refuse); - return; - } - - for (i = 0; i < numplayers; i++) - { - strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); - if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) - { - SV_SendRefuse(node, "Bad player name"); - return; - } - } - - SV_AddNode(node); - - if (!SV_SendServerConfig(node)) - { - /// \note Shouldn't SV_SendRefuse be called before ResetNode? - ResetNode(node); - SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); - /// \todo fix this !!! - return; // restart the while - } - DEBFILE("new node joined\n"); - - if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) - { - SV_SendSaveGame(node, false); // send a complete game state - DEBFILE("send savegame\n"); - } - - // Splitscreen can allow 2 players in one node - for (i = 0; i < numplayers; i++) - SV_AddPlayer(node, names[i]); - - joindelay += cv_joindelay.value * TICRATE; - player_joining = true; -} - /** Called when a PT_SERVERSHUTDOWN packet is received * * \param node The packet sender (should be the server) @@ -2486,40 +2051,6 @@ static void HandleTimeout(SINT8 node) M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); } -static void PT_AskInfoViaMS(SINT8 node) -{ - Net_CloseConnection(node); -} - -static void PT_TellFilesNeeded(SINT8 node) -{ - if (server && serverrunning) - { - UINT8 *p; - INT32 firstfile = netbuffer->u.filesneedednum; - - netbuffer->packettype = PT_MOREFILESNEEDED; - netbuffer->u.filesneededcfg.first = firstfile; - netbuffer->u.filesneededcfg.more = 0; - - p = PutFileNeeded(firstfile); - - HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); - } - else // Shouldn't get this if you aren't the server...? - Net_CloseConnection(node); -} - -static void PT_AskInfo(SINT8 node) -{ - if (server && serverrunning) - { - SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); - SV_SendPlayerInfo(node); // Send extra info - } - Net_CloseConnection(node); -} - static void PT_ClientCmd(SINT8 node, INT32 netconsole) { tic_t realend, realstart; diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 3b0690a79..b8da645ba 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -381,7 +381,6 @@ extern tic_t neededtic; void Command_Ping_f(void); extern tic_t connectiontimeout; -extern tic_t jointimeout; extern UINT16 pingmeasurecount; extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; @@ -405,11 +404,13 @@ void SendKick(UINT8 playernum, UINT8 msg); void NetUpdate(void); void GetPackets(void); +void ResetNode(INT32 node); void SV_StartSinglePlayerServer(void); void SV_SpawnServer(void); void SV_StopServer(void); void SV_ResetServer(void); +void SV_SendSaveGame(INT32 node, boolean resending); void CL_AddSplitscreenPlayer(void); void CL_RemoveSplitscreenPlayer(void); void CL_Reset(void); diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c index ed310805d..fe4cf22bd 100644 --- a/src/netcode/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -36,6 +36,7 @@ #include "../p_spec.h" #include "../m_cheat.h" #include "d_clisrv.h" +#include "server_connection.h" #include "d_net.h" #include "../v_video.h" #include "../d_main.h" diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c new file mode 100644 index 000000000..9e397b0f3 --- /dev/null +++ b/src/netcode/server_connection.c @@ -0,0 +1,497 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 server_connection.c +/// \brief Server-side part of connection handling + +#include "server_connection.h" +#include "i_net.h" +#include "d_clisrv.h" +#include "d_netfil.h" +#include "mserv.h" +#include "../byteptr.h" +#include "../g_game.h" +#include "../g_state.h" +#include "../p_setup.h" +#include "../p_tick.h" +#include "../doomstat.h" + +// Minimum timeout for sending the savegame +// The actual timeout will be longer depending on the savegame length +tic_t jointimeout = (10*TICRATE); + +// Incremented by cv_joindelay when a client joins, decremented each tic. +// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. +tic_t joindelay = 0; + +// Minimum timeout for sending the savegame +// The actual timeout will be longer depending on the savegame length +char playeraddress[MAXPLAYERS][64]; + +UINT8 player_joining = false; + +static INT32 FindRejoinerNum(SINT8 node) +{ + char strippednodeaddress[64]; + const char *nodeaddress; + char *port; + INT32 i; + + // Make sure there is no dead dress before proceeding to the stripping + if (!I_GetNodeAddress) + return -1; + nodeaddress = I_GetNodeAddress(node); + if (!nodeaddress) + return -1; + + // Strip the address of its port + strcpy(strippednodeaddress, nodeaddress); + port = strchr(strippednodeaddress, ':'); + if (port) + *port = '\0'; + + // Check if any player matches the stripped address + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX + && !strcmp(playeraddress[i], strippednodeaddress)) + return i; + } + + return -1; +} + +static UINT8 +GetRefuseReason (INT32 node) +{ + if (!node || FindRejoinerNum(node) != -1) + return 0; + else if (bannednode && bannednode[node]) + return REFUSE_BANNED; + else if (!cv_allownewplayer.value) + return REFUSE_JOINS_DISABLED; + else if (D_NumPlayers() >= cv_maxplayers.value) + return REFUSE_SLOTS_FULL; + else + return 0; +} + +static void SV_SendServerInfo(INT32 node, tic_t servertime) +{ + UINT8 *p; + + netbuffer->packettype = PT_SERVERINFO; + netbuffer->u.serverinfo._255 = 255; + netbuffer->u.serverinfo.packetversion = PACKETVERSION; + netbuffer->u.serverinfo.version = VERSION; + netbuffer->u.serverinfo.subversion = SUBVERSION; + strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION, + sizeof netbuffer->u.serverinfo.application); + // return back the time value so client can compute their ping + netbuffer->u.serverinfo.time = (tic_t)LONG(servertime); + netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); + + netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); + netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; + + netbuffer->u.serverinfo.refusereason = GetRefuseReason(node); + + strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], + sizeof netbuffer->u.serverinfo.gametypename); + netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; + netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); + netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0); + strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, + MAXSERVERNAME); + strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); + + M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); + + memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle); + + if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl) + { + char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle; + while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0') + { + if (!(*read & 0x80)) + { + *writ = toupper(*read); + writ++; + } + read++; + } + *writ = '\0'; + //strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); + } + else + strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32); + + if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + netbuffer->u.serverinfo.iszone = 1; + else + netbuffer->u.serverinfo.iszone = 0; + + if (mapheaderinfo[gamemap-1]) + netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum; + + p = PutFileNeeded(0); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); +} + +static void SV_SendPlayerInfo(INT32 node) +{ + UINT8 i; + netbuffer->packettype = PT_PLAYERINFO; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + { + netbuffer->u.playerinfo[i].num = 255; // This slot is empty. + continue; + } + + netbuffer->u.playerinfo[i].num = i; + strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); + netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; + + //fetch IP address + //No, don't do that, you fuckface. + memset(netbuffer->u.playerinfo[i].address, 0, 4); + + if (G_GametypeHasTeams()) + { + if (!players[i].ctfteam) + netbuffer->u.playerinfo[i].team = 255; + else + netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam; + } + else + { + if (players[i].spectator) + netbuffer->u.playerinfo[i].team = 255; + else + netbuffer->u.playerinfo[i].team = 0; + } + + netbuffer->u.playerinfo[i].score = LONG(players[i].score); + netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); + netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin +#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself + % 3 +#endif + ); + + // Extra data + netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; + + if (players[i].pflags & PF_TAGIT) + netbuffer->u.playerinfo[i].data |= 0x20; + + if (players[i].gotflag) + netbuffer->u.playerinfo[i].data |= 0x40; + + if (players[i].powers[pw_super]) + netbuffer->u.playerinfo[i].data |= 0x80; + } + + HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); +} + +/** Sends a PT_SERVERCFG packet + * + * \param node The destination + * \return True if the packet was successfully sent + * + */ +static boolean SV_SendServerConfig(INT32 node) +{ + boolean waspacketsent; + + netbuffer->packettype = PT_SERVERCFG; + + netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; + netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); + netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); + netbuffer->u.servercfg.clientnode = (UINT8)node; + netbuffer->u.servercfg.gamestate = (UINT8)gamestate; + netbuffer->u.servercfg.gametype = (UINT8)gametype; + netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; + + memcpy(netbuffer->u.servercfg.server_context, server_context, 8); + + { + const size_t len = sizeof (serverconfig_pak); + +#ifdef DEBUGFILE + if (debugfile) + { + fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n", + sizeu1(len), node); + } +#endif + + waspacketsent = HSendPacket(node, true, 0, len); + } + +#ifdef DEBUGFILE + if (debugfile) + { + if (waspacketsent) + { + fprintf(debugfile, "ServerConfig Packet was sent\n"); + } + else + { + fprintf(debugfile, "ServerConfig Packet could not be sent right now\n"); + } + } +#endif + + return waspacketsent; +} + +// Adds a node to the game (player will follow at map change or at savegame....) +static inline void SV_AddNode(INT32 node) +{ + netnodes[node].tic = gametic; + netnodes[node].supposedtic = gametic; + // little hack because the server connects to itself and puts + // nodeingame when connected not here + if (node) + netnodes[node].ingame = true; +} + +static void SV_AddPlayer(SINT8 node, const char *name) +{ + INT32 n; + UINT8 buf[2 + MAXPLAYERNAME]; + UINT8 *p; + INT32 newplayernum; + + newplayernum = FindRejoinerNum(node); + if (newplayernum == -1) + { + // search for a free playernum + // we can't use playeringame since it is not updated here + for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) + { + if (playeringame[newplayernum]) + continue; + for (n = 0; n < MAXNETNODES; n++) + if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum) + break; + if (n == MAXNETNODES) + break; + } + } + + // should never happen since we check the playernum + // before accepting the join + I_Assert(newplayernum < MAXPLAYERS); + + playernode[newplayernum] = (UINT8)node; + + p = buf + 2; + buf[0] = (UINT8)node; + buf[1] = newplayernum; + if (netnodes[node].numplayers < 1) + { + netnodes[node].player = newplayernum; + } + else + { + netnodes[node].player2 = newplayernum; + buf[1] |= 0x80; + } + WRITESTRINGN(p, name, MAXPLAYERNAME); + netnodes[node].numplayers++; + + SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); + + DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); +} + +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); + Net_CloseConnection(node); +} + +static const char * +GetRefuseMessage (SINT8 node, INT32 rejoinernum) +{ + clientconfig_pak *cc = &netbuffer->u.clientcfg; + + boolean rejoining = (rejoinernum != -1); + + if (!node)/* server connecting to itself */ + return NULL; + + if ( + cc->modversion != MODVERSION || + strncmp(cc->application, SRB2APPLICATION, + sizeof cc->application) + ){ + return/* this is probably client's fault */ + "Incompatible."; + } + else if (bannednode && bannednode[node]) + { + return + "You have been banned\n" + "from the server."; + } + else if (cc->localplayers != 1) + { + return + "Wrong player count."; + } + + if (!rejoining) + { + if (!cv_allownewplayer.value) + { + return + "The server is not accepting\n" + "joins for the moment."; + } + else if (D_NumPlayers() >= cv_maxplayers.value) + { + return va( + "Maximum players reached: %d", + cv_maxplayers.value); + } + } + + if (luafiletransfers) + { + return + "The serveris broadcasting a file\n" + "requested by a Lua script.\n" + "Please wait a bit and then\n" + "try rejoining."; + } + + if (netgame) + { + const tic_t th = 2 * cv_joindelay.value * TICRATE; + + if (joindelay > th) + { + return va( + "Too many people are connecting.\n" + "Please wait %d seconds and then\n" + "try rejoining.", + (joindelay - th) / TICRATE); + } + } + + return NULL; +} + +/** Called when a PT_CLIENTJOIN packet is received + * + * \param node The packet sender + * + */ +void HandleConnect(SINT8 node) +{ + char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; + INT32 numplayers = netbuffer->u.clientcfg.localplayers; + INT32 rejoinernum; + INT32 i; + + // Ignore duplicate packets + if (netnodes[node].ingame) + return; + + rejoinernum = FindRejoinerNum(node); + + const char *refuse = GetRefuseMessage(node, rejoinernum); + if (refuse) + { + SV_SendRefuse(node, refuse); + return; + } + + for (i = 0; i < numplayers; i++) + { + strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); + if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) + { + SV_SendRefuse(node, "Bad player name"); + return; + } + } + + SV_AddNode(node); + + if (!SV_SendServerConfig(node)) + { + /// \note Shouldn't SV_SendRefuse be called before ResetNode? + ResetNode(node); + SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); + /// \todo fix this !!! + return; // restart the while + } + DEBFILE("new node joined\n"); + + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) + { + SV_SendSaveGame(node, false); // send a complete game state + DEBFILE("send savegame\n"); + } + + // Splitscreen can allow 2 players in one node + for (i = 0; i < numplayers; i++) + SV_AddPlayer(node, names[i]); + + joindelay += cv_joindelay.value * TICRATE; + player_joining = true; +} + +void PT_AskInfoViaMS(SINT8 node) +{ + Net_CloseConnection(node); +} + +void PT_TellFilesNeeded(SINT8 node) +{ + if (server && serverrunning) + { + UINT8 *p; + INT32 firstfile = netbuffer->u.filesneedednum; + + netbuffer->packettype = PT_MOREFILESNEEDED; + netbuffer->u.filesneededcfg.first = firstfile; + netbuffer->u.filesneededcfg.more = 0; + + p = PutFileNeeded(firstfile); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); + } + else // Shouldn't get this if you aren't the server...? + Net_CloseConnection(node); +} + +void PT_AskInfo(SINT8 node) +{ + if (server && serverrunning) + { + SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); + SV_SendPlayerInfo(node); // Send extra info + } + Net_CloseConnection(node); +} diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h new file mode 100644 index 000000000..4204db2e6 --- /dev/null +++ b/src/netcode/server_connection.h @@ -0,0 +1,29 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 server_connection.h +/// \brief Server-side part of connection handling + +#ifndef __D_SERVER_CONNECTION__ +#define __D_SERVER_CONNECTION__ + +#include "../doomdef.h" +#include "../doomtype.h" + +void HandleConnect(SINT8 node); +void PT_AskInfoViaMS(SINT8 node); +void PT_TellFilesNeeded(SINT8 node); +void PT_AskInfo(SINT8 node); + +extern tic_t jointimeout; +extern tic_t joindelay; +extern char playeraddress[MAXPLAYERS][64]; +extern UINT8 player_joining; + +#endif From 7d53e4647b7317cb90708e86b7f8794241a307e1 Mon Sep 17 00:00:00 2001 From: Arthur Date: Sun, 1 Jan 2023 21:07:15 -0500 Subject: [PATCH 33/89] Fixes issue #568 --- src/p_enemy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/p_enemy.c b/src/p_enemy.c index ece4f3814..3053e1fb8 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13460,6 +13460,9 @@ static boolean PIT_DustDevilLaunch(mobj_t *thing) if (!player) return true; + if (player->spectator) + return true; + if (player->powers[pw_carry] != CR_DUSTDEVIL && (player->powers[pw_ignorelatch] & (1<<15))) return true; From 7c2fe20cd5aa9badc4d7d7fed9561e8bf966997b Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 5 Jan 2023 22:51:17 +0100 Subject: [PATCH 34/89] Move tic and net command handling to new files --- src/blua/liolib.c | 1 + src/command.c | 1 + src/g_game.c | 1 + src/hu_stuff.c | 1 + src/lua_consolelib.c | 1 + src/netcode/Sourcefile | 2 + src/netcode/d_clisrv.c | 878 +------------------------------- src/netcode/d_clisrv.h | 12 +- src/netcode/d_net.c | 1 + src/netcode/d_netcmd.c | 1 + src/netcode/d_netfil.c | 1 + src/netcode/net_command.c | 315 ++++++++++++ src/netcode/net_command.h | 62 +++ src/netcode/server_connection.c | 1 + src/netcode/tic_command.c | 504 ++++++++++++++++++ src/netcode/tic_command.h | 44 ++ src/p_inter.c | 1 + src/p_mobj.c | 1 + src/p_setup.c | 2 + src/p_tick.c | 1 + src/p_user.c | 1 + 21 files changed, 955 insertions(+), 877 deletions(-) create mode 100644 src/netcode/net_command.c create mode 100644 src/netcode/net_command.h create mode 100644 src/netcode/tic_command.c create mode 100644 src/netcode/tic_command.h diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 8a6354121..bce8e87cc 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -20,6 +20,7 @@ #include "../i_system.h" #include "../g_game.h" #include "../netcode/d_netfil.h" +#include "../netcode/net_command.h" #include "../lua_libs.h" #include "../byteptr.h" #include "../lua_script.h" diff --git a/src/command.c b/src/command.c index 4e8989fd3..a87a04dcc 100644 --- a/src/command.c +++ b/src/command.c @@ -29,6 +29,7 @@ #include "p_saveg.h" #include "g_game.h" // for player_names #include "netcode/d_netcmd.h" +#include "netcode/net_command.h" #include "hu_stuff.h" #include "p_setup.h" #include "lua_script.h" diff --git a/src/g_game.c b/src/g_game.c index 93b8eb936..17dbff7d3 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -16,6 +16,7 @@ #include "d_main.h" #include "d_player.h" #include "netcode/d_clisrv.h" +#include "netcode/net_command.h" #include "f_finale.h" #include "p_setup.h" #include "p_saveg.h" diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e5cf5aeb1..e08351a8a 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -20,6 +20,7 @@ #include "m_misc.h" // word jumping #include "netcode/d_clisrv.h" +#include "netcode/net_command.h" #include "g_game.h" #include "g_input.h" diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 816051199..a1bdf380d 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -16,6 +16,7 @@ #include "g_game.h" #include "byteptr.h" #include "z_zone.h" +#include "netcode/net_command.h" #include "lua_script.h" #include "lua_libs.h" diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile index 4177166a4..1039b218a 100644 --- a/src/netcode/Sourcefile +++ b/src/netcode/Sourcefile @@ -1,6 +1,8 @@ d_clisrv.c server_connection.c client_connection.c +tic_command.c +net_command.c d_net.c d_netcmd.c d_netfil.c diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index c1264768e..06e6da751 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -51,6 +51,8 @@ #include "../m_perfstats.h" #include "server_connection.h" #include "client_connection.h" +#include "tic_command.h" +#include "net_command.h" // // NETWORKING @@ -86,11 +88,9 @@ UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. tic_t servermaxping = 800; // server's max ping. Defaults to 800 -static tic_t firstticstosend; // min of the nettics -static tic_t tictoclear = 0; // optimize d_clearticcmd tic_t maketic; -static INT16 consistancy[BACKUPTICS]; +INT16 consistancy[BACKUPTICS]; UINT8 hu_redownloadinggamestate = 0; @@ -101,14 +101,9 @@ UINT8 adminpassmd5[16]; boolean adminpasswordset = false; // Client specific -static ticcmd_t localcmds; -static ticcmd_t localcmds2; -static boolean cl_packetmissed; // here it is for the secondary local player (splitscreen) static boolean cl_redownloadinggamestate = false; -static UINT8 localtextcmd[MAXTEXTCMD]; -static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen tic_t neededtic; SINT8 servernode = 0; // the number of the server node @@ -116,385 +111,19 @@ SINT8 servernode = 0; // the number of the server node /// \todo WORK! boolean acceptnewnode = true; -// engine - -// Must be a power of two -#define TEXTCMD_HASH_SIZE 4 - -typedef struct textcmdplayer_s -{ - INT32 playernum; - UINT8 cmd[MAXTEXTCMD]; - struct textcmdplayer_s *next; -} textcmdplayer_t; - -typedef struct textcmdtic_s -{ - tic_t tic; - textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE]; - struct textcmdtic_s *next; -} textcmdtic_t; - -ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; -static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; - - consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); -static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) -{ - const size_t d = n / sizeof(ticcmd_t); - const size_t r = n % sizeof(ticcmd_t); - UINT8 *ret = dest; - - if (r) - M_Memcpy(dest, src, n); - else if (d) - G_MoveTiccmd(dest, src, d); - return ret+n; -} - -static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) -{ - const size_t d = n / sizeof(ticcmd_t); - const size_t r = n % sizeof(ticcmd_t); - UINT8 *ret = src; - - if (r) - M_Memcpy(dest, src, n); - else if (d) - G_MoveTiccmd(dest, src, d); - return ret+n; -} - - - // Some software don't support largest packet // (original sersetup, not exactely, but the probability of sending a packet // of 512 bytes is like 0.1) UINT16 software_MAXPACKETLENGTH; -/** Guesses the full value of a tic from its lowest byte, for a specific node - * - * \param low The lowest byte of the tic value - * \param node The node to deduce the tic for - * \return The full tic value - * - */ -tic_t ExpandTics(INT32 low, INT32 node) -{ - INT32 delta; - - delta = low - (netnodes[node].tic & UINT8_MAX); - - if (delta >= -64 && delta <= 64) - return (netnodes[node].tic & ~UINT8_MAX) + low; - else if (delta > 64) - return (netnodes[node].tic & ~UINT8_MAX) - 256 + low; - else //if (delta < -64) - return (netnodes[node].tic & ~UINT8_MAX) + 256 + low; -} - -// ----------------------------------------------------------------- -// Some extra data function for handle textcmd buffer -// ----------------------------------------------------------------- - -static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum); - -void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) -{ -#ifdef PARANOIA - if (id >= MAXNETXCMD) - I_Error("Command id %d too big", id); - if (listnetxcmd[id] != 0) - I_Error("Command id %d already used", id); -#endif - listnetxcmd[id] = cmd_f; -} - -void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam) -{ - if (localtextcmd[0]+2+nparam > MAXTEXTCMD) - { - // for future reference: if (cv_debug) != debug disabled. - CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam)); - return; - } - localtextcmd[0]++; - localtextcmd[localtextcmd[0]] = (UINT8)id; - if (param && nparam) - { - M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam); - localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam); - } -} - -// splitscreen player -void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam) -{ - if (localtextcmd2[0]+2+nparam > MAXTEXTCMD) - { - I_Error("No more place in the buffer for netcmd %d\n",id); - return; - } - localtextcmd2[0]++; - localtextcmd2[localtextcmd2[0]] = (UINT8)id; - if (param && nparam) - { - M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam); - localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam); - } -} - -UINT8 GetFreeXCmdSize(void) -{ - // -1 for the size and another -1 for the ID. - return (UINT8)(localtextcmd[0] - 2); -} - -// Frees all textcmd memory for the specified tic -static void D_FreeTextcmd(tic_t tic) -{ - textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdtic_t *textcmdtic = *tctprev; - - while (textcmdtic && textcmdtic->tic != tic) - { - tctprev = &textcmdtic->next; - textcmdtic = textcmdtic->next; - } - - if (textcmdtic) - { - INT32 i; - - // Remove this tic from the list. - *tctprev = textcmdtic->next; - - // Free all players. - for (i = 0; i < TEXTCMD_HASH_SIZE; i++) - { - textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i]; - - while (textcmdplayer) - { - textcmdplayer_t *tcpnext = textcmdplayer->next; - Z_Free(textcmdplayer); - textcmdplayer = tcpnext; - } - } - - // Free this tic's own memory. - Z_Free(textcmdtic); - } -} - -// Gets the buffer for the specified ticcmd, or NULL if there isn't one -static UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum) -{ - textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next; - - // Do we have an entry for the tic? If so, look for player. - if (textcmdtic) - { - textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; - while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next; - - if (textcmdplayer) return textcmdplayer->cmd; - } - - return NULL; -} - -// Gets the buffer for the specified ticcmd, creating one if necessary -static UINT8* D_GetTextcmd(tic_t tic, INT32 playernum) -{ - textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdplayer_t *textcmdplayer, **tcpprev; - - // Look for the tic. - while (textcmdtic && textcmdtic->tic != tic) - { - tctprev = &textcmdtic->next; - textcmdtic = textcmdtic->next; - } - - // If we don't have an entry for the tic, make it. - if (!textcmdtic) - { - textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL); - textcmdtic->tic = tic; - } - - tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; - textcmdplayer = *tcpprev; - - // Look for the player. - while (textcmdplayer && textcmdplayer->playernum != playernum) - { - tcpprev = &textcmdplayer->next; - textcmdplayer = textcmdplayer->next; - } - - // If we don't have an entry for the player, make it. - if (!textcmdplayer) - { - textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL); - textcmdplayer->playernum = playernum; - } - - return textcmdplayer->cmd; -} - -static void ExtraDataTicker(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] || i == 0) - { - UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i); - - if (bufferstart) - { - UINT8 *curpos = bufferstart; - UINT8 *bufferend = &curpos[curpos[0]+1]; - - curpos++; - while (curpos < bufferend) - { - if (*curpos < MAXNETXCMD && listnetxcmd[*curpos]) - { - const UINT8 id = *curpos; - curpos++; - DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); - (listnetxcmd[id])(&curpos, i); - DEBFILE("done\n"); - } - else - { - if (server) - { - SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); - } - CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); - break; - } - } - } - } - - // If you are a client, you can safely forget the net commands for this tic - // If you are the server, you need to remember them until every client has been acknowledged, - // because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it - if (client) - D_FreeTextcmd(gametic); -} - -static void D_Clearticcmd(tic_t tic) -{ - INT32 i; - - D_FreeTextcmd(tic); - - for (i = 0; i < MAXPLAYERS; i++) - netcmds[tic%BACKUPTICS][i].angleturn = 0; - - DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); -} - -void D_ResetTiccmds(void) -{ - INT32 i; - - memset(&localcmds, 0, sizeof(ticcmd_t)); - memset(&localcmds2, 0, sizeof(ticcmd_t)); - - // Reset the net command list - for (i = 0; i < TEXTCMD_HASH_SIZE; i++) - while (textcmds[i]) - D_Clearticcmd(textcmds[i]->tic); -} - -void SendKick(UINT8 playernum, UINT8 msg) -{ - UINT8 buf[2]; - - if (!(server && cv_rejointimeout.value)) - msg &= ~KICK_MSG_KEEP_BODY; - - buf[0] = playernum; - buf[1] = msg; - SendNetXCmd(XD_KICK, &buf, 2); -} - -// ----------------------------------------------------------------- -// end of extra data function -// ----------------------------------------------------------------- - -// ----------------------------------------------------------------- -// extra data function for lmps -// ----------------------------------------------------------------- - -// if extradatabit is set, after the ziped tic you find this: -// -// type | description -// ---------+-------------- -// byte | size of the extradata -// byte | the extradata (xd) bits: see XD_... -// with this byte you know what parameter folow -// if (xd & XDNAMEANDCOLOR) -// byte | color -// char[MAXPLAYERNAME] | name of the player -// endif -// if (xd & XD_WEAPON_PREF) -// byte | original weapon switch: boolean, true if use the old -// | weapon switch methode -// char[NUMWEAPONS] | the weapon switch priority -// byte | autoaim: true if use the old autoaim system -// endif -/*boolean AddLmpExtradata(UINT8 **demo_point, INT32 playernum) -{ - UINT8 *textcmd = D_GetExistingTextcmd(gametic, playernum); - - if (!textcmd) - return false; - - M_Memcpy(*demo_point, textcmd, textcmd[0]+1); - *demo_point += textcmd[0]+1; - return true; -} - -void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum) -{ - UINT8 nextra; - UINT8 *textcmd; - - if (!demo_pointer) - return; - - textcmd = D_GetTextcmd(gametic, playernum); - nextra = **demo_pointer; - M_Memcpy(textcmd, *demo_pointer, nextra + 1); - // increment demo pointer - *demo_pointer += nextra + 1; -}*/ - -// ----------------------------------------------------------------- -// end extra data function for lmps -// ----------------------------------------------------------------- - -static INT16 Consistancy(void); - #define SAVEGAMESIZE (768*1024) -static boolean SV_ResendingSavegameToAnyone(void) +boolean SV_ResendingSavegameToAnyone(void) { INT32 i; @@ -2005,22 +1634,6 @@ void SV_StartSinglePlayerServer(void) multiplayer = true; } -// used at txtcmds received to check packetsize bound -static size_t TotalTextCmdPerTic(tic_t tic) -{ - INT32 i; - size_t total = 1; // num of textcmds in the tic (ntextcmd byte) - - for (i = 0; i < MAXPLAYERS; i++) - { - UINT8 *textcmd = D_GetExistingTextcmd(tic, i); - if ((!i || playeringame[i]) && textcmd) - total += 2 + textcmd[0]; // "+2" for size and playernum - } - - return total; -} - /** Called when a PT_SERVERSHUTDOWN packet is received * * \param node The packet sender (should be the server) @@ -2051,168 +1664,6 @@ static void HandleTimeout(SINT8 node) M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); } -static void PT_ClientCmd(SINT8 node, INT32 netconsole) -{ - tic_t realend, realstart; - - if (client) - return; - - // To save bytes, only the low byte of tic numbers are sent - // Use ExpandTics to figure out what the rest of the bytes are - realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); - - if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS - || netbuffer->packettype == PT_NODEKEEPALIVEMIS - || netnodes[node].supposedtic < realend) - { - netnodes[node].supposedtic = realend; - } - // Discard out of order packet - if (netnodes[node].tic > realend) - { - DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic)); - return; - } - - // Update the nettics - netnodes[node].tic = realend; - - // Don't do anything for packets of type NODEKEEPALIVE? - if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE - || netbuffer->packettype == PT_NODEKEEPALIVEMIS) - return; - - // As long as clients send valid ticcmds, the server can keep running, so reset the timeout - /// \todo Use a separate cvar for that kind of timeout? - netnodes[node].freezetimeout = I_GetTime() + connectiontimeout; - - // Copy ticcmd - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); - - // Check ticcmd for "speed hacks" - if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE - || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); - //D_Clearticcmd(k); - - SendKick(netconsole, KICK_MSG_CON_FAIL); - return; - } - - // Splitscreen cmd - if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - && netnodes[node].player2 >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2], - &netbuffer->u.client2pak.cmd2, 1); - - // Check player consistancy during the level - if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL - && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) - && !SV_ResendingSavegameToAnyone() - && !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime()) - { - if (cv_resynchattempts.value) - { - // Tell the client we are about to resend them the gamestate - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - HSendPacket(node, true, 0, 0); - - netnodes[node].resendingsavegame = true; - - if (cv_blamecfail.value) - CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), - netconsole+1, player_names[netconsole], - consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy)); - DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - return; - } - else - { - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - return; - } - } -} - -static void PT_TextCmd(SINT8 node, INT32 netconsole) -{ - if (client) - return; - - // splitscreen special - if (netbuffer->packettype == PT_TEXTCMD2) - netconsole = netnodes[node].player2; - - if (netconsole < 0 || netconsole >= MAXPLAYERS) - Net_UnAcknowledgePacket(node); - else - { - size_t j; - tic_t tic = maketic; - UINT8 *textcmd; - - // ignore if the textcmd has a reported size of zero - // this shouldn't be sent at all - if (!netbuffer->u.textcmd[0]) - { - DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", - node, netconsole)); - Net_UnAcknowledgePacket(node); - return; - } - - // ignore if the textcmd size var is actually larger than it should be - // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength - if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) - { - DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", - netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), - node, netconsole)); - Net_UnAcknowledgePacket(node); - return; - } - - // check if tic that we are making isn't too large else we cannot send it :( - // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time - j = software_MAXPACKETLENGTH - - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE - + (doomcom->numslots+1)*sizeof(ticcmd_t)); - - // search a tic that have enougth space in the ticcmd - while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), - (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) - && tic < firstticstosend + BACKUPTICS) - tic++; - - if (tic >= firstticstosend + BACKUPTICS) - { - DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " - "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), - maketic, firstticstosend, node, netconsole)); - Net_UnAcknowledgePacket(node); - return; - } - - // Make sure we have a buffer - if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); - - DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", - tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); - - M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); - textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; - } -} - static void PT_Login(SINT8 node, INT32 netconsole) { (void)node; @@ -2314,81 +1765,6 @@ static void PT_ReceivedGamestate(SINT8 node) netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE; } -static void PT_ServerTics(SINT8 node, INT32 netconsole) -{ - UINT8 *pak, *txtpak, numtxtpak; - tic_t realend, realstart; - - if (!netnodes[node].ingame) - { - // Do not remove my own server (we have just get a out of order packet) - if (node != servernode) - { - DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); - Net_CloseConnection(node); - } - return; - } - - // Only accept PT_SERVERTICS from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - return; - } - - realstart = netbuffer->u.serverpak.starttic; - realend = realstart + netbuffer->u.serverpak.numtics; - - txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots - * netbuffer->u.serverpak.numtics]; - - if (realend > gametic + CLIENTBACKUPTICS) - realend = gametic + CLIENTBACKUPTICS; - cl_packetmissed = realstart > neededtic; - - if (realstart <= neededtic && realend > neededtic) - { - tic_t i, j; - pak = (UINT8 *)&netbuffer->u.serverpak.cmds; - - for (i = realstart; i < realend; i++) - { - // clear first - D_Clearticcmd(i); - - // copy the tics - pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, - netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); - - // copy the textcmds - numtxtpak = *txtpak++; - for (j = 0; j < numtxtpak; j++) - { - INT32 k = *txtpak++; // playernum - const size_t txtsize = txtpak[0]+1; - - if (i >= gametic) // Don't copy old net commands - M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); - txtpak += txtsize; - } - } - - neededtic = realend; - } - else - { - DEBFILE(va("frame not in bound: %u\n", neededtic)); - /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) - I_Error("Received an out of order PT_SERVERTICS packet!\n" - "Got tics %d-%d, needed tic %d\n\n" - "Please report this crash on the Master Board,\n" - "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ - } -} - static void PT_Ping(SINT8 node, INT32 netconsole) { // Only accept PT_PING from the server. @@ -2602,7 +1978,7 @@ void GetPackets(void) // no more use random generator, because at very first tic isn't yet synchronized // Note: It is called consistAncy on purpose. // -static INT16 Consistancy(void) +INT16 Consistancy(void) { INT32 i; UINT32 ret = 0; @@ -2708,239 +2084,11 @@ static INT16 Consistancy(void) return (INT16)(ret & 0xFFFF); } -// send the client packet to the server -static void CL_SendClientCmd(void) -{ - size_t packetsize = 0; - - netbuffer->packettype = PT_CLIENTCMD; - - if (cl_packetmissed) - netbuffer->packettype++; - netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX); - netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX); - - if (gamestate == GS_WAITINGPLAYERS) - { - // Send PT_NODEKEEPALIVE packet - netbuffer->packettype += 4; - packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); - HSendPacket(servernode, false, 0, packetsize); - } - else if (gamestate != GS_NULL && (addedtogame || dedicated)) - { - G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); - netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); - - // Send a special packet with 2 cmd for splitscreen - if (splitscreen || botingame) - { - netbuffer->packettype += 2; - G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1); - packetsize = sizeof (client2cmd_pak); - } - else - packetsize = sizeof (clientcmd_pak); - - HSendPacket(servernode, false, 0, packetsize); - } - - if (cl_mode == CL_CONNECTED || dedicated) - { - // Send extra data if needed - if (localtextcmd[0]) - { - netbuffer->packettype = PT_TEXTCMD; - M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... - localtextcmd[0] = 0; - } - - // Send extra data if needed for player 2 (splitscreen) - if (localtextcmd2[0]) - { - netbuffer->packettype = PT_TEXTCMD2; - M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... - localtextcmd2[0] = 0; - } - } -} - -// send the server packet -// send tic from firstticstosend to maketic-1 -static void SV_SendTics(void) -{ - tic_t realfirsttic, lasttictosend, i; - UINT32 n; - INT32 j; - size_t packsize; - UINT8 *bufpos; - UINT8 *ntextcmd; - - // send to all client but not to me - // for each node create a packet with x tics and send it - // x is computed using netnodes[n].supposedtic, max packet size and maketic - for (n = 1; n < MAXNETNODES; n++) - if (netnodes[n].ingame) - { - // assert netnodes[n].supposedtic>=netnodes[n].tic - realfirsttic = netnodes[n].supposedtic; - lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS); - - if (realfirsttic >= lasttictosend) - { - // well we have sent all tics we will so use extrabandwidth - // to resent packet that are supposed lost (this is necessary since lost - // packet detection work when we have received packet with firsttic > neededtic - // (getpacket servertics case) - DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", - n, maketic, netnodes[n].supposedtic, netnodes[n].tic)); - realfirsttic = netnodes[n].tic; - if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) - // all tic are ok - continue; - DEBFILE(va("Sent %d anyway\n", realfirsttic)); - } - if (realfirsttic < firstticstosend) - realfirsttic = firstticstosend; - - // compute the length of the packet and cut it if too large - packsize = BASESERVERTICSSIZE; - for (i = realfirsttic; i < lasttictosend; i++) - { - packsize += sizeof (ticcmd_t) * doomcom->numslots; - packsize += TotalTextCmdPerTic(i); - - if (packsize > software_MAXPACKETLENGTH) - { - DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", - sizeu1(packsize), i, realfirsttic, lasttictosend)); - lasttictosend = i; - - // too bad: too much player have send extradata and there is too - // much data in one tic. - // To avoid it put the data on the next tic. (see getpacket - // textcmd case) but when numplayer changes the computation can be different - if (lasttictosend == realfirsttic) - { - if (packsize > MAXPACKETLENGTH) - I_Error("Too many players: can't send %s data for %d players to node %d\n" - "Well sorry nobody is perfect....\n", - sizeu1(packsize), doomcom->numslots, n); - else - { - lasttictosend++; // send it anyway! - DEBFILE("sending it anyway\n"); - } - } - break; - } - } - - // Send the tics - netbuffer->packettype = PT_SERVERTICS; - netbuffer->u.serverpak.starttic = realfirsttic; - netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); - netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); - bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; - - for (i = realfirsttic; i < lasttictosend; i++) - { - bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); - } - - // add textcmds - for (i = realfirsttic; i < lasttictosend; i++) - { - ntextcmd = bufpos++; - *ntextcmd = 0; - for (j = 0; j < MAXPLAYERS; j++) - { - UINT8 *textcmd = D_GetExistingTextcmd(i, j); - INT32 size = textcmd ? textcmd[0] : 0; - - if ((!j || playeringame[j]) && size) - { - (*ntextcmd)++; - WRITEUINT8(bufpos, j); - M_Memcpy(bufpos, textcmd, size + 1); - bufpos += size + 1; - } - } - } - packsize = bufpos - (UINT8 *)&(netbuffer->u); - - HSendPacket(n, false, 0, packsize); - // when tic are too large, only one tic is sent so don't go backward! - if (lasttictosend-doomcom->extratics > realfirsttic) - netnodes[n].supposedtic = lasttictosend-doomcom->extratics; - else - netnodes[n].supposedtic = lasttictosend; - if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic; - } - // node 0 is me! - netnodes[0].supposedtic = maketic; -} - -// -// TryRunTics -// -static void Local_Maketic(INT32 realtics) -{ - I_OsPolling(); // I_Getevent - D_ProcessEvents(); // menu responder, cons responder, - // game responder calls HU_Responder, AM_Responder, - // and G_MapEventsToControls - if (!dedicated) rendergametic = gametic; - // translate inputs (keyboard/mouse/gamepad) into game controls - G_BuildTiccmd(&localcmds, realtics, 1); - if (splitscreen || botingame) - G_BuildTiccmd(&localcmds2, realtics, 2); - - localcmds.angleturn |= TICCMD_RECEIVED; - localcmds2.angleturn |= TICCMD_RECEIVED; -} - -// create missed tic -static void SV_Maketic(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - // We didn't receive this tic - if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) - { - ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; - ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; - - if (players[i].quittime) - { - // Copy the angle/aiming from the previous tic - // and empty the other inputs - memset(ticcmd, 0, sizeof(netcmds[0][0])); - ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; - ticcmd->aiming = prevticcmd->aiming; - } - else - { - DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); - // Copy the input from the previous tic - *ticcmd = *prevticcmd; - ticcmd->angleturn &= ~TICCMD_RECEIVED; - } - } - } - - // all tic are now proceed make the next - maketic++; -} +/* +Ping Update except better: +We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. +If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. +*/ boolean TryRunTics(tic_t realtics) { @@ -3049,12 +2197,6 @@ boolean TryRunTics(tic_t realtics) return ticking; } -/* -Ping Update except better: -We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. -If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. -*/ - static INT32 pingtimeout[MAXPLAYERS]; static inline void PingUpdate(void) diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index b8da645ba..8b73170da 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -378,6 +378,7 @@ extern boolean acceptnewnode; extern SINT8 servernode; extern tic_t maketic; extern tic_t neededtic; +extern INT16 consistancy[BACKUPTICS]; void Command_Ping_f(void); extern tic_t connectiontimeout; @@ -391,20 +392,15 @@ extern consvar_t cv_resynchattempts, cv_blamecfail; extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; // Used in d_net, the only dependence -tic_t ExpandTics(INT32 low, INT32 node); void D_ClientServerInit(void); -// Initialise the other field -void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); -void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); -void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player -void SendKick(UINT8 playernum, UINT8 msg); - // Create any new ticcmds and broadcast to other players. void NetUpdate(void); void GetPackets(void); void ResetNode(INT32 node); +INT16 Consistancy(void); +boolean SV_ResendingSavegameToAnyone(void); void SV_StartSinglePlayerServer(void); void SV_SpawnServer(void); @@ -440,10 +436,8 @@ extern char motd[254], server_context[8]; extern UINT8 playernode[MAXPLAYERS]; INT32 D_NumPlayers(void); -void D_ResetTiccmds(void); tic_t GetLag(INT32 node); -UINT8 GetFreeXCmdSize(void); void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest); diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c index ae0605001..972342282 100644 --- a/src/netcode/d_net.c +++ b/src/netcode/d_net.c @@ -26,6 +26,7 @@ #include "../w_wad.h" #include "d_netfil.h" #include "d_clisrv.h" +#include "tic_command.h" #include "../z_zone.h" #include "i_tcp.h" #include "../d_main.h" // srb2home diff --git a/src/netcode/d_netcmd.c b/src/netcode/d_netcmd.c index fe4cf22bd..0d1785510 100644 --- a/src/netcode/d_netcmd.c +++ b/src/netcode/d_netcmd.c @@ -37,6 +37,7 @@ #include "../m_cheat.h" #include "d_clisrv.h" #include "server_connection.h" +#include "net_command.h" #include "d_net.h" #include "../v_video.h" #include "../d_main.h" diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c index 80fa06852..e581be2ac 100644 --- a/src/netcode/d_netfil.c +++ b/src/netcode/d_netfil.c @@ -42,6 +42,7 @@ #include "d_net.h" #include "../w_wad.h" #include "d_netfil.h" +#include "net_command.h" #include "../z_zone.h" #include "../byteptr.h" #include "../p_setup.h" diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c new file mode 100644 index 000000000..eeb479d21 --- /dev/null +++ b/src/netcode/net_command.c @@ -0,0 +1,315 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 net_command.c +/// \brief Net command handling + +#include "net_command.h" +#include "tic_command.h" +#include "d_clisrv.h" +#include "i_net.h" +#include "../g_game.h" +#include "../z_zone.h" +#include "../doomtype.h" + +textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; +UINT8 localtextcmd[MAXTEXTCMD]; +UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen +static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum); + +void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) +{ +#ifdef PARANOIA + if (id >= MAXNETXCMD) + I_Error("Command id %d too big", id); + if (listnetxcmd[id] != 0) + I_Error("Command id %d already used", id); +#endif + listnetxcmd[id] = cmd_f; +} + +void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam) +{ + if (localtextcmd[0]+2+nparam > MAXTEXTCMD) + { + // for future reference: if (cv_debug) != debug disabled. + CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam)); + return; + } + localtextcmd[0]++; + localtextcmd[localtextcmd[0]] = (UINT8)id; + if (param && nparam) + { + M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam); + localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam); + } +} + +// splitscreen player +void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam) +{ + if (localtextcmd2[0]+2+nparam > MAXTEXTCMD) + { + I_Error("No more place in the buffer for netcmd %d\n",id); + return; + } + localtextcmd2[0]++; + localtextcmd2[localtextcmd2[0]] = (UINT8)id; + if (param && nparam) + { + M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam); + localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam); + } +} + +UINT8 GetFreeXCmdSize(void) +{ + // -1 for the size and another -1 for the ID. + return (UINT8)(localtextcmd[0] - 2); +} + +// Frees all textcmd memory for the specified tic +void D_FreeTextcmd(tic_t tic) +{ + textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdtic_t *textcmdtic = *tctprev; + + while (textcmdtic && textcmdtic->tic != tic) + { + tctprev = &textcmdtic->next; + textcmdtic = textcmdtic->next; + } + + if (textcmdtic) + { + INT32 i; + + // Remove this tic from the list. + *tctprev = textcmdtic->next; + + // Free all players. + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + { + textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i]; + + while (textcmdplayer) + { + textcmdplayer_t *tcpnext = textcmdplayer->next; + Z_Free(textcmdplayer); + textcmdplayer = tcpnext; + } + } + + // Free this tic's own memory. + Z_Free(textcmdtic); + } +} + +// Gets the buffer for the specified ticcmd, or NULL if there isn't one +UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum) +{ + textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next; + + // Do we have an entry for the tic? If so, look for player. + if (textcmdtic) + { + textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; + while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next; + + if (textcmdplayer) return textcmdplayer->cmd; + } + + return NULL; +} + +// Gets the buffer for the specified ticcmd, creating one if necessary +UINT8* D_GetTextcmd(tic_t tic, INT32 playernum) +{ + textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdplayer_t *textcmdplayer, **tcpprev; + + // Look for the tic. + while (textcmdtic && textcmdtic->tic != tic) + { + tctprev = &textcmdtic->next; + textcmdtic = textcmdtic->next; + } + + // If we don't have an entry for the tic, make it. + if (!textcmdtic) + { + textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL); + textcmdtic->tic = tic; + } + + tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; + textcmdplayer = *tcpprev; + + // Look for the player. + while (textcmdplayer && textcmdplayer->playernum != playernum) + { + tcpprev = &textcmdplayer->next; + textcmdplayer = textcmdplayer->next; + } + + // If we don't have an entry for the player, make it. + if (!textcmdplayer) + { + textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL); + textcmdplayer->playernum = playernum; + } + + return textcmdplayer->cmd; +} + +void ExtraDataTicker(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] || i == 0) + { + UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i); + + if (bufferstart) + { + UINT8 *curpos = bufferstart; + UINT8 *bufferend = &curpos[curpos[0]+1]; + + curpos++; + while (curpos < bufferend) + { + if (*curpos < MAXNETXCMD && listnetxcmd[*curpos]) + { + const UINT8 id = *curpos; + curpos++; + DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); + (listnetxcmd[id])(&curpos, i); + DEBFILE("done\n"); + } + else + { + if (server) + { + SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); + } + CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); + break; + } + } + } + } + + // If you are a client, you can safely forget the net commands for this tic + // If you are the server, you need to remember them until every client has been acknowledged, + // because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it + if (client) + D_FreeTextcmd(gametic); +} + +// used at txtcmds received to check packetsize bound +size_t TotalTextCmdPerTic(tic_t tic) +{ + INT32 i; + size_t total = 1; // num of textcmds in the tic (ntextcmd byte) + + for (i = 0; i < MAXPLAYERS; i++) + { + UINT8 *textcmd = D_GetExistingTextcmd(tic, i); + if ((!i || playeringame[i]) && textcmd) + total += 2 + textcmd[0]; // "+2" for size and playernum + } + + return total; +} + +void PT_TextCmd(SINT8 node, INT32 netconsole) +{ + if (client) + return; + + // splitscreen special + if (netbuffer->packettype == PT_TEXTCMD2) + netconsole = netnodes[node].player2; + + if (netconsole < 0 || netconsole >= MAXPLAYERS) + Net_UnAcknowledgePacket(node); + else + { + size_t j; + tic_t tic = maketic; + UINT8 *textcmd; + + // ignore if the textcmd has a reported size of zero + // this shouldn't be sent at all + if (!netbuffer->u.textcmd[0]) + { + DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", + node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // ignore if the textcmd size var is actually larger than it should be + // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength + if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) + { + DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", + netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), + node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // check if tic that we are making isn't too large else we cannot send it :( + // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time + j = software_MAXPACKETLENGTH + - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE + + (doomcom->numslots+1)*sizeof(ticcmd_t)); + + // search a tic that have enougth space in the ticcmd + while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), + (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) + && tic < firstticstosend + BACKUPTICS) + tic++; + + if (tic >= firstticstosend + BACKUPTICS) + { + DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " + "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), + maketic, firstticstosend, node, netconsole)); + Net_UnAcknowledgePacket(node); + return; + } + + // Make sure we have a buffer + if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); + + DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", + tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); + + M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); + textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; + } +} + +void SendKick(UINT8 playernum, UINT8 msg) +{ + UINT8 buf[2]; + + if (!(server && cv_rejointimeout.value)) + msg &= ~KICK_MSG_KEEP_BODY; + + buf[0] = playernum; + buf[1] = msg; + SendNetXCmd(XD_KICK, &buf, 2); +} diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h new file mode 100644 index 000000000..1fc5035c8 --- /dev/null +++ b/src/netcode/net_command.h @@ -0,0 +1,62 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 net_command.h +/// \brief Net command handling + +#ifndef __D_NET_COMMAND__ +#define __D_NET_COMMAND__ + +#include "d_clisrv.h" +#include "../doomtype.h" + +// Must be a power of two +#define TEXTCMD_HASH_SIZE 4 + +typedef struct textcmdplayer_s +{ + INT32 playernum; + UINT8 cmd[MAXTEXTCMD]; + struct textcmdplayer_s *next; +} textcmdplayer_t; + +typedef struct textcmdtic_s +{ + tic_t tic; + textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE]; + struct textcmdtic_s *next; +} textcmdtic_t; + +extern textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE]; + +extern UINT8 localtextcmd[MAXTEXTCMD]; +extern UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen + +void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); +void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); +void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player + +UINT8 GetFreeXCmdSize(void); +void D_FreeTextcmd(tic_t tic); + +// Gets the buffer for the specified ticcmd, or NULL if there isn't one +UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum); + +// Gets the buffer for the specified ticcmd, creating one if necessary +UINT8* D_GetTextcmd(tic_t tic, INT32 playernum); + +void ExtraDataTicker(void); + +// used at txtcmds received to check packetsize bound +size_t TotalTextCmdPerTic(tic_t tic); + +void PT_TextCmd(SINT8 node, INT32 netconsole); +void SendKick(UINT8 playernum, UINT8 msg); + +#endif diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index 9e397b0f3..b776db422 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -15,6 +15,7 @@ #include "d_clisrv.h" #include "d_netfil.h" #include "mserv.h" +#include "net_command.h" #include "../byteptr.h" #include "../g_game.h" #include "../g_state.h" diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c new file mode 100644 index 000000000..e7df895ff --- /dev/null +++ b/src/netcode/tic_command.c @@ -0,0 +1,504 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 tic_command.c +/// \brief Tic command handling + +#include "tic_command.h" +#include "d_clisrv.h" +#include "net_command.h" +#include "client_connection.h" +#include "i_net.h" +#include "../d_main.h" +#include "../g_game.h" +#include "../i_system.h" +#include "../i_time.h" +#include "../byteptr.h" +#include "../doomstat.h" +#include "../doomtype.h" + +tic_t firstticstosend; // min of the nettics +tic_t tictoclear = 0; // optimize d_clearticcmd +ticcmd_t localcmds; +ticcmd_t localcmds2; +boolean cl_packetmissed; +ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; + +static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) +{ + const size_t d = n / sizeof(ticcmd_t); + const size_t r = n % sizeof(ticcmd_t); + UINT8 *ret = dest; + + if (r) + M_Memcpy(dest, src, n); + else if (d) + G_MoveTiccmd(dest, src, d); + return ret+n; +} + +static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) +{ + const size_t d = n / sizeof(ticcmd_t); + const size_t r = n % sizeof(ticcmd_t); + UINT8 *ret = src; + + if (r) + M_Memcpy(dest, src, n); + else if (d) + G_MoveTiccmd(dest, src, d); + return ret+n; +} + +/** Guesses the full value of a tic from its lowest byte, for a specific node + * + * \param low The lowest byte of the tic value + * \param node The node to deduce the tic for + * \return The full tic value + * + */ +tic_t ExpandTics(INT32 low, INT32 node) +{ + INT32 delta; + + delta = low - (netnodes[node].tic & UINT8_MAX); + + if (delta >= -64 && delta <= 64) + return (netnodes[node].tic & ~UINT8_MAX) + low; + else if (delta > 64) + return (netnodes[node].tic & ~UINT8_MAX) - 256 + low; + else //if (delta < -64) + return (netnodes[node].tic & ~UINT8_MAX) + 256 + low; +} + +void D_Clearticcmd(tic_t tic) +{ + INT32 i; + + D_FreeTextcmd(tic); + + for (i = 0; i < MAXPLAYERS; i++) + netcmds[tic%BACKUPTICS][i].angleturn = 0; + + DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); +} + +void D_ResetTiccmds(void) +{ + INT32 i; + + memset(&localcmds, 0, sizeof(ticcmd_t)); + memset(&localcmds2, 0, sizeof(ticcmd_t)); + + // Reset the net command list + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + while (textcmds[i]) + D_Clearticcmd(textcmds[i]->tic); +} + +void PT_ClientCmd(SINT8 node, INT32 netconsole) +{ + tic_t realend, realstart; + + if (client) + return; + + // To save bytes, only the low byte of tic numbers are sent + // Use ExpandTics to figure out what the rest of the bytes are + realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); + + if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS + || netbuffer->packettype == PT_NODEKEEPALIVEMIS + || netnodes[node].supposedtic < realend) + { + netnodes[node].supposedtic = realend; + } + // Discard out of order packet + if (netnodes[node].tic > realend) + { + DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic)); + return; + } + + // Update the nettics + netnodes[node].tic = realend; + + // Don't do anything for packets of type NODEKEEPALIVE? + if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE + || netbuffer->packettype == PT_NODEKEEPALIVEMIS) + return; + + // As long as clients send valid ticcmds, the server can keep running, so reset the timeout + /// \todo Use a separate cvar for that kind of timeout? + netnodes[node].freezetimeout = I_GetTime() + connectiontimeout; + + // Copy ticcmd + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); + + // Check ticcmd for "speed hacks" + if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE + || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); + //D_Clearticcmd(k); + + SendKick(netconsole, KICK_MSG_CON_FAIL); + return; + } + + // Splitscreen cmd + if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) + && netnodes[node].player2 >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2], + &netbuffer->u.client2pak.cmd2, 1); + + // Check player consistancy during the level + if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) + && !SV_ResendingSavegameToAnyone() + && !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime()) + { + if (cv_resynchattempts.value) + { + // Tell the client we are about to resend them the gamestate + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + HSendPacket(node, true, 0, 0); + + netnodes[node].resendingsavegame = true; + + if (cv_blamecfail.value) + CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), + netconsole+1, player_names[netconsole], + consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy)); + DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + return; + } + else + { + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + return; + } + } +} + +void PT_ServerTics(SINT8 node, INT32 netconsole) +{ + UINT8 *pak, *txtpak, numtxtpak; + tic_t realend, realstart; + + if (!netnodes[node].ingame) + { + // Do not remove my own server (we have just get a out of order packet) + if (node != servernode) + { + DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); + Net_CloseConnection(node); + } + return; + } + + // Only accept PT_SERVERTICS from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + + realstart = netbuffer->u.serverpak.starttic; + realend = realstart + netbuffer->u.serverpak.numtics; + + txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots + * netbuffer->u.serverpak.numtics]; + + if (realend > gametic + CLIENTBACKUPTICS) + realend = gametic + CLIENTBACKUPTICS; + cl_packetmissed = realstart > neededtic; + + if (realstart <= neededtic && realend > neededtic) + { + tic_t i, j; + pak = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realstart; i < realend; i++) + { + // clear first + D_Clearticcmd(i); + + // copy the tics + pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, + netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); + + // copy the textcmds + numtxtpak = *txtpak++; + for (j = 0; j < numtxtpak; j++) + { + INT32 k = *txtpak++; // playernum + const size_t txtsize = txtpak[0]+1; + + if (i >= gametic) // Don't copy old net commands + M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); + txtpak += txtsize; + } + } + + neededtic = realend; + } + else + { + DEBFILE(va("frame not in bound: %u\n", neededtic)); + /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) + I_Error("Received an out of order PT_SERVERTICS packet!\n" + "Got tics %d-%d, needed tic %d\n\n" + "Please report this crash on the Master Board,\n" + "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ + } +} + +// send the client packet to the server +void CL_SendClientCmd(void) +{ + size_t packetsize = 0; + + netbuffer->packettype = PT_CLIENTCMD; + + if (cl_packetmissed) + netbuffer->packettype++; + netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX); + netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX); + + if (gamestate == GS_WAITINGPLAYERS) + { + // Send PT_NODEKEEPALIVE packet + netbuffer->packettype += 4; + packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); + HSendPacket(servernode, false, 0, packetsize); + } + else if (gamestate != GS_NULL && (addedtogame || dedicated)) + { + G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); + netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); + + // Send a special packet with 2 cmd for splitscreen + if (splitscreen || botingame) + { + netbuffer->packettype += 2; + G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1); + packetsize = sizeof (client2cmd_pak); + } + else + packetsize = sizeof (clientcmd_pak); + + HSendPacket(servernode, false, 0, packetsize); + } + + if (cl_mode == CL_CONNECTED || dedicated) + { + // Send extra data if needed + if (localtextcmd[0]) + { + netbuffer->packettype = PT_TEXTCMD; + M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... + localtextcmd[0] = 0; + } + + // Send extra data if needed for player 2 (splitscreen) + if (localtextcmd2[0]) + { + netbuffer->packettype = PT_TEXTCMD2; + M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... + localtextcmd2[0] = 0; + } + } +} + +// send the server packet +// send tic from firstticstosend to maketic-1 +void SV_SendTics(void) +{ + tic_t realfirsttic, lasttictosend, i; + UINT32 n; + INT32 j; + size_t packsize; + UINT8 *bufpos; + UINT8 *ntextcmd; + + // send to all client but not to me + // for each node create a packet with x tics and send it + // x is computed using netnodes[n].supposedtic, max packet size and maketic + for (n = 1; n < MAXNETNODES; n++) + if (netnodes[n].ingame) + { + // assert netnodes[n].supposedtic>=netnodes[n].tic + realfirsttic = netnodes[n].supposedtic; + lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS); + + if (realfirsttic >= lasttictosend) + { + // well we have sent all tics we will so use extrabandwidth + // to resent packet that are supposed lost (this is necessary since lost + // packet detection work when we have received packet with firsttic > neededtic + // (getpacket servertics case) + DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", + n, maketic, netnodes[n].supposedtic, netnodes[n].tic)); + realfirsttic = netnodes[n].tic; + if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) + // all tic are ok + continue; + DEBFILE(va("Sent %d anyway\n", realfirsttic)); + } + if (realfirsttic < firstticstosend) + realfirsttic = firstticstosend; + + // compute the length of the packet and cut it if too large + packsize = BASESERVERTICSSIZE; + for (i = realfirsttic; i < lasttictosend; i++) + { + packsize += sizeof (ticcmd_t) * doomcom->numslots; + packsize += TotalTextCmdPerTic(i); + + if (packsize > software_MAXPACKETLENGTH) + { + DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", + sizeu1(packsize), i, realfirsttic, lasttictosend)); + lasttictosend = i; + + // too bad: too much player have send extradata and there is too + // much data in one tic. + // To avoid it put the data on the next tic. (see getpacket + // textcmd case) but when numplayer changes the computation can be different + if (lasttictosend == realfirsttic) + { + if (packsize > MAXPACKETLENGTH) + I_Error("Too many players: can't send %s data for %d players to node %d\n" + "Well sorry nobody is perfect....\n", + sizeu1(packsize), doomcom->numslots, n); + else + { + lasttictosend++; // send it anyway! + DEBFILE("sending it anyway\n"); + } + } + break; + } + } + + // Send the tics + netbuffer->packettype = PT_SERVERTICS; + netbuffer->u.serverpak.starttic = realfirsttic; + netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); + netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); + bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realfirsttic; i < lasttictosend; i++) + { + bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); + } + + // add textcmds + for (i = realfirsttic; i < lasttictosend; i++) + { + ntextcmd = bufpos++; + *ntextcmd = 0; + for (j = 0; j < MAXPLAYERS; j++) + { + UINT8 *textcmd = D_GetExistingTextcmd(i, j); + INT32 size = textcmd ? textcmd[0] : 0; + + if ((!j || playeringame[j]) && size) + { + (*ntextcmd)++; + WRITEUINT8(bufpos, j); + M_Memcpy(bufpos, textcmd, size + 1); + bufpos += size + 1; + } + } + } + packsize = bufpos - (UINT8 *)&(netbuffer->u); + + HSendPacket(n, false, 0, packsize); + // when tic are too large, only one tic is sent so don't go backward! + if (lasttictosend-doomcom->extratics > realfirsttic) + netnodes[n].supposedtic = lasttictosend-doomcom->extratics; + else + netnodes[n].supposedtic = lasttictosend; + if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic; + } + // node 0 is me! + netnodes[0].supposedtic = maketic; +} + +// +// TryRunTics +// +void Local_Maketic(INT32 realtics) +{ + I_OsPolling(); // I_Getevent + D_ProcessEvents(); // menu responder, cons responder, + // game responder calls HU_Responder, AM_Responder, + // and G_MapEventsToControls + if (!dedicated) rendergametic = gametic; + // translate inputs (keyboard/mouse/gamepad) into game controls + G_BuildTiccmd(&localcmds, realtics, 1); + if (splitscreen || botingame) + G_BuildTiccmd(&localcmds2, realtics, 2); + + localcmds.angleturn |= TICCMD_RECEIVED; + localcmds2.angleturn |= TICCMD_RECEIVED; +} + +// create missed tic +void SV_Maketic(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + // We didn't receive this tic + if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) + { + ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; + ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; + + if (players[i].quittime) + { + // Copy the angle/aiming from the previous tic + // and empty the other inputs + memset(ticcmd, 0, sizeof(netcmds[0][0])); + ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; + ticcmd->aiming = prevticcmd->aiming; + } + else + { + DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); + // Copy the input from the previous tic + *ticcmd = *prevticcmd; + ticcmd->angleturn &= ~TICCMD_RECEIVED; + } + } + } + + // all tic are now proceed make the next + maketic++; +} diff --git a/src/netcode/tic_command.h b/src/netcode/tic_command.h new file mode 100644 index 000000000..d19f4c1aa --- /dev/null +++ b/src/netcode/tic_command.h @@ -0,0 +1,44 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 tic_command.h +/// \brief Tic command handling + +#ifndef __D_TIC_COMMAND__ +#define __D_TIC_COMMAND__ + +#include "d_clisrv.h" +#include "../d_ticcmd.h" +#include "../doomdef.h" +#include "../doomtype.h" + +extern tic_t firstticstosend; // min of the nettics +extern tic_t tictoclear; // optimize d_clearticcmd + +extern ticcmd_t localcmds; +extern ticcmd_t localcmds2; +extern boolean cl_packetmissed; + +extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; + +tic_t ExpandTics(INT32 low, INT32 node); +void D_Clearticcmd(tic_t tic); +void D_ResetTiccmds(void); + +void PT_ClientCmd(SINT8 node, INT32 netconsole); +void PT_ServerTics(SINT8 node, INT32 netconsole); + +// send the client packet to the server +void CL_SendClientCmd(void); + +void SV_SendTics(void); +void Local_Maketic(INT32 realtics); +void SV_Maketic(void); + +#endif diff --git a/src/p_inter.c b/src/p_inter.c index f3c13e315..f33494d1f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -30,6 +30,7 @@ #include "m_misc.h" #include "v_video.h" // video flags for CEchos #include "f_finale.h" +#include "netcode/net_command.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" diff --git a/src/p_mobj.c b/src/p_mobj.c index 49c438b3f..e60e81ace 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -36,6 +36,7 @@ #include "p_slopes.h" #include "f_finale.h" #include "m_cond.h" +#include "netcode/net_command.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); diff --git a/src/p_setup.c b/src/p_setup.c index cd9185484..22d48dd0b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -87,6 +87,8 @@ #include "taglist.h" +#include "netcode/net_command.h" + // // Map MD5, calculated on level load. // Sent to clients in PT_SERVERINFO. diff --git a/src/p_tick.c b/src/p_tick.c index fe6a4d33f..8fb99d737 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -26,6 +26,7 @@ #include "r_main.h" #include "r_fps.h" #include "i_video.h" // rendermode +#include "netcode/net_command.h" // Object place #include "m_cheat.h" diff --git a/src/p_user.c b/src/p_user.c index 1e8dee885..a06bf894f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -19,6 +19,7 @@ #include "i_system.h" #include "d_event.h" #include "netcode/d_net.h" +#include "netcode/net_command.h" #include "g_game.h" #include "p_local.h" #include "r_fps.h" From f1ad1bf13e211ead21da068f3414d62605296f6a Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 5 Jan 2023 23:48:44 +0100 Subject: [PATCH 35/89] Declare data exchanged through the network to a new file --- src/netcode/d_clisrv.c | 1 + src/netcode/d_clisrv.h | 313 +------------------------------------- src/netcode/d_net.h | 2 + src/netcode/protocol.h | 335 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 339 insertions(+), 312 deletions(-) create mode 100644 src/netcode/protocol.h diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 06e6da751..b834c92f5 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -53,6 +53,7 @@ #include "client_connection.h" #include "tic_command.h" #include "net_command.h" +#include "protocol.h" // // NETWORKING diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 8b73170da..691a2787f 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -13,6 +13,7 @@ #ifndef __D_CLISRV__ #define __D_CLISRV__ +#include "protocol.h" #include "../d_ticcmd.h" #include "d_net.h" #include "d_netcmd.h" @@ -21,85 +22,7 @@ #include "../d_player.h" #include "mserv.h" -/* -The 'packet version' is used to distinguish packet -formats. This version is independent of VERSION and -SUBVERSION. Different applications may follow different -packet versions. - -If you change the struct or the meaning of a field -therein, increment this number. -*/ -#define PACKETVERSION 4 - -// Network play related stuff. -// There is a data struct that stores network -// communication related stuff, and another -// one that defines the actual packets to -// be transmitted. - -// Networking and tick handling related. -#define BACKUPTICS 1024 #define CLIENTBACKUPTICS 32 -#define MAXTEXTCMD 256 -// -// Packet structure -// -typedef enum -{ - PT_NOTHING, // To send a nop through the network. ^_~ - PT_SERVERCFG, // Server config used in start game - // (must stay 1 for backwards compatibility). - // This is a positive response to a CLIENTJOIN request. - PT_CLIENTCMD, // Ticcmd of the client. - PT_CLIENTMIS, // Same as above with but saying resend from. - PT_CLIENT2CMD, // 2 cmds in the packet for splitscreen. - PT_CLIENT2MIS, // Same as above with but saying resend from - PT_NODEKEEPALIVE, // Same but without ticcmd and consistancy - PT_NODEKEEPALIVEMIS, - PT_SERVERTICS, // All cmds for the tic. - PT_SERVERREFUSE, // Server refuses joiner (reason inside). - PT_SERVERSHUTDOWN, - PT_CLIENTQUIT, // Client closes the connection. - - PT_ASKINFO, // Anyone can ask info of the server. - PT_SERVERINFO, // Send game & server info (gamespy). - PT_PLAYERINFO, // Send information for players in game (gamespy). - PT_REQUESTFILE, // Client requests a file transfer - PT_ASKINFOVIAMS, // Packet from the MS requesting info be sent to new client. - // If this ID changes, update masterserver definition. - - PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate! - PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead. - PT_RECEIVEDGAMESTATE, // Thank you Server, I am ready to play again! - - PT_SENDINGLUAFILE, // Server telling a client Lua needs to open a file - PT_ASKLUAFILE, // Client telling the server they don't have the file - PT_HASLUAFILE, // Client telling the server they have the file - - // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility. - - PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL - // allows HSendPacket(*, true, *, *) to return false. - // In addition, this packet can't occupy all the available slots. - - PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file. - PT_FILEACK, - PT_FILERECEIVED, - - PT_TEXTCMD, // Extra text commands from the client. - PT_TEXTCMD2, // Splitscreen text commands. - PT_CLIENTJOIN, // Client wants to join; used in start game. - PT_NODETIMEOUT, // Packet sent to self if the connection times out. - - PT_LOGIN, // Login attempt from the client. - - PT_TELLFILESNEEDED, // Client, to server: "what other files do I need starting from this number?" - PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)" - - PT_PING, // Packet sent to tell clients the other client's latency to server. - NUMPACKETTYPE -} packettype_t; #ifdef PACKETDROP void Command_Drop(void); @@ -109,229 +32,6 @@ void Command_Droprate(void); void Command_Numnodes(void); #endif -#if defined(_MSC_VER) -#pragma pack(1) -#endif - -// Client to server packet -typedef struct -{ - UINT8 client_tic; - UINT8 resendfrom; - INT16 consistancy; - ticcmd_t cmd; -} ATTRPACK clientcmd_pak; - -// Splitscreen packet -// WARNING: must have the same format of clientcmd_pak, for more easy use -typedef struct -{ - UINT8 client_tic; - UINT8 resendfrom; - INT16 consistancy; - ticcmd_t cmd, cmd2; -} ATTRPACK client2cmd_pak; - -#ifdef _MSC_VER -#pragma warning(disable : 4200) -#endif - -// Server to client packet -// this packet is too large -typedef struct -{ - tic_t starttic; - UINT8 numtics; - UINT8 numslots; // "Slots filled": Highest player number in use plus one. - ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large -} ATTRPACK servertics_pak; - -typedef struct -{ - // Server launch stuffs - UINT8 serverplayer; - UINT8 totalslotnum; // "Slots": highest player number in use plus one. - - tic_t gametic; - UINT8 clientnode; - UINT8 gamestate; - - UINT8 gametype; - UINT8 modifiedgame; - - char server_context[8]; // Unique context id, generated at server startup. -} ATTRPACK serverconfig_pak; - -typedef struct -{ - UINT8 fileid; - UINT32 filesize; - UINT8 iteration; - UINT32 position; - UINT16 size; - UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH -} ATTRPACK filetx_pak; - -typedef struct -{ - UINT32 start; - UINT32 acks; -} ATTRPACK fileacksegment_t; - -typedef struct -{ - UINT8 fileid; - UINT8 iteration; - UINT8 numsegments; - fileacksegment_t segments[0]; -} ATTRPACK fileack_pak; - -#ifdef _MSC_VER -#pragma warning(default : 4200) -#endif - -#define MAXAPPLICATION 16 - -typedef struct -{ - UINT8 modversion; - char application[MAXAPPLICATION]; - UINT8 localplayers; - UINT8 mode; - char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME]; -} ATTRPACK clientconfig_pak; - -#define SV_DEDICATED 0x40 // server is dedicated -#define SV_LOTSOFADDONS 0x20 // flag used to ask for full file list in d_netfil - -enum { - REFUSE_JOINS_DISABLED = 1, - REFUSE_SLOTS_FULL, - REFUSE_BANNED, -}; - -#define MAXSERVERNAME 32 -#define MAXFILENEEDED 915 -// This packet is too large -typedef struct -{ - /* - In the old packet, 'version' is the first field. Now that field is set - to 255 always, so older versions won't be confused with the new - versions or vice-versa. - */ - UINT8 _255; - UINT8 packetversion; - char application[MAXAPPLICATION]; - UINT8 version; - UINT8 subversion; - UINT8 numberofplayer; - UINT8 maxplayer; - UINT8 refusereason; // 0: joinable, REFUSE enum - char gametypename[24]; - UINT8 modifiedgame; - UINT8 cheatsenabled; - UINT8 flags; - UINT8 fileneedednum; - tic_t time; - tic_t leveltime; - char servername[MAXSERVERNAME]; - char mapname[8]; - char maptitle[33]; - unsigned char mapmd5[16]; - UINT8 actnum; - UINT8 iszone; - UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) -} ATTRPACK serverinfo_pak; - -typedef struct -{ - char reason[255]; -} ATTRPACK serverrefuse_pak; - -typedef struct -{ - UINT8 version; - tic_t time; // used for ping evaluation -} ATTRPACK askinfo_pak; - -typedef struct -{ - char clientaddr[22]; - tic_t time; // used for ping evaluation -} ATTRPACK msaskinfo_pak; - -// Shorter player information for external use. -typedef struct -{ - UINT8 num; - char name[MAXPLAYERNAME+1]; - UINT8 address[4]; // sending another string would run us up against MAXPACKETLENGTH - UINT8 team; - UINT8 skin; - UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused. - UINT32 score; - UINT16 timeinserver; // In seconds. -} ATTRPACK plrinfo; - -// Shortest player information for join during intermission. -typedef struct -{ - char name[MAXPLAYERNAME+1]; - UINT8 skin; - UINT16 color; - UINT32 pflags; - UINT32 score; - UINT8 ctfteam; -} ATTRPACK plrconfig; - -typedef struct -{ - INT32 first; - UINT8 num; - UINT8 more; - UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) -} ATTRPACK filesneededconfig_pak; - -// -// Network packet data -// -typedef struct -{ - UINT32 checksum; - UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack - UINT8 ackreturn; // The return of the ack number - - UINT8 packettype; - UINT8 reserved; // Padding - union - { - clientcmd_pak clientpak; // 144 bytes - client2cmd_pak client2pak; // 200 bytes - servertics_pak serverpak; // 132495 bytes (more around 360, no?) - serverconfig_pak servercfg; // 773 bytes - UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) - filetx_pak filetxpak; // 139 bytes - fileack_pak fileack; - UINT8 filereceived; - clientconfig_pak clientcfg; // 136 bytes - UINT8 md5sum[16]; - serverinfo_pak serverinfo; // 1024 bytes - serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) - askinfo_pak askinfo; // 61 bytes - msaskinfo_pak msaskinfo; // 22 bytes - plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?) - plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) - INT32 filesneedednum; // 4 bytes - filesneededconfig_pak filesneededcfg; // ??? bytes - UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes - } u; // This is needed to pack diff packet types data together -} ATTRPACK doomdata_t; - -#if defined(_MSC_VER) -#pragma pack() -#endif - extern INT32 mapchangepending; // Points inside doomcom @@ -341,19 +41,8 @@ extern consvar_t cv_showjoinaddress; extern consvar_t cv_playbackspeed; #define BASEPACKETSIZE offsetof(doomdata_t, u) -#define FILETXHEADER offsetof(filetx_pak, data) #define BASESERVERTICSSIZE offsetof(doomdata_t, u.serverpak.cmds[0]) -#define KICK_MSG_GO_AWAY 1 -#define KICK_MSG_CON_FAIL 2 -#define KICK_MSG_PLAYER_QUIT 3 -#define KICK_MSG_TIMEOUT 4 -#define KICK_MSG_BANNED 5 -#define KICK_MSG_PING_HIGH 6 -#define KICK_MSG_CUSTOM_KICK 7 -#define KICK_MSG_CUSTOM_BAN 8 -#define KICK_MSG_KEEP_BODY 0x80 - typedef enum { KR_KICK = 1, //Kicked by server diff --git a/src/netcode/d_net.h b/src/netcode/d_net.h index 803b49d74..857c3463c 100644 --- a/src/netcode/d_net.h +++ b/src/netcode/d_net.h @@ -18,6 +18,8 @@ #ifndef __D_NET__ #define __D_NET__ +#include "../doomtype.h" + // Max computers in a game // 127 is probably as high as this can go, because // SINT8 is used for nodes sometimes >:( diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h new file mode 100644 index 000000000..566d10d8c --- /dev/null +++ b/src/netcode/protocol.h @@ -0,0 +1,335 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 protocol.h +/// \brief Data exchanged through the network + +#ifndef __PROTOCOL__ +#define __PROTOCOL__ + +#include "d_net.h" +#include "../d_ticcmd.h" +#include "../doomdef.h" + +/* +The 'packet version' is used to distinguish packet +formats. This version is independent of VERSION and +SUBVERSION. Different applications may follow different +packet versions. + +If you change the struct or the meaning of a field +therein, increment this number. +*/ +#define PACKETVERSION 4 + +// Network play related stuff. +// There is a data struct that stores network +// communication related stuff, and another +// one that defines the actual packets to +// be transmitted. + +#define BACKUPTICS 1024 +#define MAXTEXTCMD 256 + +// +// Packet structure +// +typedef enum +{ + PT_NOTHING, // To send a nop through the network. ^_~ + PT_SERVERCFG, // Server config used in start game + // (must stay 1 for backwards compatibility). + // This is a positive response to a CLIENTJOIN request. + PT_CLIENTCMD, // Ticcmd of the client. + PT_CLIENTMIS, // Same as above with but saying resend from. + PT_CLIENT2CMD, // 2 cmds in the packet for splitscreen. + PT_CLIENT2MIS, // Same as above with but saying resend from + PT_NODEKEEPALIVE, // Same but without ticcmd and consistancy + PT_NODEKEEPALIVEMIS, + PT_SERVERTICS, // All cmds for the tic. + PT_SERVERREFUSE, // Server refuses joiner (reason inside). + PT_SERVERSHUTDOWN, + PT_CLIENTQUIT, // Client closes the connection. + + PT_ASKINFO, // Anyone can ask info of the server. + PT_SERVERINFO, // Send game & server info (gamespy). + PT_PLAYERINFO, // Send information for players in game (gamespy). + PT_REQUESTFILE, // Client requests a file transfer + PT_ASKINFOVIAMS, // Packet from the MS requesting info be sent to new client. + // If this ID changes, update masterserver definition. + + PT_WILLRESENDGAMESTATE, // Hey Client, I am about to resend you the gamestate! + PT_CANRECEIVEGAMESTATE, // Okay Server, I'm ready to receive it, you can go ahead. + PT_RECEIVEDGAMESTATE, // Thank you Server, I am ready to play again! + + PT_SENDINGLUAFILE, // Server telling a client Lua needs to open a file + PT_ASKLUAFILE, // Client telling the server they don't have the file + PT_HASLUAFILE, // Client telling the server they have the file + + // Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility. + + PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL + // allows HSendPacket(*, true, *, *) to return false. + // In addition, this packet can't occupy all the available slots. + + PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file. + PT_FILEACK, + PT_FILERECEIVED, + + PT_TEXTCMD, // Extra text commands from the client. + PT_TEXTCMD2, // Splitscreen text commands. + PT_CLIENTJOIN, // Client wants to join; used in start game. + PT_NODETIMEOUT, // Packet sent to self if the connection times out. + + PT_LOGIN, // Login attempt from the client. + + PT_TELLFILESNEEDED, // Client, to server: "what other files do I need starting from this number?" + PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)" + + PT_PING, // Packet sent to tell clients the other client's latency to server. + NUMPACKETTYPE +} packettype_t; + +#if defined(_MSC_VER) +#pragma pack(1) +#endif + +// Client to server packet +typedef struct +{ + UINT8 client_tic; + UINT8 resendfrom; + INT16 consistancy; + ticcmd_t cmd; +} ATTRPACK clientcmd_pak; + +// Splitscreen packet +// WARNING: must have the same format of clientcmd_pak, for more easy use +typedef struct +{ + UINT8 client_tic; + UINT8 resendfrom; + INT16 consistancy; + ticcmd_t cmd, cmd2; +} ATTRPACK client2cmd_pak; + +#ifdef _MSC_VER +#pragma warning(disable : 4200) +#endif + +// Server to client packet +// this packet is too large +typedef struct +{ + tic_t starttic; + UINT8 numtics; + UINT8 numslots; // "Slots filled": Highest player number in use plus one. + ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large +} ATTRPACK servertics_pak; + +typedef struct +{ + // Server launch stuffs + UINT8 serverplayer; + UINT8 totalslotnum; // "Slots": highest player number in use plus one. + + tic_t gametic; + UINT8 clientnode; + UINT8 gamestate; + + UINT8 gametype; + UINT8 modifiedgame; + + char server_context[8]; // Unique context id, generated at server startup. +} ATTRPACK serverconfig_pak; + +typedef struct +{ + UINT8 fileid; + UINT32 filesize; + UINT8 iteration; + UINT32 position; + UINT16 size; + UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH +} ATTRPACK filetx_pak; + +typedef struct +{ + UINT32 start; + UINT32 acks; +} ATTRPACK fileacksegment_t; + +typedef struct +{ + UINT8 fileid; + UINT8 iteration; + UINT8 numsegments; + fileacksegment_t segments[0]; +} ATTRPACK fileack_pak; + +#ifdef _MSC_VER +#pragma warning(default : 4200) +#endif + +#define MAXAPPLICATION 16 + +typedef struct +{ + UINT8 modversion; + char application[MAXAPPLICATION]; + UINT8 localplayers; + UINT8 mode; + char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME]; +} ATTRPACK clientconfig_pak; + +#define SV_DEDICATED 0x40 // server is dedicated +#define SV_LOTSOFADDONS 0x20 // flag used to ask for full file list in d_netfil + +enum { + REFUSE_JOINS_DISABLED = 1, + REFUSE_SLOTS_FULL, + REFUSE_BANNED, +}; + +#define MAXSERVERNAME 32 +#define MAXFILENEEDED 915 + +// This packet is too large +typedef struct +{ + /* + In the old packet, 'version' is the first field. Now that field is set + to 255 always, so older versions won't be confused with the new + versions or vice-versa. + */ + UINT8 _255; + UINT8 packetversion; + char application[MAXAPPLICATION]; + UINT8 version; + UINT8 subversion; + UINT8 numberofplayer; + UINT8 maxplayer; + UINT8 refusereason; // 0: joinable, REFUSE enum + char gametypename[24]; + UINT8 modifiedgame; + UINT8 cheatsenabled; + UINT8 flags; + UINT8 fileneedednum; + tic_t time; + tic_t leveltime; + char servername[MAXSERVERNAME]; + char mapname[8]; + char maptitle[33]; + unsigned char mapmd5[16]; + UINT8 actnum; + UINT8 iszone; + UINT8 fileneeded[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) +} ATTRPACK serverinfo_pak; + +typedef struct +{ + char reason[255]; +} ATTRPACK serverrefuse_pak; + +typedef struct +{ + UINT8 version; + tic_t time; // used for ping evaluation +} ATTRPACK askinfo_pak; + +typedef struct +{ + char clientaddr[22]; + tic_t time; // used for ping evaluation +} ATTRPACK msaskinfo_pak; + +// Shorter player information for external use. +typedef struct +{ + UINT8 num; + char name[MAXPLAYERNAME+1]; + UINT8 address[4]; // sending another string would run us up against MAXPACKETLENGTH + UINT8 team; + UINT8 skin; + UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused. + UINT32 score; + UINT16 timeinserver; // In seconds. +} ATTRPACK plrinfo; + +// Shortest player information for join during intermission. +typedef struct +{ + char name[MAXPLAYERNAME+1]; + UINT8 skin; + UINT16 color; + UINT32 pflags; + UINT32 score; + UINT8 ctfteam; +} ATTRPACK plrconfig; + +typedef struct +{ + INT32 first; + UINT8 num; + UINT8 more; + UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) +} ATTRPACK filesneededconfig_pak; + +// +// Network packet data +// +typedef struct +{ + UINT32 checksum; + UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack + UINT8 ackreturn; // The return of the ack number + + UINT8 packettype; + UINT8 reserved; // Padding + union + { + clientcmd_pak clientpak; // 144 bytes + client2cmd_pak client2pak; // 200 bytes + servertics_pak serverpak; // 132495 bytes (more around 360, no?) + serverconfig_pak servercfg; // 773 bytes + UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) + filetx_pak filetxpak; // 139 bytes + fileack_pak fileack; + UINT8 filereceived; + clientconfig_pak clientcfg; // 136 bytes + UINT8 md5sum[16]; + serverinfo_pak serverinfo; // 1024 bytes + serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) + askinfo_pak askinfo; // 61 bytes + msaskinfo_pak msaskinfo; // 22 bytes + plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?) + plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) + INT32 filesneedednum; // 4 bytes + filesneededconfig_pak filesneededcfg; // ??? bytes + UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes + } u; // This is needed to pack diff packet types data together +} ATTRPACK doomdata_t; + +#if defined(_MSC_VER) +#pragma pack() +#endif + +#define FILETXHEADER offsetof(filetx_pak, data) + +#define KICK_MSG_GO_AWAY 1 +#define KICK_MSG_CON_FAIL 2 +#define KICK_MSG_PLAYER_QUIT 3 +#define KICK_MSG_TIMEOUT 4 +#define KICK_MSG_BANNED 5 +#define KICK_MSG_PING_HIGH 6 +#define KICK_MSG_CUSTOM_KICK 7 +#define KICK_MSG_CUSTOM_BAN 8 +#define KICK_MSG_KEEP_BODY 0x80 + +#endif From d1c5f7adcc02d2c0d5a49a9b2f4c0997821212ae Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 7 Jan 2023 13:01:48 +0100 Subject: [PATCH 36/89] Move gamestate handling to a new file --- src/hu_stuff.c | 1 + src/netcode/Sourcefile | 1 + src/netcode/d_clisrv.c | 308 +---------------------------- src/netcode/d_clisrv.h | 5 - src/netcode/gamestate.c | 341 ++++++++++++++++++++++++++++++++ src/netcode/gamestate.h | 31 +++ src/netcode/net_command.c | 1 + src/netcode/server_connection.c | 1 + src/netcode/tic_command.c | 1 + 9 files changed, 378 insertions(+), 312 deletions(-) create mode 100644 src/netcode/gamestate.c create mode 100644 src/netcode/gamestate.h diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e08351a8a..7ec33bd79 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -21,6 +21,7 @@ #include "netcode/d_clisrv.h" #include "netcode/net_command.h" +#include "netcode/gamestate.h" #include "g_game.h" #include "g_input.h" diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile index 1039b218a..c59050367 100644 --- a/src/netcode/Sourcefile +++ b/src/netcode/Sourcefile @@ -3,6 +3,7 @@ server_connection.c client_connection.c tic_command.c net_command.c +gamestate.c d_net.c d_netcmd.c d_netfil.c diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index b834c92f5..8ba10f6ea 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -53,6 +53,7 @@ #include "client_connection.h" #include "tic_command.h" #include "net_command.h" +#include "gamestate.h" #include "protocol.h" // @@ -93,18 +94,12 @@ tic_t maketic; INT16 consistancy[BACKUPTICS]; -UINT8 hu_redownloadinggamestate = 0; - // true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks boolean hu_stopped = false; UINT8 adminpassmd5[16]; boolean adminpasswordset = false; -// Client specific -// here it is for the secondary local player (splitscreen) -static boolean cl_redownloadinggamestate = false; - tic_t neededtic; SINT8 servernode = 0; // the number of the server node @@ -122,233 +117,6 @@ consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_c // of 512 bytes is like 0.1) UINT16 software_MAXPACKETLENGTH; -#define SAVEGAMESIZE (768*1024) - -boolean SV_ResendingSavegameToAnyone(void) -{ - INT32 i; - - for (i = 0; i < MAXNETNODES; i++) - if (netnodes[i].resendingsavegame) - return true; - return false; -} - -void SV_SendSaveGame(INT32 node, boolean resending) -{ - size_t length, compressedlen; - UINT8 *savebuffer; - UINT8 *compressedsave; - UINT8 *buffertosend; - - // first save it in a malloced buffer - savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); - if (!savebuffer) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - // Leave room for the uncompressed length. - save_p = savebuffer + sizeof(UINT32); - - P_SaveNetGame(resending); - - length = save_p - savebuffer; - if (length > SAVEGAMESIZE) - { - free(savebuffer); - save_p = NULL; - I_Error("Savegame buffer overrun"); - } - - // Allocate space for compressed save: one byte fewer than for the - // uncompressed data to ensure that the compression is worthwhile. - compressedsave = malloc(length - 1); - if (!compressedsave) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - // Attempt to compress it. - if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) - { - // Compressing succeeded; send compressed data - - free(savebuffer); - - // State that we're compressed. - buffertosend = compressedsave; - WRITEUINT32(compressedsave, length - sizeof(UINT32)); - length = compressedlen + sizeof(UINT32); - } - else - { - // Compression failed to make it smaller; send original - - free(compressedsave); - - // State that we're not compressed - buffertosend = savebuffer; - WRITEUINT32(savebuffer, 0); - } - - AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); - save_p = NULL; - - // Remember when we started sending the savegame so we can handle timeouts - netnodes[node].sendingsavegame = true; - netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte -} - -#ifdef DUMPCONSISTENCY -#define TMPSAVENAME "badmath.sav" -static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -static void SV_SavedGame(void) -{ - size_t length; - UINT8 *savebuffer; - char tmpsave[256]; - - if (!cv_dumpconsistency.value) - return; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - // first save it in a malloced buffer - save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); - if (!save_p) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - P_SaveNetGame(false); - - length = save_p - savebuffer; - if (length > SAVEGAMESIZE) - { - free(savebuffer); - save_p = NULL; - I_Error("Savegame buffer overrun"); - } - - // then save it! - if (!FIL_WriteFile(tmpsave, savebuffer, length)) - CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); - - free(savebuffer); - save_p = NULL; -} - -#undef TMPSAVENAME -#endif -#define TMPSAVENAME "$$$.sav" - - -void CL_LoadReceivedSavegame(boolean reloading) -{ - UINT8 *savebuffer = NULL; - size_t length, decompressedlen; - char tmpsave[256]; - - FreeFileNeeded(); - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - length = FIL_ReadFile(tmpsave, &savebuffer); - - CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); - if (!length) - { - I_Error("Can't read savegame sent"); - return; - } - - save_p = savebuffer; - - // Decompress saved game if necessary. - decompressedlen = READUINT32(save_p); - if(decompressedlen > 0) - { - UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); - lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); - Z_Free(savebuffer); - save_p = savebuffer = decompressedbuffer; - } - - paused = false; - demoplayback = false; - titlemapinaction = TITLEMAP_OFF; - titledemo = false; - automapactive = false; - - P_StopRumble(NULL); - - // load a base level - if (P_LoadNetGame(reloading)) - { - const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; - CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); - if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) - { - CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); - if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - CONS_Printf(M_GetText(" Zone")); - if (actnum > 0) - CONS_Printf(" %2d", actnum); - } - CONS_Printf("\"\n"); - } - - // done - Z_Free(savebuffer); - save_p = NULL; - if (unlink(tmpsave) == -1) - CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); - consistancy[gametic%BACKUPTICS] = Consistancy(); - CON_ToggleOff(); - - // Tell the server we have received and reloaded the gamestate - // so they know they can resume the game - netbuffer->packettype = PT_RECEIVEDGAMESTATE; - HSendPacket(servernode, true, 0, 0); -} - -static void CL_ReloadReceivedSavegame(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - LUA_InvalidatePlayer(&players[i]); - sprintf(player_names[i], "Player %d", i + 1); - } - - CL_LoadReceivedSavegame(true); - - if (neededtic < gametic) - neededtic = gametic; - maketic = neededtic; - - ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; - P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16)); - if (splitscreen) - { - ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn; - P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16)); - } - - camera.subsector = R_PointInSubsector(camera.x, camera.y); - camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); - - cl_redownloadinggamestate = false; - - CONS_Printf(M_GetText("Game state reloaded\n")); -} - typedef struct banreason_s { char *reason; @@ -1006,34 +774,6 @@ static void Command_Kick(void) CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); } -static void Command_ResendGamestate(void) -{ - SINT8 playernum; - - if (COM_Argc() == 1) - { - CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); - return; - } - else if (client) - { - CONS_Printf(M_GetText("Only the server can use this.\n")); - return; - } - - playernum = nametonum(COM_Argv(1)); - if (playernum == -1 || playernum == 0) - return; - - // Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - if (!HSendPacket(playernode[playernum], true, 0, 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n")); - return; - } -} - static void Got_KickCmd(UINT8 **p, INT32 playernum) { INT32 pnum, msg; @@ -1736,17 +1476,6 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) netnodes[node].ingame = false; } -static void PT_CanReceiveGamestate(SINT8 node) -{ - if (client || netnodes[node].sendingsavegame) - return; - - CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]); - - SV_SendSaveGame(node, true); // Resend a complete game state - netnodes[node].resendingsavegame = true; -} - static void PT_AskLuaFile(SINT8 node) { if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) @@ -1759,13 +1488,6 @@ static void PT_HasLuaFile(SINT8 node) SV_HandleLuaFileSent(node); } -static void PT_ReceivedGamestate(SINT8 node) -{ - netnodes[node].sendingsavegame = false; - netnodes[node].resendingsavegame = false; - netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE; -} - static void PT_Ping(SINT8 node, INT32 netconsole) { // Only accept PT_PING from the server. @@ -1789,34 +1511,6 @@ static void PT_Ping(SINT8 node, INT32 netconsole) } } -static void PT_WillResendGamestate(SINT8 node) -{ - (void)node; - - char tmpsave[256]; - - if (server || cl_redownloadinggamestate) - return; - - // Send back a PT_CANRECEIVEGAMESTATE packet to the server - // so they know they can start sending the game state - netbuffer->packettype = PT_CANRECEIVEGAMESTATE; - if (!HSendPacket(servernode, true, 0, 0)) - return; - - CONS_Printf(M_GetText("Reloading game state...\n")); - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - // Don't get a corrupt savegame error because tmpsave already exists - if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) - I_Error("Can't delete %s\n", tmpsave); - - CL_PrepareDownloadSaveGame(tmpsave); - - cl_redownloadinggamestate = true; -} - static void PT_SendingLuaFile(SINT8 node) { (void)node; diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 691a2787f..d5bafe750 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -89,19 +89,16 @@ void NetUpdate(void); void GetPackets(void); void ResetNode(INT32 node); INT16 Consistancy(void); -boolean SV_ResendingSavegameToAnyone(void); void SV_StartSinglePlayerServer(void); void SV_SpawnServer(void); void SV_StopServer(void); void SV_ResetServer(void); -void SV_SendSaveGame(INT32 node, boolean resending); void CL_AddSplitscreenPlayer(void); void CL_RemoveSplitscreenPlayer(void); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_RemovePlayer(INT32 playernum, kickreason_t reason); -void CL_LoadReceivedSavegame(boolean reloading); // Is there a game running boolean Playing(void); @@ -130,8 +127,6 @@ tic_t GetLag(INT32 node); void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest); -extern UINT8 hu_redownloadinggamestate; - extern UINT8 adminpassmd5[16]; extern boolean adminpasswordset; diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c new file mode 100644 index 000000000..4a3e9f3af --- /dev/null +++ b/src/netcode/gamestate.c @@ -0,0 +1,341 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 gamestate.c +/// \brief Gamestate (re)sending + +#include "d_clisrv.h" +#include "d_netfil.h" +#include "gamestate.h" +#include "i_net.h" +#include "protocol.h" +#include "server_connection.h" +#include "../am_map.h" +#include "../byteptr.h" +#include "../console.h" +#include "../d_main.h" +#include "../doomstat.h" +#include "../doomtype.h" +#include "../f_finale.h" +#include "../g_demo.h" +#include "../g_game.h" +#include "../i_time.h" +#include "../lua_script.h" +#include "../lzf.h" +#include "../m_misc.h" +#include "../p_haptic.h" +#include "../p_local.h" +#include "../p_saveg.h" +#include "../r_main.h" +#include "../tables.h" +#include "../z_zone.h" + +#define SAVEGAMESIZE (768*1024) + +UINT8 hu_redownloadinggamestate = 0; +boolean cl_redownloadinggamestate = false; + +boolean SV_ResendingSavegameToAnyone(void) +{ + INT32 i; + + for (i = 0; i < MAXNETNODES; i++) + if (netnodes[i].resendingsavegame) + return true; + return false; +} + +void SV_SendSaveGame(INT32 node, boolean resending) +{ + size_t length, compressedlen; + UINT8 *savebuffer; + UINT8 *compressedsave; + UINT8 *buffertosend; + + // first save it in a malloced buffer + savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); + if (!savebuffer) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + // Leave room for the uncompressed length. + save_p = savebuffer + sizeof(UINT32); + + P_SaveNetGame(resending); + + length = save_p - savebuffer; + if (length > SAVEGAMESIZE) + { + free(savebuffer); + save_p = NULL; + I_Error("Savegame buffer overrun"); + } + + // Allocate space for compressed save: one byte fewer than for the + // uncompressed data to ensure that the compression is worthwhile. + compressedsave = malloc(length - 1); + if (!compressedsave) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + // Attempt to compress it. + if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) + { + // Compressing succeeded; send compressed data + + free(savebuffer); + + // State that we're compressed. + buffertosend = compressedsave; + WRITEUINT32(compressedsave, length - sizeof(UINT32)); + length = compressedlen + sizeof(UINT32); + } + else + { + // Compression failed to make it smaller; send original + + free(compressedsave); + + // State that we're not compressed + buffertosend = savebuffer; + WRITEUINT32(savebuffer, 0); + } + + AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); + save_p = NULL; + + // Remember when we started sending the savegame so we can handle timeouts + netnodes[node].sendingsavegame = true; + netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte +} + +#ifdef DUMPCONSISTENCY +#define TMPSAVENAME "badmath.sav" +static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +void SV_SavedGame(void) +{ + size_t length; + UINT8 *savebuffer; + char tmpsave[256]; + + if (!cv_dumpconsistency.value) + return; + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + // first save it in a malloced buffer + save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); + if (!save_p) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + P_SaveNetGame(false); + + length = save_p - savebuffer; + if (length > SAVEGAMESIZE) + { + free(savebuffer); + save_p = NULL; + I_Error("Savegame buffer overrun"); + } + + // then save it! + if (!FIL_WriteFile(tmpsave, savebuffer, length)) + CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); + + free(savebuffer); + save_p = NULL; +} + +#undef TMPSAVENAME +#endif +#define TMPSAVENAME "$$$.sav" + + +void CL_LoadReceivedSavegame(boolean reloading) +{ + UINT8 *savebuffer = NULL; + size_t length, decompressedlen; + char tmpsave[256]; + + FreeFileNeeded(); + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + length = FIL_ReadFile(tmpsave, &savebuffer); + + CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); + if (!length) + { + I_Error("Can't read savegame sent"); + return; + } + + save_p = savebuffer; + + // Decompress saved game if necessary. + decompressedlen = READUINT32(save_p); + if(decompressedlen > 0) + { + UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); + lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); + Z_Free(savebuffer); + save_p = savebuffer = decompressedbuffer; + } + + paused = false; + demoplayback = false; + titlemapinaction = TITLEMAP_OFF; + titledemo = false; + automapactive = false; + + P_StopRumble(NULL); + + // load a base level + if (P_LoadNetGame(reloading)) + { + const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; + CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); + if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) + { + CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); + if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + CONS_Printf(M_GetText(" Zone")); + if (actnum > 0) + CONS_Printf(" %2d", actnum); + } + CONS_Printf("\"\n"); + } + + // done + Z_Free(savebuffer); + save_p = NULL; + if (unlink(tmpsave) == -1) + CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); + consistancy[gametic%BACKUPTICS] = Consistancy(); + CON_ToggleOff(); + + // Tell the server we have received and reloaded the gamestate + // so they know they can resume the game + netbuffer->packettype = PT_RECEIVEDGAMESTATE; + HSendPacket(servernode, true, 0, 0); +} + +void CL_ReloadReceivedSavegame(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + LUA_InvalidatePlayer(&players[i]); + sprintf(player_names[i], "Player %d", i + 1); + } + + CL_LoadReceivedSavegame(true); + + if (neededtic < gametic) + neededtic = gametic; + maketic = neededtic; + + ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; + P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16)); + if (splitscreen) + { + ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn; + P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16)); + } + + camera.subsector = R_PointInSubsector(camera.x, camera.y); + camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); + + cl_redownloadinggamestate = false; + + CONS_Printf(M_GetText("Game state reloaded\n")); +} + +void Command_ResendGamestate(void) +{ + SINT8 playernum; + + if (COM_Argc() == 1) + { + CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); + return; + } + else if (client) + { + CONS_Printf(M_GetText("Only the server can use this.\n")); + return; + } + + playernum = nametonum(COM_Argv(1)); + if (playernum == -1 || playernum == 0) + return; + + // Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + if (!HSendPacket(playernode[playernum], true, 0, 0)) + { + CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n")); + return; + } +} + +void PT_CanReceiveGamestate(SINT8 node) +{ + if (client || netnodes[node].sendingsavegame) + return; + + CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]); + + SV_SendSaveGame(node, true); // Resend a complete game state + netnodes[node].resendingsavegame = true; +} + +void PT_ReceivedGamestate(SINT8 node) +{ + netnodes[node].sendingsavegame = false; + netnodes[node].resendingsavegame = false; + netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE; +} + +void PT_WillResendGamestate(SINT8 node) +{ + (void)node; + + char tmpsave[256]; + + if (server || cl_redownloadinggamestate) + return; + + // Send back a PT_CANRECEIVEGAMESTATE packet to the server + // so they know they can start sending the game state + netbuffer->packettype = PT_CANRECEIVEGAMESTATE; + if (!HSendPacket(servernode, true, 0, 0)) + return; + + CONS_Printf(M_GetText("Reloading game state...\n")); + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + // Don't get a corrupt savegame error because tmpsave already exists + if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) + I_Error("Can't delete %s\n", tmpsave); + + CL_PrepareDownloadSaveGame(tmpsave); + + cl_redownloadinggamestate = true; +} diff --git a/src/netcode/gamestate.h b/src/netcode/gamestate.h new file mode 100644 index 000000000..9d2779772 --- /dev/null +++ b/src/netcode/gamestate.h @@ -0,0 +1,31 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 gamestate.h +/// \brief Gamestate (re)sending + +#ifndef __GAMESTATE__ +#define __GAMESTATE__ + +#include "../doomtype.h" + +extern UINT8 hu_redownloadinggamestate; +extern boolean cl_redownloadinggamestate; + +boolean SV_ResendingSavegameToAnyone(void); +void SV_SendSaveGame(INT32 node, boolean resending); +void SV_SavedGame(void); +void CL_LoadReceivedSavegame(boolean reloading); +void CL_ReloadReceivedSavegame(void); +void Command_ResendGamestate(void); +void PT_CanReceiveGamestate(SINT8 node); +void PT_ReceivedGamestate(SINT8 node); +void PT_WillResendGamestate(SINT8 node); + +#endif diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index eeb479d21..6f9f3f79a 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -12,6 +12,7 @@ #include "net_command.h" #include "tic_command.h" +#include "gamestate.h" #include "d_clisrv.h" #include "i_net.h" #include "../g_game.h" diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index b776db422..28d97c052 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -16,6 +16,7 @@ #include "d_netfil.h" #include "mserv.h" #include "net_command.h" +#include "gamestate.h" #include "../byteptr.h" #include "../g_game.h" #include "../g_state.h" diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index e7df895ff..180d54e69 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -14,6 +14,7 @@ #include "d_clisrv.h" #include "net_command.h" #include "client_connection.h" +#include "gamestate.h" #include "i_net.h" #include "../d_main.h" #include "../g_game.h" From 94ba47a950ca0e01c664c5d03fa3af707b944253 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 7 Jan 2023 13:37:33 +0100 Subject: [PATCH 37/89] Remove unused stuff --- src/netcode/d_clisrv.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 8ba10f6ea..36116b931 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -69,13 +69,9 @@ // firstticstosend is used to optimize a condition // Normally maketic >= gametic > 0 -#define PREDICTIONQUEUE BACKUPTICS -#define PREDICTIONMASK (PREDICTIONQUEUE-1) #define MAX_REASONLENGTH 30 boolean server = true; // true or false but !server == client -#define client (!server) -boolean nodownload = false; boolean serverrunning = false; INT32 serverplayer = 0; char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) From dfdbdee0a823ee6f02946d8faf74d99465cf8160 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 7 Jan 2023 15:57:04 +0100 Subject: [PATCH 38/89] Move netcode console variables to appropriate files --- src/m_menu.c | 1 + src/netcode/client_connection.c | 1 + src/netcode/d_clisrv.c | 40 +++++++++------------------------ src/netcode/d_clisrv.h | 7 +----- src/netcode/d_netfil.c | 9 ++++++++ src/netcode/d_netfil.h | 2 ++ src/netcode/net_command.c | 1 + src/netcode/server_connection.c | 14 ++++++++++++ src/netcode/server_connection.h | 2 ++ src/p_tick.c | 1 + 10 files changed, 43 insertions(+), 35 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 06d1b1b96..34322200d 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -57,6 +57,7 @@ #include "netcode/d_net.h" #include "netcode/mserv.h" +#include "netcode/server_connection.h" #include "netcode/client_connection.h" #include "m_misc.h" #include "m_anigif.h" diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c index bfaea2e81..a00157a50 100644 --- a/src/netcode/client_connection.c +++ b/src/netcode/client_connection.c @@ -11,6 +11,7 @@ /// \brief Client connection handling #include "client_connection.h" +#include "gamestate.h" #include "d_clisrv.h" #include "d_netfil.h" #include "../d_main.h" diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 36116b931..0b1c487d8 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -103,11 +103,6 @@ SINT8 servernode = 0; // the number of the server node /// \todo WORK! boolean acceptnewnode = true; -consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; -consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); - // Some software don't support largest packet // (original sersetup, not exactely, but the probability of sending a packet // of 512 bytes is like 0.1) @@ -123,6 +118,17 @@ typedef struct banreason_s static banreason_t *reasontail = NULL; //last entry, use prev static banreason_t *reasonhead = NULL; //1st entry, use next +static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; +consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); + +static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; +consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); + +consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; +consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); + static void Command_ShowBan(void) //Print out ban list { size_t i; @@ -961,30 +967,6 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) CL_RemovePlayer(pnum, kickreason); } -static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; -consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); - -consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); -static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; -consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); -static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); -static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); - -static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; -consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); -consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -// max file size to send to a player (in kilobytes) -static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}}; -consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); -consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -// Speed of file downloading (in packets per tic) -static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; -consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); - static void Got_AddPlayer(UINT8 **p, INT32 playernum); // called one time at init diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index d5bafe750..9cb3ef3e8 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -37,9 +37,6 @@ extern INT32 mapchangepending; // Points inside doomcom extern doomdata_t *netbuffer; -extern consvar_t cv_showjoinaddress; -extern consvar_t cv_playbackspeed; - #define BASEPACKETSIZE offsetof(doomdata_t, u) #define BASESERVERTICSSIZE offsetof(doomdata_t, u.serverpak.cmds[0]) @@ -76,9 +73,7 @@ extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; -extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout; -extern consvar_t cv_resynchattempts, cv_blamecfail; -extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; +extern consvar_t cv_netticbuffer, cv_resynchattempts, cv_blamecfail, cv_playbackspeed; // Used in d_net, the only dependence void D_ClientServerInit(void); diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c index e581be2ac..205abf713 100644 --- a/src/netcode/d_netfil.c +++ b/src/netcode/d_netfil.c @@ -116,6 +116,15 @@ boolean waitingforluafiletransfer = false; boolean waitingforluafilecommand = false; char luafiledir[256 + 16] = "luafiles"; +// max file size to send to a player (in kilobytes) +static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}}; +consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); + +consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +// Speed of file downloading (in packets per tic) +static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; +consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); static UINT16 GetWadNumFromFileNeededId(UINT8 id) { diff --git a/src/netcode/d_netfil.h b/src/netcode/d_netfil.h index 732efcd5e..850e24d49 100644 --- a/src/netcode/d_netfil.h +++ b/src/netcode/d_netfil.h @@ -76,6 +76,8 @@ extern UINT32 downloadcompletedsize; extern INT32 totalfilesrequestednum; extern UINT32 totalfilesrequestedsize; +extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; + void AllocFileNeeded(INT32 size); void FreeFileNeeded(void); UINT8 *PutFileNeeded(UINT16 firstfile); diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 6f9f3f79a..95a49a959 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -13,6 +13,7 @@ #include "net_command.h" #include "tic_command.h" #include "gamestate.h" +#include "server_connection.h" #include "d_clisrv.h" #include "i_net.h" #include "../g_game.h" diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index 28d97c052..ce1192475 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -22,6 +22,7 @@ #include "../g_state.h" #include "../p_setup.h" #include "../p_tick.h" +#include "../command.h" #include "../doomstat.h" // Minimum timeout for sending the savegame @@ -38,6 +39,19 @@ char playeraddress[MAXPLAYERS][64]; UINT8 player_joining = false; +consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; +consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); + +static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); + +static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); + static INT32 FindRejoinerNum(SINT8 node) { char strippednodeaddress[64]; diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h index 4204db2e6..a40bd4603 100644 --- a/src/netcode/server_connection.h +++ b/src/netcode/server_connection.h @@ -13,6 +13,7 @@ #ifndef __D_SERVER_CONNECTION__ #define __D_SERVER_CONNECTION__ +#include "../command.h" #include "../doomdef.h" #include "../doomtype.h" @@ -25,5 +26,6 @@ extern tic_t jointimeout; extern tic_t joindelay; extern char playeraddress[MAXPLAYERS][64]; extern UINT8 player_joining; +extern consvar_t cv_showjoinaddress, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout; #endif diff --git a/src/p_tick.c b/src/p_tick.c index 8fb99d737..e92dfe8e4 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -27,6 +27,7 @@ #include "r_fps.h" #include "i_video.h" // rendermode #include "netcode/net_command.h" +#include "netcode/server_connection.h" // Object place #include "m_cheat.h" From 7240ed22de0260e7f1a9ea0fcc1118b166d63c11 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 7 Jan 2023 15:57:17 +0100 Subject: [PATCH 39/89] Add missing PT_ prefix to a few packet handlers --- src/netcode/client_connection.c | 4 ++-- src/netcode/client_connection.h | 2 +- src/netcode/d_clisrv.c | 12 ++++++------ src/netcode/server_connection.c | 2 +- src/netcode/server_connection.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c index a00157a50..bed445a64 100644 --- a/src/netcode/client_connection.c +++ b/src/netcode/client_connection.c @@ -1052,7 +1052,7 @@ void CL_ConnectToServer(void) * \note What happens if the packet comes from a client or something like that? * */ -void HandleServerInfo(SINT8 node) +void PT_ServerInfo(SINT8 node) { // compute ping in ms const tic_t ticnow = I_GetTime(); @@ -1173,7 +1173,7 @@ void PT_ServerCFG(SINT8 node) /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? /// Shouldn't them be downloaded even at intermission time? - /// Also, according to HandleConnect, the server will send the savegame even during intermission... + /// Also, according to PT_Connect, the server will send the savegame even during intermission... if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) cl_mode = CL_DOWNLOADSAVEGAME; diff --git a/src/netcode/client_connection.h b/src/netcode/client_connection.h index a76411ba6..74cff61ff 100644 --- a/src/netcode/client_connection.h +++ b/src/netcode/client_connection.h @@ -53,7 +53,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room); void CL_ConnectToServer(void); boolean CL_SendJoin(void); -void HandleServerInfo(SINT8 node); +void PT_ServerInfo(SINT8 node); void PT_MoreFilesNeeded(SINT8 node); void PT_ServerRefuse(SINT8 node); void PT_ServerCFG(SINT8 node); diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 0b1c487d8..4589c83d3 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1358,7 +1358,7 @@ void SV_StartSinglePlayerServer(void) * \param node The packet sender (should be the server) * */ -static void HandleShutdown(SINT8 node) +static void PT_Shutdown(SINT8 node) { (void)node; LUA_HookBool(false, HOOK(GameQuit)); @@ -1373,7 +1373,7 @@ static void HandleShutdown(SINT8 node) * \param node The packet sender (should be the server) * */ -static void HandleTimeout(SINT8 node) +static void PT_Timeout(SINT8 node) { (void)node; LUA_HookBool(false, HOOK(GameQuit)); @@ -1608,26 +1608,26 @@ void GetPackets(void) if (netbuffer->packettype == PT_CLIENTJOIN && server) { - HandleConnect(node); + PT_Connect(node); continue; } if (node == servernode && client && cl_mode != CL_SEARCHING) { if (netbuffer->packettype == PT_SERVERSHUTDOWN) { - HandleShutdown(node); + PT_Shutdown(node); continue; } if (netbuffer->packettype == PT_NODETIMEOUT) { - HandleTimeout(node); + PT_Timeout(node); continue; } } if (netbuffer->packettype == PT_SERVERINFO) { - HandleServerInfo(node); + PT_ServerInfo(node); continue; } diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index ce1192475..e37c45951 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -422,7 +422,7 @@ GetRefuseMessage (SINT8 node, INT32 rejoinernum) * \param node The packet sender * */ -void HandleConnect(SINT8 node) +void PT_Connect(SINT8 node) { char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; INT32 numplayers = netbuffer->u.clientcfg.localplayers; diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h index a40bd4603..585c9e428 100644 --- a/src/netcode/server_connection.h +++ b/src/netcode/server_connection.h @@ -17,7 +17,7 @@ #include "../doomdef.h" #include "../doomtype.h" -void HandleConnect(SINT8 node); +void PT_Connect(SINT8 node); void PT_AskInfoViaMS(SINT8 node); void PT_TellFilesNeeded(SINT8 node); void PT_AskInfo(SINT8 node); From 4a2fd43aff5e5604a70ce8c01946977f5f5af816 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 7 Jan 2023 16:14:41 +0100 Subject: [PATCH 40/89] Add missing _pak suffix to a few packet structures --- src/netcode/protocol.h | 8 ++++---- src/netcode/server_connection.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h index 566d10d8c..1eba9286b 100644 --- a/src/netcode/protocol.h +++ b/src/netcode/protocol.h @@ -260,7 +260,7 @@ typedef struct UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused. UINT32 score; UINT16 timeinserver; // In seconds. -} ATTRPACK plrinfo; +} ATTRPACK plrinfo_pak; // Shortest player information for join during intermission. typedef struct @@ -271,7 +271,7 @@ typedef struct UINT32 pflags; UINT32 score; UINT8 ctfteam; -} ATTRPACK plrconfig; +} ATTRPACK plrconfig_pak; typedef struct { @@ -308,8 +308,8 @@ typedef struct serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) askinfo_pak askinfo; // 61 bytes msaskinfo_pak msaskinfo; // 22 bytes - plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?) - plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) + plrinfo_pak playerinfo[MAXPLAYERS]; // 576 bytes(?) + plrconfig_pak playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) INT32 filesneedednum; // 4 bytes filesneededconfig_pak filesneededcfg; // ??? bytes UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index e37c45951..924aa82d6 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -219,7 +219,7 @@ static void SV_SendPlayerInfo(INT32 node) netbuffer->u.playerinfo[i].data |= 0x80; } - HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); + HSendPacket(node, false, 0, sizeof(plrinfo_pak) * MAXPLAYERS); } /** Sends a PT_SERVERCFG packet From fef13b630cd674ec23876d9fcc022a2d9c75776b Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 8 Jan 2023 00:43:18 +0100 Subject: [PATCH 41/89] Remove outdated or misleading comments --- src/netcode/client_connection.c | 11 ++--------- src/netcode/d_clisrv.c | 14 ++------------ src/netcode/protocol.h | 32 ++++++++++++++++---------------- src/netcode/server_connection.c | 2 +- src/netcode/tic_command.c | 8 -------- 5 files changed, 21 insertions(+), 46 deletions(-) diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c index bed445a64..1adc3a65e 100644 --- a/src/netcode/client_connection.c +++ b/src/netcode/client_connection.c @@ -36,7 +36,7 @@ cl_mode_t cl_mode = CL_SEARCHING; static UINT16 cl_lastcheckedfilecount = 0; // used for full file list 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; -UINT8 mynode; // my address pointofview server +UINT8 mynode; static void *snake = NULL; static void CL_DrawConnectionStatusBox(void) @@ -226,12 +226,9 @@ static boolean CL_AskFileList(INT32 firstfile) return HSendPacket(servernode, false, 0, sizeof (INT32)); } -/** Sends a special packet to declare how many players in local - * Used only in arbitratrenetstart() - * Sends a PT_CLIENTJOIN packet to the server +/** Sends a PT_CLIENTJOIN packet to the server * * \return True if the packet was successfully sent - * \todo Improve the description... * */ boolean CL_SendJoin(void) @@ -928,10 +925,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) FileReceiveTicker(); - // why are these here? this is for servers, we're a client - //if (key == 's' && server) - // doomcom->numnodes = (INT16)pnumnodes; - //FileSendTicker(); *oldtic = I_GetTime(); if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 4589c83d3..eef9bdfe5 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -99,13 +99,8 @@ boolean adminpasswordset = false; tic_t neededtic; SINT8 servernode = 0; // the number of the server node -/// \brief do we accept new players? -/// \todo WORK! boolean acceptnewnode = true; -// Some software don't support largest packet -// (original sersetup, not exactely, but the probability of sending a packet -// of 512 bytes is like 0.1) UINT16 software_MAXPACKETLENGTH; typedef struct banreason_s @@ -300,9 +295,6 @@ static void Command_connect(void) return; } - // modified game check: no longer handled - // we don't request a restart unless the filelist differs - server = false; /* if (!stricmp(COM_Argv(1), "self")) @@ -1768,7 +1760,7 @@ boolean TryRunTics(tic_t realtics) boolean ticking; // the machine has lagged but it is not so bad - if (realtics > TICRATE/7) // FIXME: consistency failure!! + if (realtics > TICRATE/7) { if (server) realtics = 1; @@ -1802,8 +1794,6 @@ boolean TryRunTics(tic_t realtics) #ifdef DEBUGFILE if (debugfile && (realtics || neededtic > gametic)) { - //SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5. - //Shut up stupid warning! fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n", realtics, neededtic, gametic, debugload); debugload = 100000; @@ -1983,7 +1973,7 @@ void NetUpdate(void) if (client) maketic = neededtic; - Local_Maketic(realtics); // make local tic, and call menu? + Local_Maketic(realtics); if (server) CL_SendClientCmd(); // send it diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h index 1eba9286b..cbeb37f36 100644 --- a/src/netcode/protocol.h +++ b/src/netcode/protocol.h @@ -294,25 +294,25 @@ typedef struct UINT8 reserved; // Padding union { - clientcmd_pak clientpak; // 144 bytes - client2cmd_pak client2pak; // 200 bytes - servertics_pak serverpak; // 132495 bytes (more around 360, no?) - serverconfig_pak servercfg; // 773 bytes - UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) - filetx_pak filetxpak; // 139 bytes + clientcmd_pak clientpak; + client2cmd_pak client2pak; + servertics_pak serverpak; + serverconfig_pak servercfg; + UINT8 textcmd[MAXTEXTCMD+1]; + filetx_pak filetxpak; fileack_pak fileack; UINT8 filereceived; - clientconfig_pak clientcfg; // 136 bytes + clientconfig_pak clientcfg; UINT8 md5sum[16]; - serverinfo_pak serverinfo; // 1024 bytes - serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) - askinfo_pak askinfo; // 61 bytes - msaskinfo_pak msaskinfo; // 22 bytes - plrinfo_pak playerinfo[MAXPLAYERS]; // 576 bytes(?) - plrconfig_pak playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) - INT32 filesneedednum; // 4 bytes - filesneededconfig_pak filesneededcfg; // ??? bytes - UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes + serverinfo_pak serverinfo; + serverrefuse_pak serverrefuse; + askinfo_pak askinfo; + msaskinfo_pak msaskinfo; + plrinfo_pak playerinfo[MAXPLAYERS]; + plrconfig_pak playerconfig[MAXPLAYERS]; + INT32 filesneedednum; + filesneededconfig_pak filesneededcfg; + UINT32 pingtable[MAXPLAYERS+1]; } u; // This is needed to pack diff packet types data together } ATTRPACK doomdata_t; diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index 924aa82d6..f2b0306e6 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -460,7 +460,7 @@ void PT_Connect(SINT8 node) ResetNode(node); SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); /// \todo fix this !!! - return; // restart the while + return; } DEBFILE("new node joined\n"); diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index 180d54e69..bab0fad0a 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -262,11 +262,6 @@ void PT_ServerTics(SINT8 node, INT32 netconsole) else { DEBFILE(va("frame not in bound: %u\n", neededtic)); - /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) - I_Error("Received an out of order PT_SERVERTICS packet!\n" - "Got tics %d-%d, needed tic %d\n\n" - "Please report this crash on the Master Board,\n" - "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ } } @@ -447,9 +442,6 @@ void SV_SendTics(void) netnodes[0].supposedtic = maketic; } -// -// TryRunTics -// void Local_Maketic(INT32 realtics) { I_OsPolling(); // I_Getevent From db85e956c993bd52b4feff5b528c91d1616c9a7b Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 8 Jan 2023 00:46:12 +0100 Subject: [PATCH 42/89] Rename packet handlers to match their associated PT_ constants --- src/netcode/client_connection.c | 2 +- src/netcode/d_clisrv.c | 4 ++-- src/netcode/server_connection.c | 2 +- src/netcode/server_connection.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c index 1adc3a65e..ff20a6fc7 100644 --- a/src/netcode/client_connection.c +++ b/src/netcode/client_connection.c @@ -1166,7 +1166,7 @@ void PT_ServerCFG(SINT8 node) /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? /// Shouldn't them be downloaded even at intermission time? - /// Also, according to PT_Connect, the server will send the savegame even during intermission... + /// Also, according to PT_ClientJoin, the server will send the savegame even during intermission... if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) cl_mode = CL_DOWNLOADSAVEGAME; diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index eef9bdfe5..7fc64b456 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1350,7 +1350,7 @@ void SV_StartSinglePlayerServer(void) * \param node The packet sender (should be the server) * */ -static void PT_Shutdown(SINT8 node) +static void PT_ServerShutdown(SINT8 node) { (void)node; LUA_HookBool(false, HOOK(GameQuit)); @@ -1365,7 +1365,7 @@ static void PT_Shutdown(SINT8 node) * \param node The packet sender (should be the server) * */ -static void PT_Timeout(SINT8 node) +static void PT_NodeTimeout(SINT8 node) { (void)node; LUA_HookBool(false, HOOK(GameQuit)); diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index f2b0306e6..3e7d46ef6 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -422,7 +422,7 @@ GetRefuseMessage (SINT8 node, INT32 rejoinernum) * \param node The packet sender * */ -void PT_Connect(SINT8 node) +void PT_ClientJoin(SINT8 node) { char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; INT32 numplayers = netbuffer->u.clientcfg.localplayers; diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h index 585c9e428..96f7566ba 100644 --- a/src/netcode/server_connection.h +++ b/src/netcode/server_connection.h @@ -17,7 +17,7 @@ #include "../doomdef.h" #include "../doomtype.h" -void PT_Connect(SINT8 node); +void PT_ClientJoin(SINT8 node); void PT_AskInfoViaMS(SINT8 node); void PT_TellFilesNeeded(SINT8 node); void PT_AskInfo(SINT8 node); From 75b52171d2908896617d77cfb8a943876439773b Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 8 Jan 2023 00:47:33 +0100 Subject: [PATCH 43/89] Call all packet handlers from the same place --- src/netcode/d_clisrv.c | 41 +++++++++++---------------------- src/netcode/server_connection.c | 2 +- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 7fc64b456..c004eeede 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1352,6 +1352,9 @@ void SV_StartSinglePlayerServer(void) */ static void PT_ServerShutdown(SINT8 node) { + if (node != servernode || server || cl_mode == CL_SEARCHING) + return; + (void)node; LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); @@ -1412,7 +1415,11 @@ static void PT_Login(SINT8 node, INT32 netconsole) static void PT_ClientQuit(SINT8 node, INT32 netconsole) { if (client) + { + if (node == servernode && cl_mode != CL_SEARCHING && netbuffer->packettype == PT_NODETIMEOUT) + PT_NodeTimeout(node); return; + } if (!netnodes[node].ingame) { @@ -1505,6 +1512,7 @@ static void HandlePacketFromAwayNode(SINT8 node) switch (netbuffer->packettype) { case PT_ASKINFOVIAMS : PT_AskInfoViaMS (node ); break; + case PT_SERVERINFO : PT_ServerInfo (node ); break; case PT_TELLFILESNEEDED: PT_TellFilesNeeded(node ); break; case PT_MOREFILESNEEDED: PT_MoreFilesNeeded(node ); break; case PT_ASKINFO : PT_AskInfo (node ); break; @@ -1517,6 +1525,8 @@ static void HandlePacketFromAwayNode(SINT8 node) case PT_NODETIMEOUT : PT_ClientQuit (node, -1); break; case PT_CLIENTQUIT : PT_ClientQuit (node, -1); break; case PT_SERVERTICS : PT_ServerTics (node, -1); break; + case PT_CLIENTJOIN : PT_ClientJoin (node ); break; + case PT_SERVERSHUTDOWN : PT_ServerShutdown (node ); break; case PT_CLIENTCMD : break; // This is not an "unknown packet" default: @@ -1566,6 +1576,7 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_ASKLUAFILE : PT_AskLuaFile (node ); break; case PT_HASLUAFILE : PT_HasLuaFile (node ); break; case PT_RECEIVEDGAMESTATE : PT_ReceivedGamestate (node ); break; + case PT_SERVERINFO : PT_ServerInfo (node ); break; // CLIENT RECEIVE case PT_SERVERTICS : PT_ServerTics (node, netconsole); break; @@ -1575,7 +1586,9 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_FILERECEIVED : PT_FileReceived (node ); break; case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node ); break; case PT_SENDINGLUAFILE : PT_SendingLuaFile (node ); break; + case PT_SERVERSHUTDOWN : PT_ServerShutdown (node ); break; case PT_SERVERCFG : break; + case PT_CLIENTJOIN : break; default: DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", @@ -1598,34 +1611,6 @@ void GetPackets(void) { node = (SINT8)doomcom->remotenode; - if (netbuffer->packettype == PT_CLIENTJOIN && server) - { - PT_Connect(node); - continue; - } - if (node == servernode && client && cl_mode != CL_SEARCHING) - { - if (netbuffer->packettype == PT_SERVERSHUTDOWN) - { - PT_Shutdown(node); - continue; - } - if (netbuffer->packettype == PT_NODETIMEOUT) - { - PT_Timeout(node); - continue; - } - } - - if (netbuffer->packettype == PT_SERVERINFO) - { - PT_ServerInfo(node); - continue; - } - - if (netbuffer->packettype == PT_PLAYERINFO) - continue; // We do nothing with PLAYERINFO, that's for the MS browser. - // Packet received from someone already playing if (netnodes[node].ingame) HandlePacketFromPlayer(node); diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index 3e7d46ef6..9b41da920 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -430,7 +430,7 @@ void PT_ClientJoin(SINT8 node) INT32 i; // Ignore duplicate packets - if (netnodes[node].ingame) + if (client || netnodes[node].ingame) return; rejoinernum = FindRejoinerNum(node); From 62986da9da31cce87a7c551c63239fdf3c695378 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 8 Jan 2023 13:27:08 +0100 Subject: [PATCH 44/89] Remove useless variable --- src/netcode/d_clisrv.c | 9 --------- src/netcode/server_connection.c | 3 --- src/netcode/server_connection.h | 1 - 3 files changed, 13 deletions(-) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index c004eeede..ecd7c9bbe 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1605,8 +1605,6 @@ void GetPackets(void) { SINT8 node; // The packet sender - player_joining = false; - while (HGetPacket()) { node = (SINT8)doomcom->remotenode; @@ -1793,13 +1791,6 @@ boolean TryRunTics(tic_t realtics) hu_stopped = false; } - if (player_joining) - { - if (realtics) - hu_stopped = true; - return false; - } - if (ticking) { if (advancedemo) diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index 9b41da920..ed0e28309 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -37,8 +37,6 @@ tic_t joindelay = 0; // The actual timeout will be longer depending on the savegame length char playeraddress[MAXPLAYERS][64]; -UINT8 player_joining = false; - consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); @@ -475,7 +473,6 @@ void PT_ClientJoin(SINT8 node) SV_AddPlayer(node, names[i]); joindelay += cv_joindelay.value * TICRATE; - player_joining = true; } void PT_AskInfoViaMS(SINT8 node) diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h index 96f7566ba..7481d0eb5 100644 --- a/src/netcode/server_connection.h +++ b/src/netcode/server_connection.h @@ -25,7 +25,6 @@ void PT_AskInfo(SINT8 node); extern tic_t jointimeout; extern tic_t joindelay; extern char playeraddress[MAXPLAYERS][64]; -extern UINT8 player_joining; extern consvar_t cv_showjoinaddress, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout; #endif From 5a608120ff577c645ca7ec5732394c74a6520c8a Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 8 Jan 2023 16:28:41 +0100 Subject: [PATCH 45/89] Simplify convoluted code --- src/netcode/d_clisrv.c | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index ecd7c9bbe..5bb2c4600 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1421,15 +1421,9 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) return; } - if (!netnodes[node].ingame) - { - Net_CloseConnection(node); - return; - } - // nodeingame will be put false in the execution of kick command // this allow to send some packets to the quitting client to have their ack back - if (netconsole != -1 && playeringame[netconsole]) + if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole]) { UINT8 kickmsg; @@ -1440,17 +1434,18 @@ static void PT_ClientQuit(SINT8 node, INT32 netconsole) kickmsg |= KICK_MSG_KEEP_BODY; SendKick(netconsole, kickmsg); - netnodes[node].player = -1; if (netnodes[node].player2 != -1 && netnodes[node].player2 >= 0 && playeringame[(UINT8)netnodes[node].player2]) { SendKick(netnodes[node].player2, kickmsg); - netnodes[node].player2 = -1; } } + Net_CloseConnection(node); netnodes[node].ingame = false; + netnodes[node].player = -1; + netnodes[node].player2 = -1; } static void PT_AskLuaFile(SINT8 node) @@ -1603,11 +1598,9 @@ static void HandlePacketFromPlayer(SINT8 node) */ void GetPackets(void) { - SINT8 node; // The packet sender - while (HGetPacket()) { - node = (SINT8)doomcom->remotenode; + SINT8 node = doomcom->remotenode; // Packet received from someone already playing if (netnodes[node].ingame) @@ -1740,8 +1733,6 @@ If they're not lagging, decrement the timer by 1. Of course, reset all of this i boolean TryRunTics(tic_t realtics) { - boolean ticking; - // the machine has lagged but it is not so bad if (realtics > TICRATE/7) { @@ -1783,16 +1774,11 @@ boolean TryRunTics(tic_t realtics) } #endif - ticking = neededtic > gametic; - - if (ticking) + if (neededtic > gametic) { if (realtics) hu_stopped = false; - } - if (ticking) - { if (advancedemo) { if (timedemo_quit) @@ -1826,16 +1812,19 @@ boolean TryRunTics(tic_t realtics) if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) break; } + + return true; } else { if (realtics) hu_stopped = true; - } - return ticking; + return false; + } } + static INT32 pingtimeout[MAXPLAYERS]; static inline void PingUpdate(void) @@ -1976,8 +1965,6 @@ void NetUpdate(void) { if (!demoplayback) { - INT32 counts; - hu_redownloadinggamestate = false; firstticstosend = gametic; @@ -1991,8 +1978,7 @@ void NetUpdate(void) } // Don't erase tics not acknowledged - counts = realtics; - + INT32 counts = realtics; if (maketic + counts >= firstticstosend + BACKUPTICS) counts = firstticstosend+BACKUPTICS-maketic-1; From 1eb801399b2e8c9a181c4924cb9be939a7898c9e Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 8 Jan 2023 16:33:21 +0100 Subject: [PATCH 46/89] Move comment to an appropriate place --- src/netcode/d_clisrv.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 5bb2c4600..b0d534b84 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1725,12 +1725,6 @@ INT16 Consistancy(void) return (INT16)(ret & 0xFFFF); } -/* -Ping Update except better: -We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. -If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. -*/ - boolean TryRunTics(tic_t realtics) { // the machine has lagged but it is not so bad @@ -1824,6 +1818,11 @@ boolean TryRunTics(tic_t realtics) } } +/* +Ping Update except better: +We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. +If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. +*/ static INT32 pingtimeout[MAXPLAYERS]; From 62cfb0eb81ac9e3216afbced1c02fe8092ba880b Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Mon, 9 Jan 2023 21:39:33 +0100 Subject: [PATCH 47/89] Fix clients not being able to join servers --- src/netcode/d_clisrv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index b0d534b84..6680c7e42 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1523,6 +1523,7 @@ static void HandlePacketFromAwayNode(SINT8 node) case PT_CLIENTJOIN : PT_ClientJoin (node ); break; case PT_SERVERSHUTDOWN : PT_ServerShutdown (node ); break; case PT_CLIENTCMD : break; // This is not an "unknown packet" + case PT_PLAYERINFO : break; // This is not an "unknown packet" default: DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); From 18bfc3266aa30c81acb6ae91926231ba29126329 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Mon, 9 Jan 2023 22:14:58 +0100 Subject: [PATCH 48/89] Remove reboundstore timeout hack --- src/netcode/d_clisrv.c | 50 ++++++++------------------------------- src/netcode/d_clisrv.h | 1 + src/netcode/d_net.c | 19 +++++---------- src/netcode/net_command.c | 10 ++++++++ src/netcode/net_command.h | 1 + src/netcode/protocol.h | 1 - 6 files changed, 28 insertions(+), 54 deletions(-) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 6680c7e42..81d7e5543 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -479,6 +479,15 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) P_CheckRacers(); } +void CL_HandleTimeout(void) +{ + LUA_HookBool(false, HOOK(GameQuit)); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); +} + void CL_Reset(void) { if (metalrecording) @@ -1363,21 +1372,6 @@ static void PT_ServerShutdown(SINT8 node) M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); } -/** Called when a PT_NODETIMEOUT packet is received - * - * \param node The packet sender (should be the server) - * - */ -static void PT_NodeTimeout(SINT8 node) -{ - (void)node; - LUA_HookBool(false, HOOK(GameQuit)); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); -} - static void PT_Login(SINT8 node, INT32 netconsole) { (void)node; @@ -1415,32 +1409,10 @@ static void PT_Login(SINT8 node, INT32 netconsole) static void PT_ClientQuit(SINT8 node, INT32 netconsole) { if (client) - { - if (node == servernode && cl_mode != CL_SEARCHING && netbuffer->packettype == PT_NODETIMEOUT) - PT_NodeTimeout(node); return; - } - // nodeingame will be put false in the execution of kick command - // this allow to send some packets to the quitting client to have their ack back if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole]) - { - UINT8 kickmsg; - - if (netbuffer->packettype == PT_NODETIMEOUT) - kickmsg = KICK_MSG_TIMEOUT; - else - kickmsg = KICK_MSG_PLAYER_QUIT; - kickmsg |= KICK_MSG_KEEP_BODY; - - SendKick(netconsole, kickmsg); - - if (netnodes[node].player2 != -1 && netnodes[node].player2 >= 0 - && playeringame[(UINT8)netnodes[node].player2]) - { - SendKick(netnodes[node].player2, kickmsg); - } - } + SendKicksForNode(node, KICK_MSG_PLAYER_QUIT | KICK_MSG_KEEP_BODY); Net_CloseConnection(node); netnodes[node].ingame = false; @@ -1517,7 +1489,6 @@ static void HandlePacketFromAwayNode(SINT8 node) case PT_FILEACK : PT_FileAck (node ); break; case PT_FILERECEIVED : PT_FileReceived (node ); break; case PT_REQUESTFILE : PT_RequestFile (node ); break; - case PT_NODETIMEOUT : PT_ClientQuit (node, -1); break; case PT_CLIENTQUIT : PT_ClientQuit (node, -1); break; case PT_SERVERTICS : PT_ServerTics (node, -1); break; case PT_CLIENTJOIN : PT_ClientJoin (node ); break; @@ -1566,7 +1537,6 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_TEXTCMD : PT_TextCmd (node, netconsole); break; case PT_TEXTCMD2 : PT_TextCmd (node, netconsole); break; case PT_LOGIN : PT_Login (node, netconsole); break; - case PT_NODETIMEOUT : PT_ClientQuit (node, netconsole); break; case PT_CLIENTQUIT : PT_ClientQuit (node, netconsole); break; case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node ); break; case PT_ASKLUAFILE : PT_AskLuaFile (node ); break; diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h index 9cb3ef3e8..0abd638ce 100644 --- a/src/netcode/d_clisrv.h +++ b/src/netcode/d_clisrv.h @@ -94,6 +94,7 @@ void CL_RemoveSplitscreenPlayer(void); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_RemovePlayer(INT32 playernum, kickreason_t reason); +void CL_HandleTimeout(void); // Is there a game running boolean Playing(void); diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c index 972342282..3c16392d0 100644 --- a/src/netcode/d_net.c +++ b/src/netcode/d_net.c @@ -27,6 +27,7 @@ #include "d_netfil.h" #include "d_clisrv.h" #include "tic_command.h" +#include "net_command.h" #include "../z_zone.h" #include "i_tcp.h" #include "../d_main.h" // srb2home @@ -461,14 +462,10 @@ void Net_ConnectionTimeout(INT32 node) return; nodes[node].flags |= NF_TIMEOUT; - // Send a very special packet to self (hack the reboundstore queue) - // Main code will handle it - reboundstore[rebound_head].packettype = PT_NODETIMEOUT; - reboundstore[rebound_head].ack = 0; - reboundstore[rebound_head].ackreturn = 0; - reboundstore[rebound_head].u.textcmd[0] = (UINT8)node; - reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1); - rebound_head = (rebound_head+1) % MAXREBOUND; + if (server) + SendKicksForNode(node, KICK_MSG_TIMEOUT | KICK_MSG_KEEP_BODY); + else + CL_HandleTimeout(); // Do not redo it quickly (if we do not close connection it is // for a good reason!) @@ -782,7 +779,6 @@ static const char *packettypename[NUMPACKETTYPE] = "TEXTCMD", "TEXTCMD2", "CLIENTJOIN", - "NODETIMEOUT", "LOGIN", "TELLFILESNEEDED", "MOREFILESNEEDED", @@ -1065,10 +1061,7 @@ boolean HGetPacket(void) { M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]); doomcom->datalength = reboundsize[rebound_tail]; - if (netbuffer->packettype == PT_NODETIMEOUT) - doomcom->remotenode = netbuffer->u.textcmd[0]; - else - doomcom->remotenode = 0; + doomcom->remotenode = 0; rebound_tail = (rebound_tail+1) % MAXREBOUND; #ifdef DEBUGFILE diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 95a49a959..13477d42d 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -315,3 +315,13 @@ void SendKick(UINT8 playernum, UINT8 msg) buf[1] = msg; SendNetXCmd(XD_KICK, &buf, 2); } + +void SendKicksForNode(SINT8 node, UINT8 msg) +{ + if (!netnodes[node].ingame) + return; + + for (INT32 playernum = netnodes[node].player; playernum != -1; playernum = netnodes[node].player2) + if (playernum != -1 && playeringame[playernum]) + SendKick(playernum, msg); +} diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h index 1fc5035c8..f1b4b2f3f 100644 --- a/src/netcode/net_command.h +++ b/src/netcode/net_command.h @@ -58,5 +58,6 @@ size_t TotalTextCmdPerTic(tic_t tic); void PT_TextCmd(SINT8 node, INT32 netconsole); void SendKick(UINT8 playernum, UINT8 msg); +void SendKicksForNode(SINT8 node, UINT8 msg); #endif diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h index cbeb37f36..374cc5bff 100644 --- a/src/netcode/protocol.h +++ b/src/netcode/protocol.h @@ -85,7 +85,6 @@ typedef enum PT_TEXTCMD, // Extra text commands from the client. PT_TEXTCMD2, // Splitscreen text commands. PT_CLIENTJOIN, // Client wants to join; used in start game. - PT_NODETIMEOUT, // Packet sent to self if the connection times out. PT_LOGIN, // Login attempt from the client. From ab23b445b027ef35af52eb513defc37d58339e22 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Thu, 12 Jan 2023 19:40:31 +0100 Subject: [PATCH 49/89] Move netgame commands to a new file --- src/netcode/Sourcefile | 1 + src/netcode/commands.c | 489 +++++++++++++++++++++++++++++++++++++++++ src/netcode/commands.h | 33 +++ src/netcode/d_clisrv.c | 460 +------------------------------------- src/netcode/d_net.h | 1 - 5 files changed, 524 insertions(+), 460 deletions(-) create mode 100644 src/netcode/commands.c create mode 100644 src/netcode/commands.h diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile index c59050367..7c0354714 100644 --- a/src/netcode/Sourcefile +++ b/src/netcode/Sourcefile @@ -4,6 +4,7 @@ client_connection.c tic_command.c net_command.c gamestate.c +commands.c d_net.c d_netcmd.c d_netfil.c diff --git a/src/netcode/commands.c b/src/netcode/commands.c new file mode 100644 index 000000000..834e1c666 --- /dev/null +++ b/src/netcode/commands.c @@ -0,0 +1,489 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 commands.c +/// \brief Various netgame commands, such as kick and ban + +#include "commands.h" +#include "d_clisrv.h" +#include "client_connection.h" +#include "net_command.h" +#include "d_netcmd.h" +#include "d_net.h" +#include "i_net.h" +#include "protocol.h" +#include "../byteptr.h" +#include "../d_main.h" +#include "../g_game.h" +#include "../w_wad.h" +#include "../z_zone.h" +#include "../doomstat.h" +#include "../doomdef.h" +#include "../r_local.h" +#include +#include +#include +#include + +typedef struct banreason_s +{ + char *reason; + struct banreason_s *prev; //-1 + struct banreason_s *next; //+1 +} banreason_t; + +static banreason_t *reasontail = NULL; //last entry, use prev +static banreason_t *reasonhead = NULL; //1st entry, use next + +void Ban_Add(const char *reason) +{ + banreason_t *reasonlist = malloc(sizeof(*reasonlist)); + + if (!reasonlist) + return; + if (!reason) + reason = "NA"; + + reasonlist->next = NULL; + reasonlist->reason = Z_StrDup(reason); + if ((reasonlist->prev = reasontail) == NULL) + reasonhead = reasonlist; + else + reasontail->next = reasonlist; + reasontail = reasonlist; +} + +static void Ban_Clear(void) +{ + banreason_t *temp; + + I_ClearBans(); + + reasontail = NULL; + + while (reasonhead) + { + temp = reasonhead->next; + Z_Free(reasonhead->reason); + free(reasonhead); + reasonhead = temp; + } +} + +void Ban_Load_File(boolean warning) +{ + FILE *f; + size_t i; + const char *address, *mask; + char buffer[MAX_WADPATH]; + + if (!I_ClearBans) + return; + + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); + + if (!f) + { + if (warning) + CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n")); + return; + } + + Ban_Clear(); + + for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) + { + address = strtok(buffer, " \t\r\n"); + mask = strtok(NULL, " \t\r\n"); + + I_SetBanAddress(address, mask); + + Ban_Add(strtok(NULL, "\r\n")); + } + + fclose(f); +} + +void D_SaveBan(void) +{ + FILE *f; + size_t i; + banreason_t *reasonlist = reasonhead; + const char *address, *mask; + const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); + + if (!reasonhead) + { + remove(path); + return; + } + + f = fopen(path, "w"); + + if (!f) + { + CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n")); + return; + } + + for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + { + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) + fprintf(f, "%s 0", address); + else + fprintf(f, "%s %s", address, mask); + + if (reasonlist && reasonlist->reason) + fprintf(f, " %s\n", reasonlist->reason); + else + fprintf(f, " %s\n", "NA"); + + if (reasonlist) reasonlist = reasonlist->next; + } + + fclose(f); +} + +void Command_ShowBan(void) //Print out ban list +{ + size_t i; + const char *address, *mask; + banreason_t *reasonlist = reasonhead; + + if (I_GetBanAddress) + CONS_Printf(M_GetText("Ban List:\n")); + else + return; + + for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + { + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) + CONS_Printf("%s: %s ", sizeu1(i+1), address); + else + CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); + + if (reasonlist && reasonlist->reason) + CONS_Printf("(%s)\n", reasonlist->reason); + else + CONS_Printf("\n"); + + if (reasonlist) reasonlist = reasonlist->next; + } + + if (i == 0 && !address) + CONS_Printf(M_GetText("(empty)\n")); +} + +void Command_ClearBans(void) +{ + if (!I_ClearBans) + return; + + Ban_Clear(); + D_SaveBan(); +} + +void Command_Ban(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("Ban : ban and kick a player\n")); + return; + } + + if (!netgame) // Don't kick Tails in splitscreen! + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + + if (server || IsPlayerAdmin(consoleplayer)) + { + UINT8 buf[3 + MAX_REASONLENGTH]; + UINT8 *p = buf; + const SINT8 pn = nametonum(COM_Argv(1)); + const INT32 node = playernode[(INT32)pn]; + + if (pn == -1 || pn == 0) + return; + + WRITEUINT8(p, pn); + + if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now + { + CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); + WRITEUINT8(p, KICK_MSG_GO_AWAY); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + if (server) // only the server is allowed to do this right now + { + Ban_Add(COM_Argv(2)); + D_SaveBan(); // save the ban list + } + + if (COM_Argc() == 2) + { + WRITEUINT8(p, KICK_MSG_BANNED); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) + { + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); + } + + WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); + } + } + } + else + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + +} + +void Command_BanIP(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("banip : ban an ip address\n")); + return; + } + + if (server) // Only the server can use this, otherwise does nothing. + { + const char *address = (COM_Argv(1)); + const char *reason; + + if (COM_Argc() == 2) + reason = NULL; + else + reason = COM_Argv(2); + + + if (I_SetBanAddress && I_SetBanAddress(address, NULL)) + { + if (reason) + CONS_Printf("Banned IP address %s for: %s\n", address, reason); + else + CONS_Printf("Banned IP address %s\n", address); + + Ban_Add(reason); + D_SaveBan(); + } + else + { + return; + } + } +} + +void Command_ReloadBan(void) //recheck ban.txt +{ + Ban_Load_File(true); +} + +void Command_Kick(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("kick : kick a player\n")); + return; + } + + if (!netgame) // Don't kick Tails in splitscreen! + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + + if (server || IsPlayerAdmin(consoleplayer)) + { + UINT8 buf[3 + MAX_REASONLENGTH]; + UINT8 *p = buf; + const SINT8 pn = nametonum(COM_Argv(1)); + + if (pn == -1 || pn == 0) + return; + + // Special case if we are trying to kick a player who is downloading the game state: + // trigger a timeout instead of kicking them, because a kick would only + // take effect after they have finished downloading + if (server && playernode[pn] != UINT8_MAX && netnodes[playernode[pn]].sendingsavegame) + { + Net_ConnectionTimeout(playernode[pn]); + return; + } + + WRITESINT8(p, pn); + + if (COM_Argc() == 2) + { + WRITEUINT8(p, KICK_MSG_GO_AWAY); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) + { + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); + } + + WRITEUINT8(p, KICK_MSG_CUSTOM_KICK); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); + } + } + else + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); +} + +void Command_connect(void) +{ + if (COM_Argc() < 2 || *COM_Argv(1) == 0) + { + CONS_Printf(M_GetText( + "Connect (port): connect to a server\n" + "Connect ANY: connect to the first lan server found\n" + //"Connect SELF: connect to your own server.\n" + )); + return; + } + + if (Playing() || titledemo) + { + CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); + return; + } + + server = false; +/* + if (!stricmp(COM_Argv(1), "self")) + { + servernode = 0; + server = true; + /// \bug should be but... + //SV_SpawnServer(); + } + else +*/ + { + // used in menu to connect to a server in the list + if (netgame && !stricmp(COM_Argv(1), "node")) + { + servernode = (SINT8)atoi(COM_Argv(2)); + } + else if (netgame) + { + CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); + return; + } + else if (I_NetOpenSocket) + { + I_NetOpenSocket(); + netgame = true; + multiplayer = true; + + if (!stricmp(COM_Argv(1), "any")) + servernode = BROADCASTADDR; + else if (I_NetMakeNodewPort) + { + if (COM_Argc() >= 3) // address AND port + servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2)); + else // address only, or address:port + servernode = I_NetMakeNode(COM_Argv(1)); + } + else + { + CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); + D_CloseConnection(); + return; + } + } + else + CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n")); + } + + splitscreen = false; + SplitScreen_OnChange(); + botingame = false; + botskin = 0; + CL_ConnectToServer(); +} + +void Command_GetPlayerNum(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + if (serverplayer == i) + CONS_Printf(M_GetText("num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); + else + CONS_Printf(M_GetText("\x82num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); + } +} + +/** Lists all players and their player numbers. + * + * \sa Command_GetPlayerNum + */ +void Command_Nodes(void) +{ + INT32 i; + size_t maxlen = 0; + const char *address; + + for (i = 0; i < MAXPLAYERS; i++) + { + const size_t plen = strlen(player_names[i]); + if (playeringame[i] && plen > maxlen) + maxlen = plen; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]); + + if (playernode[i] != UINT8_MAX) + { + CONS_Printf(" - node %.2d", playernode[i]); + if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL) + CONS_Printf(" - %s", address); + } + + if (IsPlayerAdmin(i)) + CONS_Printf(M_GetText(" (verified admin)")); + + if (players[i].spectator) + CONS_Printf(M_GetText(" (spectator)")); + + CONS_Printf("\n"); + } + } +} diff --git a/src/netcode/commands.h b/src/netcode/commands.h new file mode 100644 index 000000000..5ff4d1cae --- /dev/null +++ b/src/netcode/commands.h @@ -0,0 +1,33 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 commands.h +/// \brief Various netgame commands, such as kick and ban + +#ifndef __COMMANDS__ +#define __COMMANDS__ + +#include "../doomdef.h" + +#define MAX_REASONLENGTH 30 + +void Ban_Add(const char *reason); +void D_SaveBan(void); +void Ban_Load_File(boolean warning); +void Command_ShowBan(void); +void Command_ClearBans(void); +void Command_Ban(void); +void Command_BanIP(void); +void Command_ReloadBan(void); +void Command_Kick(void); +void Command_connect(void); +void Command_GetPlayerNum(void); +void Command_Nodes(void); + +#endif diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 81d7e5543..506de8c02 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -54,6 +54,7 @@ #include "tic_command.h" #include "net_command.h" #include "gamestate.h" +#include "commands.h" #include "protocol.h" // @@ -69,8 +70,6 @@ // firstticstosend is used to optimize a condition // Normally maketic >= gametic > 0 -#define MAX_REASONLENGTH 30 - boolean server = true; // true or false but !server == client boolean serverrunning = false; INT32 serverplayer = 0; @@ -103,16 +102,6 @@ boolean acceptnewnode = true; UINT16 software_MAXPACKETLENGTH; -typedef struct banreason_s -{ - char *reason; - struct banreason_s *prev; //-1 - struct banreason_s *next; //+1 -} banreason_t; - -static banreason_t *reasontail = NULL; //last entry, use prev -static banreason_t *reasonhead = NULL; //1st entry, use next - static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); @@ -124,232 +113,6 @@ consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_ static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); -static void Command_ShowBan(void) //Print out ban list -{ - size_t i; - const char *address, *mask; - banreason_t *reasonlist = reasonhead; - - if (I_GetBanAddress) - CONS_Printf(M_GetText("Ban List:\n")); - else - return; - - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) - { - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - CONS_Printf("%s: %s ", sizeu1(i+1), address); - else - CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); - - if (reasonlist && reasonlist->reason) - CONS_Printf("(%s)\n", reasonlist->reason); - else - CONS_Printf("\n"); - - if (reasonlist) reasonlist = reasonlist->next; - } - - if (i == 0 && !address) - CONS_Printf(M_GetText("(empty)\n")); -} - -void D_SaveBan(void) -{ - FILE *f; - size_t i; - banreason_t *reasonlist = reasonhead; - const char *address, *mask; - const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); - - if (!reasonhead) - { - remove(path); - return; - } - - f = fopen(path, "w"); - - if (!f) - { - CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n")); - return; - } - - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) - { - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - fprintf(f, "%s 0", address); - else - fprintf(f, "%s %s", address, mask); - - if (reasonlist && reasonlist->reason) - fprintf(f, " %s\n", reasonlist->reason); - else - fprintf(f, " %s\n", "NA"); - - if (reasonlist) reasonlist = reasonlist->next; - } - - fclose(f); -} - -static void Ban_Add(const char *reason) -{ - banreason_t *reasonlist = malloc(sizeof(*reasonlist)); - - if (!reasonlist) - return; - if (!reason) - reason = "NA"; - - reasonlist->next = NULL; - reasonlist->reason = Z_StrDup(reason); - if ((reasonlist->prev = reasontail) == NULL) - reasonhead = reasonlist; - else - reasontail->next = reasonlist; - reasontail = reasonlist; -} - -static void Ban_Clear(void) -{ - banreason_t *temp; - - I_ClearBans(); - - reasontail = NULL; - - while (reasonhead) - { - temp = reasonhead->next; - Z_Free(reasonhead->reason); - free(reasonhead); - reasonhead = temp; - } -} - -static void Command_ClearBans(void) -{ - if (!I_ClearBans) - return; - - Ban_Clear(); - D_SaveBan(); -} - -static void Ban_Load_File(boolean warning) -{ - FILE *f; - size_t i; - const char *address, *mask; - char buffer[MAX_WADPATH]; - - if (!I_ClearBans) - return; - - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); - - if (!f) - { - if (warning) - CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n")); - return; - } - - Ban_Clear(); - - for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) - { - address = strtok(buffer, " \t\r\n"); - mask = strtok(NULL, " \t\r\n"); - - I_SetBanAddress(address, mask); - - Ban_Add(strtok(NULL, "\r\n")); - } - - fclose(f); -} - -static void Command_ReloadBan(void) //recheck ban.txt -{ - Ban_Load_File(true); -} - -static void Command_connect(void) -{ - if (COM_Argc() < 2 || *COM_Argv(1) == 0) - { - CONS_Printf(M_GetText( - "Connect (port): connect to a server\n" - "Connect ANY: connect to the first lan server found\n" - //"Connect SELF: connect to your own server.\n" - )); - return; - } - - if (Playing() || titledemo) - { - CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); - return; - } - - server = false; -/* - if (!stricmp(COM_Argv(1), "self")) - { - servernode = 0; - server = true; - /// \bug should be but... - //SV_SpawnServer(); - } - else -*/ - { - // used in menu to connect to a server in the list - if (netgame && !stricmp(COM_Argv(1), "node")) - { - servernode = (SINT8)atoi(COM_Argv(2)); - } - else if (netgame) - { - CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); - return; - } - else if (I_NetOpenSocket) - { - I_NetOpenSocket(); - netgame = true; - multiplayer = true; - - if (!stricmp(COM_Argv(1), "any")) - servernode = BROADCASTADDR; - else if (I_NetMakeNodewPort) - { - if (COM_Argc() >= 3) // address AND port - servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2)); - else // address only, or address:port - servernode = I_NetMakeNode(COM_Argv(1)); - } - else - { - CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); - D_CloseConnection(); - return; - } - } - else - CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n")); - } - - splitscreen = false; - SplitScreen_OnChange(); - botingame = false; - botskin = 0; - CL_ConnectToServer(); -} - void ResetNode(INT32 node) { memset(&netnodes[node], 0, sizeof(*netnodes)); @@ -527,20 +290,6 @@ void CL_Reset(void) // D_StartTitle should get done now, but the calling function will handle it } -static void Command_GetPlayerNum(void) -{ - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - { - if (serverplayer == i) - CONS_Printf(M_GetText("num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); - else - CONS_Printf(M_GetText("\x82num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); - } -} - SINT8 nametonum(const char *name) { INT32 playernum, i; @@ -570,213 +319,6 @@ SINT8 nametonum(const char *name) return -1; } -/** Lists all players and their player numbers. - * - * \sa Command_GetPlayerNum - */ -static void Command_Nodes(void) -{ - INT32 i; - size_t maxlen = 0; - const char *address; - - for (i = 0; i < MAXPLAYERS; i++) - { - const size_t plen = strlen(player_names[i]); - if (playeringame[i] && plen > maxlen) - maxlen = plen; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]); - - if (playernode[i] != UINT8_MAX) - { - CONS_Printf(" - node %.2d", playernode[i]); - if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL) - CONS_Printf(" - %s", address); - } - - if (IsPlayerAdmin(i)) - CONS_Printf(M_GetText(" (verified admin)")); - - if (players[i].spectator) - CONS_Printf(M_GetText(" (spectator)")); - - CONS_Printf("\n"); - } - } -} - -static void Command_Ban(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("Ban : ban and kick a player\n")); - return; - } - - if (!netgame) // Don't kick Tails in splitscreen! - { - CONS_Printf(M_GetText("This only works in a netgame.\n")); - return; - } - - if (server || IsPlayerAdmin(consoleplayer)) - { - UINT8 buf[3 + MAX_REASONLENGTH]; - UINT8 *p = buf; - const SINT8 pn = nametonum(COM_Argv(1)); - const INT32 node = playernode[(INT32)pn]; - - if (pn == -1 || pn == 0) - return; - - WRITEUINT8(p, pn); - - if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now - { - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); - WRITEUINT8(p, KICK_MSG_GO_AWAY); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - if (server) // only the server is allowed to do this right now - { - Ban_Add(COM_Argv(2)); - D_SaveBan(); // save the ban list - } - - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_BANNED); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t i, j = COM_Argc(); - char message[MAX_REASONLENGTH]; - - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) - { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); - } - - WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } - } - } - else - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - -} - -static void Command_BanIP(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("banip : ban an ip address\n")); - return; - } - - if (server) // Only the server can use this, otherwise does nothing. - { - const char *address = (COM_Argv(1)); - const char *reason; - - if (COM_Argc() == 2) - reason = NULL; - else - reason = COM_Argv(2); - - - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) - { - if (reason) - CONS_Printf("Banned IP address %s for: %s\n", address, reason); - else - CONS_Printf("Banned IP address %s\n", address); - - Ban_Add(reason); - D_SaveBan(); - } - else - { - return; - } - } -} - -static void Command_Kick(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("kick : kick a player\n")); - return; - } - - if (!netgame) // Don't kick Tails in splitscreen! - { - CONS_Printf(M_GetText("This only works in a netgame.\n")); - return; - } - - if (server || IsPlayerAdmin(consoleplayer)) - { - UINT8 buf[3 + MAX_REASONLENGTH]; - UINT8 *p = buf; - const SINT8 pn = nametonum(COM_Argv(1)); - - if (pn == -1 || pn == 0) - return; - - // Special case if we are trying to kick a player who is downloading the game state: - // trigger a timeout instead of kicking them, because a kick would only - // take effect after they have finished downloading - if (server && playernode[pn] != UINT8_MAX && netnodes[playernode[pn]].sendingsavegame) - { - Net_ConnectionTimeout(playernode[pn]); - return; - } - - WRITESINT8(p, pn); - - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_GO_AWAY); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t i, j = COM_Argc(); - char message[MAX_REASONLENGTH]; - - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) - { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); - } - - WRITEUINT8(p, KICK_MSG_CUSTOM_KICK); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } - } - else - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); -} - static void Got_KickCmd(UINT8 **p, INT32 playernum) { INT32 pnum, msg; diff --git a/src/netcode/d_net.h b/src/netcode/d_net.h index 857c3463c..039f5b3b4 100644 --- a/src/netcode/d_net.h +++ b/src/netcode/d_net.h @@ -68,7 +68,6 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength); boolean HGetPacket(void); void D_SetDoomcom(void); -void D_SaveBan(void); boolean D_CheckNetGame(void); void D_CloseConnection(void); void Net_UnAcknowledgePacket(INT32 node); From 207053a0237b5e016afd9cba5baf7af4146626c5 Mon Sep 17 00:00:00 2001 From: spherallic Date: Fri, 13 Jan 2023 17:10:50 +0100 Subject: [PATCH 50/89] Prevent A_LobShot from dividing by 0 --- src/p_enemy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_enemy.c b/src/p_enemy.c index 373f09123..3305709fc 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -2671,7 +2671,7 @@ void A_LobShot(mobj_t *actor) fixed_t z; fixed_t dist; fixed_t vertical, horizontal; - fixed_t airtime = var2 & 65535; + fixed_t airtime = max(1, var2 & 65535); if (LUA_CallAction(A_LOBSHOT, actor)) return; From 2a360a4201beb717ed82a97f746c9bfec8689f79 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 13 Jan 2023 22:05:57 +0100 Subject: [PATCH 51/89] Reorder functions in d_clisrv.c --- src/netcode/d_clisrv.c | 1249 ++++++++++++++++++++-------------------- 1 file changed, 623 insertions(+), 626 deletions(-) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 506de8c02..1658856b6 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -83,6 +83,7 @@ UINT8 playernode[MAXPLAYERS]; UINT16 pingmeasurecount = 1; UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. +static INT32 pingtimeout[MAXPLAYERS]; tic_t servermaxping = 800; // server's max ping. Defaults to 800 tic_t maketic; @@ -120,137 +121,6 @@ void ResetNode(INT32 node) netnodes[node].player2 = -1; } -// -// CL_ClearPlayer -// -// Clears the player data so that a future client can use this slot -// -void CL_ClearPlayer(INT32 playernum) -{ - if (players[playernum].mo) - P_RemoveMobj(players[playernum].mo); - memset(&players[playernum], 0, sizeof (player_t)); - memset(playeraddress[playernum], 0, sizeof(*playeraddress)); -} - -static void UnlinkPlayerFromNode(INT32 playernum) -{ - INT32 node = playernode[playernum]; - - if (node == UINT8_MAX) - return; - - playernode[playernum] = UINT8_MAX; - - netnodes[node].numplayers--; - if (netnodes[node].numplayers <= 0) - { - netnodes[node].ingame = false; - Net_CloseConnection(node); - ResetNode(node); - } -} - -// If in a special stage, redistribute the player's -// spheres across the remaining players. -// I feel like this shouldn't even be in this file at all, but well. -static void RedistributeSpecialStageSpheres(INT32 playernum) -{ - if (!G_IsSpecialStage(gamemap) || D_NumPlayers() <= 1) - return; - - INT32 count = D_NumPlayers() - 1; - INT32 spheres = players[playernum].spheres; - INT32 rings = players[playernum].rings; - - while (spheres || rings) - { - INT32 sincrement = max(spheres / count, 1); - INT32 rincrement = max(rings / count, 1); - - INT32 i, n; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || i == playernum) - continue; - - n = min(spheres, sincrement); - P_GivePlayerSpheres(&players[i], n); - spheres -= n; - - n = min(rings, rincrement); - P_GivePlayerRings(&players[i], n); - rings -= n; - } - } -} - -// -// CL_RemovePlayer -// -// Removes a player from the current game -// -void CL_RemovePlayer(INT32 playernum, kickreason_t reason) -{ - // Sanity check: exceptional cases (i.e. c-fails) can cause multiple - // kick commands to be issued for the same player. - if (!playeringame[playernum]) - return; - - if (server) - UnlinkPlayerFromNode(playernum); - - if (gametyperules & GTR_TEAMFLAGS) - P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! - - RedistributeSpecialStageSpheres(playernum); - - LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting - - // don't look through someone's view who isn't there - if (playernum == displayplayer) - { - // Call ViewpointSwitch hooks here. - // The viewpoint was forcibly changed. - LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); - displayplayer = consoleplayer; - } - - // Reset player data - CL_ClearPlayer(playernum); - - // remove avatar of player - playeringame[playernum] = false; - while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) - doomcom->numslots--; - - // 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 - } - - LUA_InvalidatePlayer(&players[playernum]); - - if (G_TagGametype()) //Check if you still have a game. Location flexible. =P - P_CheckSurvivors(); - else if (gametyperules & GTR_RACE) - P_CheckRacers(); -} - -void CL_HandleTimeout(void) -{ - LUA_HookBool(false, HOOK(GameQuit)); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); -} - void CL_Reset(void) { if (metalrecording) @@ -290,33 +160,181 @@ void CL_Reset(void) // D_StartTitle should get done now, but the calling function will handle it } -SINT8 nametonum(const char *name) +// +// CL_ClearPlayer +// +// Clears the player data so that a future client can use this slot +// +void CL_ClearPlayer(INT32 playernum) { - INT32 playernum, i; + if (players[playernum].mo) + P_RemoveMobj(players[playernum].mo); + memset(&players[playernum], 0, sizeof (player_t)); + memset(playeraddress[playernum], 0, sizeof(*playeraddress)); +} - if (!strcmp(name, "0")) - return 0; +// Xcmd XD_ADDPLAYER +static void Got_AddPlayer(UINT8 **p, INT32 playernum) +{ + INT16 node, newplayernum; + boolean splitscreenplayer; + boolean rejoined; + player_t *newplayer; - playernum = (SINT8)atoi(name); - - if (playernum < 0 || playernum >= MAXPLAYERS) - return -1; - - if (playernum) + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { - if (playeringame[playernum]) - return (SINT8)playernum; - else - return -1; + // protect against hacked/buggy client + CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; } - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && !stricmp(player_names[i], name)) - return (SINT8)i; + node = READUINT8(*p); + newplayernum = READUINT8(*p); + splitscreenplayer = newplayernum & 0x80; + newplayernum &= ~0x80; - CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name); + rejoined = playeringame[newplayernum]; - return -1; + if (!rejoined) + { + // Clear player before joining, lest some things get set incorrectly + // HACK: don't do this for splitscreen, it relies on preset values + if (!splitscreen && !botingame) + CL_ClearPlayer(newplayernum); + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + if (newplayernum+1 > doomcom->numslots) + doomcom->numslots = (INT16)(newplayernum+1); + + if (server && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + char *port = NULL; + if (address) // MI: fix msvcrt.dll!_mbscat crash? + { + strcpy(playeraddress[newplayernum], address); + port = strchr(playeraddress[newplayernum], ':'); + if (port) + *port = '\0'; + } + } + } + + newplayer = &players[newplayernum]; + + newplayer->jointime = 0; + newplayer->quittime = 0; + + READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); + + // the server is creating my player + if (node == mynode) + { + playernode[newplayernum] = 0; // for information only + if (!splitscreenplayer) + { + consoleplayer = newplayernum; + displayplayer = newplayernum; + secondarydisplayplayer = newplayernum; + DEBFILE("spawning me\n"); + ticcmd_oldangleturn[0] = newplayer->oldrelangleturn; + } + else + { + secondarydisplayplayer = newplayernum; + DEBFILE("spawning my brother\n"); + if (botingame) + newplayer->bot = 1; + ticcmd_oldangleturn[1] = newplayer->oldrelangleturn; + } + P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16)); + D_SendPlayerConfig(); + addedtogame = true; + + if (rejoined) + { + if (newplayer->mo) + { + newplayer->viewheight = 41*newplayer->height/48; + + if (newplayer->mo->eflags & MFE_VERTICALFLIP) + newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; + else + newplayer->viewz = newplayer->mo->z + newplayer->viewheight; + } + + // wake up the status bar + ST_Start(); + // wake up the heads up text + HU_Start(); + + if (camera.chase && !splitscreenplayer) + P_ResetCamera(newplayer, &camera); + if (camera2.chase && splitscreenplayer) + P_ResetCamera(newplayer, &camera2); + } + } + + if (netgame) + { + char joinmsg[256]; + + if (rejoined) + strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); + else + strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); + strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); + + // Merge join notification + IP to avoid clogging console/chat + if (server && cv_showjoinaddress.value && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + if (address) + strcat(joinmsg, va(" (%s)", address)); + } + + HU_AddChatText(joinmsg, false); + } + + if (server && multiplayer && motd[0] != '\0') + COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); + + if (!rejoined) + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); +} + +static void UnlinkPlayerFromNode(INT32 playernum) +{ + INT32 node = playernode[playernum]; + + if (node == UINT8_MAX) + return; + + playernode[playernum] = UINT8_MAX; + + netnodes[node].numplayers--; + if (netnodes[node].numplayers <= 0) + { + netnodes[node].ingame = false; + Net_CloseConnection(node); + ResetNode(node); + } +} + +static void PT_ClientQuit(SINT8 node, INT32 netconsole) +{ + if (client) + return; + + if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole]) + SendKicksForNode(node, KICK_MSG_PLAYER_QUIT | KICK_MSG_KEEP_BODY); + + Net_CloseConnection(node); + netnodes[node].ingame = false; + netnodes[node].player = -1; + netnodes[node].player2 = -1; } static void Got_KickCmd(UINT8 **p, INT32 playernum) @@ -510,47 +528,175 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) CL_RemovePlayer(pnum, kickreason); } -static void Got_AddPlayer(UINT8 **p, INT32 playernum); - -// called one time at init -void D_ClientServerInit(void) +// If in a special stage, redistribute the player's +// spheres across the remaining players. +// I feel like this shouldn't even be in this file at all, but well. +static void RedistributeSpecialStageSpheres(INT32 playernum) { - DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n", - VERSION/100, VERSION%100, SUBVERSION)); + if (!G_IsSpecialStage(gamemap) || D_NumPlayers() <= 1) + return; - COM_AddCommand("getplayernum", Command_GetPlayerNum); - COM_AddCommand("kick", Command_Kick); - COM_AddCommand("ban", Command_Ban); - COM_AddCommand("banip", Command_BanIP); - COM_AddCommand("clearbans", Command_ClearBans); - COM_AddCommand("showbanlist", Command_ShowBan); - COM_AddCommand("reloadbans", Command_ReloadBan); - COM_AddCommand("connect", Command_connect); - COM_AddCommand("nodes", Command_Nodes); - COM_AddCommand("resendgamestate", Command_ResendGamestate); -#ifdef PACKETDROP - COM_AddCommand("drop", Command_Drop); - COM_AddCommand("droprate", Command_Droprate); + INT32 count = D_NumPlayers() - 1; + INT32 spheres = players[playernum].spheres; + INT32 rings = players[playernum].rings; + + while (spheres || rings) + { + INT32 sincrement = max(spheres / count, 1); + INT32 rincrement = max(rings / count, 1); + + INT32 i, n; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || i == playernum) + continue; + + n = min(spheres, sincrement); + P_GivePlayerSpheres(&players[i], n); + spheres -= n; + + n = min(rings, rincrement); + P_GivePlayerRings(&players[i], n); + rings -= n; + } + } +} + +// +// CL_RemovePlayer +// +// Removes a player from the current game +// +void CL_RemovePlayer(INT32 playernum, kickreason_t reason) +{ + // Sanity check: exceptional cases (i.e. c-fails) can cause multiple + // kick commands to be issued for the same player. + if (!playeringame[playernum]) + return; + + if (server) + UnlinkPlayerFromNode(playernum); + + if (gametyperules & GTR_TEAMFLAGS) + P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! + + RedistributeSpecialStageSpheres(playernum); + + LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting + + // don't look through someone's view who isn't there + if (playernum == displayplayer) + { + // Call ViewpointSwitch hooks here. + // The viewpoint was forcibly changed. + LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); + displayplayer = consoleplayer; + } + + // Reset player data + CL_ClearPlayer(playernum); + + // remove avatar of player + playeringame[playernum] = false; + while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) + doomcom->numslots--; + + // 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 + } + + LUA_InvalidatePlayer(&players[playernum]); + + if (G_TagGametype()) //Check if you still have a game. Location flexible. =P + P_CheckSurvivors(); + else if (gametyperules & GTR_RACE) + P_CheckRacers(); +} + +// +// D_QuitNetGame +// Called before quitting to leave a net game +// without hanging the other players +// +void D_QuitNetGame(void) +{ + mousegrabbedbylua = true; + I_UpdateMouseGrab(); + + if (!netgame || !netbuffer) + return; + + DEBFILE("===========================================================================\n" + " Quitting Game, closing connection\n" + "===========================================================================\n"); + + // abort send/receive of files + CloseNetFile(); + RemoveAllLuaFileTransfers(); + waitingforluafiletransfer = false; + waitingforluafilecommand = false; + + if (server) + { + INT32 i; + + netbuffer->packettype = PT_SERVERSHUTDOWN; + for (i = 0; i < MAXNETNODES; i++) + if (netnodes[i].ingame) + HSendPacket(i, true, 0, 0); +#ifdef MASTERSERVER + if (serverrunning && ms_RoomId > 0) + UnregisterServer(); #endif -#ifdef _DEBUG - COM_AddCommand("numnodes", Command_Numnodes); + } + else if (servernode > 0 && servernode < MAXNETNODES && netnodes[(UINT8)servernode].ingame) + { + netbuffer->packettype = PT_CLIENTQUIT; + HSendPacket(servernode, true, 0, 0); + } + + D_CloseConnection(); + ClearAdminPlayers(); + + DEBFILE("===========================================================================\n" + " Log finish\n" + "===========================================================================\n"); +#ifdef DEBUGFILE + if (debugfile) + { + fclose(debugfile); + debugfile = NULL; + } #endif +} - RegisterNetXCmd(XD_KICK, Got_KickCmd); - RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); -#ifdef DUMPCONSISTENCY - CV_RegisterVar(&cv_dumpconsistency); -#endif - Ban_Load_File(false); +void CL_HandleTimeout(void) +{ + LUA_HookBool(false, HOOK(GameQuit)); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); +} - gametic = 0; - localgametic = 0; +void CL_AddSplitscreenPlayer(void) +{ + if (cl_mode == CL_CONNECTED) + CL_SendJoin(); +} - // do not send anything before the real begin - SV_StopServer(); - SV_ResetServer(); - if (dedicated) - SV_SpawnServer(); +void CL_RemoveSplitscreenPlayer(void) +{ + if (cl_mode != CL_CONNECTED) + return; + + SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT); } void SV_ResetServer(void) @@ -622,215 +768,6 @@ static inline void SV_GenContext(void) } } -// -// D_QuitNetGame -// Called before quitting to leave a net game -// without hanging the other players -// -void D_QuitNetGame(void) -{ - mousegrabbedbylua = true; - I_UpdateMouseGrab(); - - if (!netgame || !netbuffer) - return; - - DEBFILE("===========================================================================\n" - " Quitting Game, closing connection\n" - "===========================================================================\n"); - - // abort send/receive of files - CloseNetFile(); - RemoveAllLuaFileTransfers(); - waitingforluafiletransfer = false; - waitingforluafilecommand = false; - - if (server) - { - INT32 i; - - netbuffer->packettype = PT_SERVERSHUTDOWN; - for (i = 0; i < MAXNETNODES; i++) - if (netnodes[i].ingame) - HSendPacket(i, true, 0, 0); -#ifdef MASTERSERVER - if (serverrunning && ms_RoomId > 0) - UnregisterServer(); -#endif - } - else if (servernode > 0 && servernode < MAXNETNODES && netnodes[(UINT8)servernode].ingame) - { - netbuffer->packettype = PT_CLIENTQUIT; - HSendPacket(servernode, true, 0, 0); - } - - D_CloseConnection(); - ClearAdminPlayers(); - - DEBFILE("===========================================================================\n" - " Log finish\n" - "===========================================================================\n"); -#ifdef DEBUGFILE - if (debugfile) - { - fclose(debugfile); - debugfile = NULL; - } -#endif -} - -// Xcmd XD_ADDPLAYER -static void Got_AddPlayer(UINT8 **p, INT32 playernum) -{ - INT16 node, newplayernum; - boolean splitscreenplayer; - boolean rejoined; - player_t *newplayer; - - if (playernum != serverplayer && !IsPlayerAdmin(playernum)) - { - // protect against hacked/buggy client - CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - return; - } - - node = READUINT8(*p); - newplayernum = READUINT8(*p); - splitscreenplayer = newplayernum & 0x80; - newplayernum &= ~0x80; - - rejoined = playeringame[newplayernum]; - - if (!rejoined) - { - // Clear player before joining, lest some things get set incorrectly - // HACK: don't do this for splitscreen, it relies on preset values - if (!splitscreen && !botingame) - CL_ClearPlayer(newplayernum); - playeringame[newplayernum] = true; - G_AddPlayer(newplayernum); - if (newplayernum+1 > doomcom->numslots) - doomcom->numslots = (INT16)(newplayernum+1); - - if (server && I_GetNodeAddress) - { - const char *address = I_GetNodeAddress(node); - char *port = NULL; - if (address) // MI: fix msvcrt.dll!_mbscat crash? - { - strcpy(playeraddress[newplayernum], address); - port = strchr(playeraddress[newplayernum], ':'); - if (port) - *port = '\0'; - } - } - } - - newplayer = &players[newplayernum]; - - newplayer->jointime = 0; - newplayer->quittime = 0; - - READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); - - // the server is creating my player - if (node == mynode) - { - playernode[newplayernum] = 0; // for information only - if (!splitscreenplayer) - { - consoleplayer = newplayernum; - displayplayer = newplayernum; - secondarydisplayplayer = newplayernum; - DEBFILE("spawning me\n"); - ticcmd_oldangleturn[0] = newplayer->oldrelangleturn; - } - else - { - secondarydisplayplayer = newplayernum; - DEBFILE("spawning my brother\n"); - if (botingame) - newplayer->bot = 1; - ticcmd_oldangleturn[1] = newplayer->oldrelangleturn; - } - P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16)); - D_SendPlayerConfig(); - addedtogame = true; - - if (rejoined) - { - if (newplayer->mo) - { - newplayer->viewheight = 41*newplayer->height/48; - - if (newplayer->mo->eflags & MFE_VERTICALFLIP) - newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; - else - newplayer->viewz = newplayer->mo->z + newplayer->viewheight; - } - - // wake up the status bar - ST_Start(); - // wake up the heads up text - HU_Start(); - - if (camera.chase && !splitscreenplayer) - P_ResetCamera(newplayer, &camera); - if (camera2.chase && splitscreenplayer) - P_ResetCamera(newplayer, &camera2); - } - } - - if (netgame) - { - char joinmsg[256]; - - if (rejoined) - strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); - else - strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); - strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); - - // Merge join notification + IP to avoid clogging console/chat - if (server && cv_showjoinaddress.value && I_GetNodeAddress) - { - const char *address = I_GetNodeAddress(node); - if (address) - strcat(joinmsg, va(" (%s)", address)); - } - - HU_AddChatText(joinmsg, false); - } - - if (server && multiplayer && motd[0] != '\0') - COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); - - if (!rejoined) - LUA_HookInt(newplayernum, HOOK(PlayerJoin)); -} - -void CL_AddSplitscreenPlayer(void) -{ - if (cl_mode == CL_CONNECTED) - CL_SendJoin(); -} - -void CL_RemoveSplitscreenPlayer(void) -{ - if (cl_mode != CL_CONNECTED) - return; - - SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT); -} - -// is there a game running -boolean Playing(void) -{ - return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); -} - void SV_SpawnServer(void) { if (demoplayback) @@ -860,6 +797,21 @@ void SV_SpawnServer(void) } } +// called at singleplayer start and stopdemo +void SV_StartSinglePlayerServer(void) +{ + server = true; + netgame = false; + multiplayer = false; + G_SetGametype(GT_COOP); + + // no more tic the game with this settings! + SV_StopServer(); + + if (splitscreen) + multiplayer = true; +} + void SV_StopServer(void) { tic_t i; @@ -881,21 +833,6 @@ void SV_StopServer(void) serverrunning = false; } -// called at singleplayer start and stopdemo -void SV_StartSinglePlayerServer(void) -{ - server = true; - netgame = false; - multiplayer = false; - G_SetGametype(GT_COOP); - - // no more tic the game with this settings! - SV_StopServer(); - - if (splitscreen) - multiplayer = true; -} - /** Called when a PT_SERVERSHUTDOWN packet is received * * \param node The packet sender (should be the server) @@ -948,20 +885,6 @@ static void PT_Login(SINT8 node, INT32 netconsole) #endif } -static void PT_ClientQuit(SINT8 node, INT32 netconsole) -{ - if (client) - return; - - if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole]) - SendKicksForNode(node, KICK_MSG_PLAYER_QUIT | KICK_MSG_KEEP_BODY); - - Net_CloseConnection(node); - netnodes[node].ingame = false; - netnodes[node].player = -1; - netnodes[node].player2 = -1; -} - static void PT_AskLuaFile(SINT8 node) { if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) @@ -974,6 +897,94 @@ static void PT_HasLuaFile(SINT8 node) SV_HandleLuaFileSent(node); } +static void PT_SendingLuaFile(SINT8 node) +{ + (void)node; + + if (client) + CL_PrepareDownloadLuaFile(); +} + +/* +Ping Update except better: +We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. +If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. +*/ + +static inline void PingUpdate(void) +{ + INT32 i; + boolean laggers[MAXPLAYERS]; + UINT8 numlaggers = 0; + memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); + + netbuffer->packettype = PT_PING; + + //check for ping limit breakage. + if (cv_maxping.value) + { + for (i = 1; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].quittime + && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) + { + if (players[i].jointime > 30 * TICRATE) + laggers[i] = true; + numlaggers++; + } + else + pingtimeout[i] = 0; + } + + //kick lagging players... unless everyone but the server's ping sucks. + //in that case, it is probably the server's fault. + if (numlaggers < D_NumPlayers() - 1) + { + for (i = 1; i < MAXPLAYERS; i++) + { + if (playeringame[i] && laggers[i]) + { + pingtimeout[i]++; + // ok your net has been bad for too long, you deserve to die. + if (pingtimeout[i] > cv_pingtimeout.value) + { + pingtimeout[i] = 0; + SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); + } + } + /* + you aren't lagging, + but you aren't free yet. + In case you'll keep spiking, + we just make the timer go back down. (Very unstable net must still get kicked). + */ + else + pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); + } + } + } + + //make the ping packet and clear server data for next one + for (i = 0; i < MAXPLAYERS; i++) + { + netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; + //server takes a snapshot of the real ping for display. + //otherwise, pings fluctuate a lot and would be odd to look at. + playerpingtable[i] = realpingtable[i] / pingmeasurecount; + realpingtable[i] = 0; //Reset each as we go. + } + + // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked. + netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value; + + //send out our ping packets + for (i = 0; i < MAXNETNODES; i++) + if (netnodes[i].ingame) + HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); + + pingmeasurecount = 1; //Reset count +} + static void PT_Ping(SINT8 node, INT32 netconsole) { // Only accept PT_PING from the server. @@ -997,14 +1008,6 @@ static void PT_Ping(SINT8 node, INT32 netconsole) } } -static void PT_SendingLuaFile(SINT8 node) -{ - (void)node; - - if (client) - CL_PrepareDownloadLuaFile(); -} - /** Handles a packet received from a node that isn't in game * * \param node The packet sender @@ -1124,120 +1127,6 @@ void GetPackets(void) } } -// -// NetUpdate -// Builds ticcmds for console player, -// sends out a packet -// -// no more use random generator, because at very first tic isn't yet synchronized -// Note: It is called consistAncy on purpose. -// -INT16 Consistancy(void) -{ - INT32 i; - UINT32 ret = 0; -#ifdef MOBJCONSISTANCY - thinker_t *th; - mobj_t *mo; -#endif - - DEBFILE(va("TIC %u ", gametic)); - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - ret ^= 0xCCCC; - else if (!players[i].mo); - else - { - ret += players[i].mo->x; - ret -= players[i].mo->y; - ret += players[i].powers[pw_shield]; - ret *= i+1; - } - } - // I give up - // Coop desynching enemies is painful - if (!G_PlatformGametype()) - ret += P_GetRandSeed(); - -#ifdef MOBJCONSISTANCY - if (gamestate == GS_LEVEL) - { - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo = (mobj_t *)th; - - if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) - { - ret -= mo->type; - ret += mo->x; - ret -= mo->y; - ret += mo->z; - ret -= mo->momx; - ret += mo->momy; - ret -= mo->momz; - ret += mo->angle; - ret -= mo->flags; - ret += mo->flags2; - ret -= mo->eflags; - if (mo->target) - { - ret += mo->target->type; - ret -= mo->target->x; - ret += mo->target->y; - ret -= mo->target->z; - ret += mo->target->momx; - ret -= mo->target->momy; - ret += mo->target->momz; - ret -= mo->target->angle; - ret += mo->target->flags; - ret -= mo->target->flags2; - ret += mo->target->eflags; - ret -= mo->target->state - states; - ret += mo->target->tics; - ret -= mo->target->sprite; - ret += mo->target->frame; - } - else - ret ^= 0x3333; - if (mo->tracer && mo->tracer->type != MT_OVERLAY) - { - ret += mo->tracer->type; - ret -= mo->tracer->x; - ret += mo->tracer->y; - ret -= mo->tracer->z; - ret += mo->tracer->momx; - ret -= mo->tracer->momy; - ret += mo->tracer->momz; - ret -= mo->tracer->angle; - ret += mo->tracer->flags; - ret -= mo->tracer->flags2; - ret += mo->tracer->eflags; - ret -= mo->tracer->state - states; - ret += mo->tracer->tics; - ret -= mo->tracer->sprite; - ret += mo->tracer->frame; - } - else - ret ^= 0xAAAA; - ret -= mo->state - states; - ret += mo->tics; - ret -= mo->sprite; - ret += mo->frame; - } - } - } -#endif - - DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF))); - - return (INT16)(ret & 0xFFFF); -} - boolean TryRunTics(tic_t realtics) { // the machine has lagged but it is not so bad @@ -1331,88 +1220,6 @@ boolean TryRunTics(tic_t realtics) } } -/* -Ping Update except better: -We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. -If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. -*/ - -static INT32 pingtimeout[MAXPLAYERS]; - -static inline void PingUpdate(void) -{ - INT32 i; - boolean laggers[MAXPLAYERS]; - UINT8 numlaggers = 0; - memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); - - netbuffer->packettype = PT_PING; - - //check for ping limit breakage. - if (cv_maxping.value) - { - for (i = 1; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].quittime - && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) - { - if (players[i].jointime > 30 * TICRATE) - laggers[i] = true; - numlaggers++; - } - else - pingtimeout[i] = 0; - } - - //kick lagging players... unless everyone but the server's ping sucks. - //in that case, it is probably the server's fault. - if (numlaggers < D_NumPlayers() - 1) - { - for (i = 1; i < MAXPLAYERS; i++) - { - if (playeringame[i] && laggers[i]) - { - pingtimeout[i]++; - // ok your net has been bad for too long, you deserve to die. - if (pingtimeout[i] > cv_pingtimeout.value) - { - pingtimeout[i] = 0; - SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); - } - } - /* - you aren't lagging, - but you aren't free yet. - In case you'll keep spiking, - we just make the timer go back down. (Very unstable net must still get kicked). - */ - else - pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); - } - } - } - - //make the ping packet and clear server data for next one - for (i = 0; i < MAXPLAYERS; i++) - { - netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; - //server takes a snapshot of the real ping for display. - //otherwise, pings fluctuate a lot and would be odd to look at. - playerpingtable[i] = realpingtable[i] / pingmeasurecount; - realpingtable[i] = 0; //Reset each as we go. - } - - // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked. - netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value; - - //send out our ping packets - for (i = 0; i < MAXNETNODES; i++) - if (netnodes[i].ingame) - HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); - - pingmeasurecount = 1; //Reset count -} - void NetUpdate(void) { static tic_t gametime = 0; @@ -1537,6 +1344,82 @@ void NetUpdate(void) FileSendTicker(); } +// called one time at init +void D_ClientServerInit(void) +{ + DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n", + VERSION/100, VERSION%100, SUBVERSION)); + + COM_AddCommand("getplayernum", Command_GetPlayerNum); + COM_AddCommand("kick", Command_Kick); + COM_AddCommand("ban", Command_Ban); + COM_AddCommand("banip", Command_BanIP); + COM_AddCommand("clearbans", Command_ClearBans); + COM_AddCommand("showbanlist", Command_ShowBan); + COM_AddCommand("reloadbans", Command_ReloadBan); + COM_AddCommand("connect", Command_connect); + COM_AddCommand("nodes", Command_Nodes); + COM_AddCommand("resendgamestate", Command_ResendGamestate); +#ifdef PACKETDROP + COM_AddCommand("drop", Command_Drop); + COM_AddCommand("droprate", Command_Droprate); +#endif +#ifdef _DEBUG + COM_AddCommand("numnodes", Command_Numnodes); +#endif + + RegisterNetXCmd(XD_KICK, Got_KickCmd); + RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); +#ifdef DUMPCONSISTENCY + CV_RegisterVar(&cv_dumpconsistency); +#endif + Ban_Load_File(false); + + gametic = 0; + localgametic = 0; + + // do not send anything before the real begin + SV_StopServer(); + SV_ResetServer(); + if (dedicated) + SV_SpawnServer(); +} + +SINT8 nametonum(const char *name) +{ + INT32 playernum, i; + + if (!strcmp(name, "0")) + return 0; + + playernum = (SINT8)atoi(name); + + if (playernum < 0 || playernum >= MAXPLAYERS) + return -1; + + if (playernum) + { + if (playeringame[playernum]) + return (SINT8)playernum; + else + return -1; + } + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && !stricmp(player_names[i], name)) + return (SINT8)i; + + CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name); + + return -1; +} + +// is there a game running +boolean Playing(void) +{ + return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); +} + /** Returns the number of players playing. * \return Number of players. Can be zero if we're running a ::dedicated * server. @@ -1551,6 +1434,120 @@ INT32 D_NumPlayers(void) return num; } +// +// NetUpdate +// Builds ticcmds for console player, +// sends out a packet +// +// no more use random generator, because at very first tic isn't yet synchronized +// Note: It is called consistAncy on purpose. +// +INT16 Consistancy(void) +{ + INT32 i; + UINT32 ret = 0; +#ifdef MOBJCONSISTANCY + thinker_t *th; + mobj_t *mo; +#endif + + DEBFILE(va("TIC %u ", gametic)); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + ret ^= 0xCCCC; + else if (!players[i].mo); + else + { + ret += players[i].mo->x; + ret -= players[i].mo->y; + ret += players[i].powers[pw_shield]; + ret *= i+1; + } + } + // I give up + // Coop desynching enemies is painful + if (!G_PlatformGametype()) + ret += P_GetRandSeed(); + +#ifdef MOBJCONSISTANCY + if (gamestate == GS_LEVEL) + { + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) + { + ret -= mo->type; + ret += mo->x; + ret -= mo->y; + ret += mo->z; + ret -= mo->momx; + ret += mo->momy; + ret -= mo->momz; + ret += mo->angle; + ret -= mo->flags; + ret += mo->flags2; + ret -= mo->eflags; + if (mo->target) + { + ret += mo->target->type; + ret -= mo->target->x; + ret += mo->target->y; + ret -= mo->target->z; + ret += mo->target->momx; + ret -= mo->target->momy; + ret += mo->target->momz; + ret -= mo->target->angle; + ret += mo->target->flags; + ret -= mo->target->flags2; + ret += mo->target->eflags; + ret -= mo->target->state - states; + ret += mo->target->tics; + ret -= mo->target->sprite; + ret += mo->target->frame; + } + else + ret ^= 0x3333; + if (mo->tracer && mo->tracer->type != MT_OVERLAY) + { + ret += mo->tracer->type; + ret -= mo->tracer->x; + ret += mo->tracer->y; + ret -= mo->tracer->z; + ret += mo->tracer->momx; + ret -= mo->tracer->momy; + ret += mo->tracer->momz; + ret -= mo->tracer->angle; + ret += mo->tracer->flags; + ret -= mo->tracer->flags2; + ret += mo->tracer->eflags; + ret -= mo->tracer->state - states; + ret += mo->tracer->tics; + ret -= mo->tracer->sprite; + ret += mo->tracer->frame; + } + else + ret ^= 0xAAAA; + ret -= mo->state - states; + ret += mo->tics; + ret -= mo->sprite; + ret += mo->frame; + } + } + } +#endif + + DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF))); + + return (INT16)(ret & 0xFFFF); +} + tic_t GetLag(INT32 node) { return gametic - netnodes[node].tic; From 0859a1597f723f8cc10170e2cfe818a27d1104f9 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Fri, 13 Jan 2023 22:28:03 +0100 Subject: [PATCH 52/89] Add missing include directive --- src/sdl/i_system.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 7684045fc..47402e549 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -189,6 +189,7 @@ static char returnWadPath[256]; #include "../i_threads.h" #include "../screen.h" //vid.WndParent #include "../netcode/d_net.h" +#include "../netcode/commands.h" #include "../g_game.h" #include "../filesrch.h" #include "endtxt.h" From a7a3f97c7b5e2233caa565a99d138b5cfa3b25c0 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Jan 2023 14:49:33 +0100 Subject: [PATCH 53/89] Split PT_ClientCmd into functions --- src/netcode/tic_command.c | 95 +++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index bab0fad0a..417a2fdfd 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -103,6 +103,55 @@ void D_ResetTiccmds(void) D_Clearticcmd(textcmds[i]->tic); } +// Check ticcmd for "speed hacks" +static void CheckTiccmdHacks(INT32 playernum) +{ + ticcmd_t *cmd = &netcmds[maketic%BACKUPTICS][playernum]; + if (cmd->forwardmove > MAXPLMOVE || cmd->forwardmove < -MAXPLMOVE + || cmd->sidemove > MAXPLMOVE || cmd->sidemove < -MAXPLMOVE) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), playernum); + SendKick(playernum, KICK_MSG_CON_FAIL); + } +} + +// Check player consistancy during the level +static void CheckConsistancy(SINT8 nodenum, tic_t tic) +{ + netnode_t *node = &netnodes[nodenum]; + INT16 neededconsistancy = consistancy[tic%BACKUPTICS]; + INT16 clientconsistancy = SHORT(netbuffer->u.clientpak.consistancy); + + if (tic > gametic || tic + BACKUPTICS - 1 <= gametic || gamestate != GS_LEVEL + || neededconsistancy == clientconsistancy || SV_ResendingSavegameToAnyone() + || node->resendingsavegame || node->savegameresendcooldown > I_GetTime()) + return; + + if (cv_resynchattempts.value) + { + // Tell the client we are about to resend them the gamestate + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + HSendPacket(nodenum, true, 0, 0); + + node->resendingsavegame = true; + + if (cv_blamecfail.value) + CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), + node->player+1, player_names[node->player], + neededconsistancy, clientconsistancy); + + DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", + node->player, tic, neededconsistancy, clientconsistancy)); + } + else + { + SendKick(node->player, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + + DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", + node->player, tic, neededconsistancy, clientconsistancy)); + } +} + void PT_ClientCmd(SINT8 node, INT32 netconsole) { tic_t realend, realstart; @@ -143,56 +192,14 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole) // Copy ticcmd G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); - // Check ticcmd for "speed hacks" - if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE - || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); - //D_Clearticcmd(k); - - SendKick(netconsole, KICK_MSG_CON_FAIL); - return; - } - // Splitscreen cmd if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) && netnodes[node].player2 >= 0) G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2], &netbuffer->u.client2pak.cmd2, 1); - // Check player consistancy during the level - if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL - && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) - && !SV_ResendingSavegameToAnyone() - && !netnodes[node].resendingsavegame && netnodes[node].savegameresendcooldown <= I_GetTime()) - { - if (cv_resynchattempts.value) - { - // Tell the client we are about to resend them the gamestate - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - HSendPacket(node, true, 0, 0); - - netnodes[node].resendingsavegame = true; - - if (cv_blamecfail.value) - CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), - netconsole+1, player_names[netconsole], - consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy)); - DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - return; - } - else - { - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - netconsole, realstart, consistancy[realstart%BACKUPTICS], - SHORT(netbuffer->u.clientpak.consistancy))); - return; - } - } + CheckTiccmdHacks(netconsole); + CheckConsistancy(node, realstart); } void PT_ServerTics(SINT8 node, INT32 netconsole) From 4081dd8edfa32f0aa7126da280b257ed0294bba3 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Jan 2023 14:52:13 +0100 Subject: [PATCH 54/89] Move net command copying to a new function --- src/netcode/net_command.c | 20 ++++++++++++++++++++ src/netcode/net_command.h | 1 + src/netcode/tic_command.c | 21 +++------------------ 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 13477d42d..227b78660 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -304,6 +304,26 @@ void PT_TextCmd(SINT8 node, INT32 netconsole) } } +void SV_CopyNetCommandsToServerPacket(tic_t tic) +{ + servertics_pak *packet = &netbuffer->u.serverpak; + UINT8 *cmds = (UINT8*)&packet->cmds[packet->numslots * packet->numtics]; + UINT8 numcmds; + + numcmds = *cmds++; + + for (UINT32 i = 0; i < numcmds; i++) + { + INT32 playernum = *cmds++; // playernum + size_t size = cmds[0]+1; + + if (tic >= gametic) // Don't copy old net commands + M_Memcpy(D_GetTextcmd(tic, playernum), cmds, size); + cmds += size; + } +} + +void CL_SendNetCommands(void) void SendKick(UINT8 playernum, UINT8 msg) { UINT8 buf[2]; diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h index f1b4b2f3f..a9447ed7a 100644 --- a/src/netcode/net_command.h +++ b/src/netcode/net_command.h @@ -57,6 +57,7 @@ void ExtraDataTicker(void); size_t TotalTextCmdPerTic(tic_t tic); void PT_TextCmd(SINT8 node, INT32 netconsole); +void SV_CopyNetCommandsToServerPacket(tic_t tic); void SendKick(UINT8 playernum, UINT8 msg); void SendKicksForNode(SINT8 node, UINT8 msg); diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index 417a2fdfd..2eb3b10ac 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -204,7 +204,6 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole) void PT_ServerTics(SINT8 node, INT32 netconsole) { - UINT8 *pak, *txtpak, numtxtpak; tic_t realend, realstart; if (!netnodes[node].ingame) @@ -230,19 +229,15 @@ void PT_ServerTics(SINT8 node, INT32 netconsole) realstart = netbuffer->u.serverpak.starttic; realend = realstart + netbuffer->u.serverpak.numtics; - txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots - * netbuffer->u.serverpak.numtics]; - if (realend > gametic + CLIENTBACKUPTICS) realend = gametic + CLIENTBACKUPTICS; cl_packetmissed = realstart > neededtic; if (realstart <= neededtic && realend > neededtic) { - tic_t i, j; - pak = (UINT8 *)&netbuffer->u.serverpak.cmds; + UINT8 *pak = (UINT8 *)&netbuffer->u.serverpak.cmds; - for (i = realstart; i < realend; i++) + for (tic_t i = realstart; i < realend; i++) { // clear first D_Clearticcmd(i); @@ -251,17 +246,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole) pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); - // copy the textcmds - numtxtpak = *txtpak++; - for (j = 0; j < numtxtpak; j++) - { - INT32 k = *txtpak++; // playernum - const size_t txtsize = txtpak[0]+1; - - if (i >= gametic) // Don't copy old net commands - M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); - txtpak += txtsize; - } + SV_CopyNetCommandsToServerPacket(i); } neededtic = realend; From aa18d5c3ffebab326f21db8a360e8b8f674c48bc Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Jan 2023 14:53:27 +0100 Subject: [PATCH 55/89] Move net command sending to a new function --- src/netcode/net_command.c | 22 ++++++++++++++++++++++ src/netcode/net_command.h | 1 + src/netcode/tic_command.c | 22 +--------------------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 227b78660..9e089a1a2 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -324,6 +324,28 @@ void SV_CopyNetCommandsToServerPacket(tic_t tic) } void CL_SendNetCommands(void) +{ + // Send extra data if needed + if (localtextcmd[0]) + { + netbuffer->packettype = PT_TEXTCMD; + M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... + localtextcmd[0] = 0; + } + + // Send extra data if needed for player 2 (splitscreen) + if (localtextcmd2[0]) + { + netbuffer->packettype = PT_TEXTCMD2; + M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... + localtextcmd2[0] = 0; + } +} + void SendKick(UINT8 playernum, UINT8 msg) { UINT8 buf[2]; diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h index a9447ed7a..3a433ebe4 100644 --- a/src/netcode/net_command.h +++ b/src/netcode/net_command.h @@ -58,6 +58,7 @@ size_t TotalTextCmdPerTic(tic_t tic); void PT_TextCmd(SINT8 node, INT32 netconsole); void SV_CopyNetCommandsToServerPacket(tic_t tic); +void CL_SendNetCommands(void); void SendKick(UINT8 playernum, UINT8 msg); void SendKicksForNode(SINT8 node, UINT8 msg); diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index 2eb3b10ac..76d46451f 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -295,27 +295,7 @@ void CL_SendClientCmd(void) } if (cl_mode == CL_CONNECTED || dedicated) - { - // Send extra data if needed - if (localtextcmd[0]) - { - netbuffer->packettype = PT_TEXTCMD; - M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... - localtextcmd[0] = 0; - } - - // Send extra data if needed for player 2 (splitscreen) - if (localtextcmd2[0]) - { - netbuffer->packettype = PT_TEXTCMD2; - M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... - localtextcmd2[0] = 0; - } - } + CL_SendNetCommands(); } // send the server packet From 530a03cc7dce25d7473a23f91004a8c1b532c37e Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Jan 2023 15:14:09 +0100 Subject: [PATCH 56/89] Fix function name --- src/netcode/net_command.c | 2 +- src/netcode/net_command.h | 2 +- src/netcode/tic_command.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 9e089a1a2..98c578b79 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -304,7 +304,7 @@ void PT_TextCmd(SINT8 node, INT32 netconsole) } } -void SV_CopyNetCommandsToServerPacket(tic_t tic) +void CL_CopyNetCommandsFromServerPacket(tic_t tic) { servertics_pak *packet = &netbuffer->u.serverpak; UINT8 *cmds = (UINT8*)&packet->cmds[packet->numslots * packet->numtics]; diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h index 3a433ebe4..0e8339ef7 100644 --- a/src/netcode/net_command.h +++ b/src/netcode/net_command.h @@ -57,7 +57,7 @@ void ExtraDataTicker(void); size_t TotalTextCmdPerTic(tic_t tic); void PT_TextCmd(SINT8 node, INT32 netconsole); -void SV_CopyNetCommandsToServerPacket(tic_t tic); +void CL_CopyNetCommandsFromServerPacket(tic_t tic); void CL_SendNetCommands(void); void SendKick(UINT8 playernum, UINT8 msg); void SendKicksForNode(SINT8 node, UINT8 msg); diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index 76d46451f..6b237ed61 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -246,7 +246,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole) pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); - SV_CopyNetCommandsToServerPacket(i); + CL_CopyNetCommandsFromServerPacket(i); } neededtic = realend; From 9291416a90749b22b8c971a89a5ea39e3fe0955c Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Jan 2023 19:01:36 +0100 Subject: [PATCH 57/89] Split SV_SendTics into functions --- src/netcode/net_command.c | 21 +++++++++ src/netcode/net_command.h | 1 + src/netcode/tic_command.c | 95 ++++++++++++++++++--------------------- 3 files changed, 66 insertions(+), 51 deletions(-) diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 98c578b79..11b351af7 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -304,6 +304,27 @@ void PT_TextCmd(SINT8 node, INT32 netconsole) } } +void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf) +{ + UINT8 *numcmds; + + numcmds = (*buf)++; + *numcmds = 0; + for (INT32 i = 0; i < MAXPLAYERS; i++) + { + UINT8 *cmd = D_GetExistingTextcmd(tic, i); + INT32 size = cmd ? cmd[0] : 0; + + if ((!i || playeringame[i]) && size) + { + (*numcmds)++; + WRITEUINT8(*buf, i); + M_Memcpy(*buf, cmd, size + 1); + *buf += size + 1; + } + } +} + void CL_CopyNetCommandsFromServerPacket(tic_t tic) { servertics_pak *packet = &netbuffer->u.serverpak; diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h index 0e8339ef7..cc26aeb0e 100644 --- a/src/netcode/net_command.h +++ b/src/netcode/net_command.h @@ -57,6 +57,7 @@ void ExtraDataTicker(void); size_t TotalTextCmdPerTic(tic_t tic); void PT_TextCmd(SINT8 node, INT32 netconsole); +void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf); void CL_CopyNetCommandsFromServerPacket(tic_t tic); void CL_SendNetCommands(void); void SendKick(UINT8 playernum, UINT8 msg); diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index 6b237ed61..bf8c5d21d 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -298,16 +298,56 @@ void CL_SendClientCmd(void) CL_SendNetCommands(); } +// PT_SERVERTICS packets can grow too large for a single UDP packet, +// So this checks how many tics worth of data can be sent in one packet. +// The rest can be sent later, usually the next tic. +static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t lasttic) +{ + size_t size = BASESERVERTICSSIZE; + tic_t tic; + + for (tic = firsttic; tic < lasttic; tic++) + { + size += sizeof (ticcmd_t) * doomcom->numslots; + size += TotalTextCmdPerTic(tic); + + if (size > software_MAXPACKETLENGTH) + { + DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", + sizeu1(size), tic, firsttic, lasttic)); + lasttic = tic; + + // too bad: too much player have send extradata and there is too + // much data in one tic. + // To avoid it put the data on the next tic. (see getpacket + // textcmd case) but when numplayer changes the computation can be different + if (lasttic == firsttic) + { + if (size > MAXPACKETLENGTH) + I_Error("Too many players: can't send %s data for %d players to node %d\n" + "Well sorry nobody is perfect....\n", + sizeu1(size), doomcom->numslots, nodenum); + else + { + lasttic++; // send it anyway! + DEBFILE("sending it anyway\n"); + } + } + break; + } + } + + return lasttic - firsttic; +} + // send the server packet // send tic from firstticstosend to maketic-1 void SV_SendTics(void) { tic_t realfirsttic, lasttictosend, i; UINT32 n; - INT32 j; size_t packsize; UINT8 *bufpos; - UINT8 *ntextcmd; // send to all client but not to me // for each node create a packet with x tics and send it @@ -336,38 +376,7 @@ void SV_SendTics(void) if (realfirsttic < firstticstosend) realfirsttic = firstticstosend; - // compute the length of the packet and cut it if too large - packsize = BASESERVERTICSSIZE; - for (i = realfirsttic; i < lasttictosend; i++) - { - packsize += sizeof (ticcmd_t) * doomcom->numslots; - packsize += TotalTextCmdPerTic(i); - - if (packsize > software_MAXPACKETLENGTH) - { - DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", - sizeu1(packsize), i, realfirsttic, lasttictosend)); - lasttictosend = i; - - // too bad: too much player have send extradata and there is too - // much data in one tic. - // To avoid it put the data on the next tic. (see getpacket - // textcmd case) but when numplayer changes the computation can be different - if (lasttictosend == realfirsttic) - { - if (packsize > MAXPACKETLENGTH) - I_Error("Too many players: can't send %s data for %d players to node %d\n" - "Well sorry nobody is perfect....\n", - sizeu1(packsize), doomcom->numslots, n); - else - { - lasttictosend++; // send it anyway! - DEBFILE("sending it anyway\n"); - } - } - break; - } - } + lasttictosend = realfirsttic + SV_CalculateNumTicsForPacket(n, realfirsttic, lasttictosend); // Send the tics netbuffer->packettype = PT_SERVERTICS; @@ -383,23 +392,7 @@ void SV_SendTics(void) // add textcmds for (i = realfirsttic; i < lasttictosend; i++) - { - ntextcmd = bufpos++; - *ntextcmd = 0; - for (j = 0; j < MAXPLAYERS; j++) - { - UINT8 *textcmd = D_GetExistingTextcmd(i, j); - INT32 size = textcmd ? textcmd[0] : 0; - - if ((!j || playeringame[j]) && size) - { - (*ntextcmd)++; - WRITEUINT8(bufpos, j); - M_Memcpy(bufpos, textcmd, size + 1); - bufpos += size + 1; - } - } - } + SV_WriteNetCommandsForTic(); packsize = bufpos - (UINT8 *)&(netbuffer->u); HSendPacket(n, false, 0, packsize); From 823fa5d27bd7d581300851087277c5d721f5b421 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Jan 2023 20:02:06 +0100 Subject: [PATCH 58/89] Cleanup --- src/netcode/client_connection.c | 21 ++++------ src/netcode/commands.c | 23 +++++------ src/netcode/d_clisrv.c | 57 ++++++++++---------------- src/netcode/d_net.c | 66 ++++++++++++------------------ src/netcode/d_netfil.c | 71 ++++++++++++--------------------- src/netcode/gamestate.c | 8 +--- src/netcode/i_tcp.c | 34 ++++++---------- src/netcode/net_command.c | 11 ++--- src/netcode/server_connection.c | 11 ++--- src/netcode/tic_command.c | 56 +++++++++++--------------- 10 files changed, 132 insertions(+), 226 deletions(-) diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c index ff20a6fc7..faa91b6d7 100644 --- a/src/netcode/client_connection.c +++ b/src/netcode/client_connection.c @@ -60,7 +60,7 @@ static inline void CL_DrawConnectionStatus(void) if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES) { - INT32 i, animtime = ((ccstime / 4) & 15) + 16; + INT32 animtime = ((ccstime / 4) & 15) + 16; UINT8 palstart; const char *cltext; @@ -75,7 +75,7 @@ static inline void CL_DrawConnectionStatus(void) palstart = 96; // Green if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) - for (i = 0; i < 16; ++i) // 15 pal entries total. + for (INT32 i = 0; i < 16; ++i) // 15 pal entries total. V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); switch (cl_mode) @@ -134,14 +134,13 @@ static inline void CL_DrawConnectionStatus(void) { INT32 totalfileslength; INT32 loadcompletednum = 0; - INT32 i; V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); //ima just count files here if (fileneeded) { - for (i = 0; i < fileneedednum; i++) + for (INT32 i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_OPEN) loadcompletednum++; } @@ -277,9 +276,7 @@ UINT32 serverlistcount = 0; static void SL_ClearServerList(INT32 connectedserver) { - UINT32 i; - - for (i = 0; i < serverlistcount; i++) + for (UINT32 i = 0; i < serverlistcount; i++) if (connectedserver != serverlist[i].node) { Net_CloseConnection(serverlist[i].node|FORCECLOSE); @@ -290,8 +287,7 @@ static void SL_ClearServerList(INT32 connectedserver) static UINT32 SL_SearchServer(INT32 node) { - UINT32 i; - for (i = 0; i < serverlistcount; i++) + for (UINT32 i = 0; i < serverlistcount; i++) if (serverlist[i].node == node) return i; @@ -388,9 +384,7 @@ Fetch_servers_thread (struct Fetch_servers_ctx *ctx) void CL_QueryServerList (msg_server_t *server_list) { - INT32 i; - - for (i = 0; server_list[i].header.buffer[0]; i++) + for (INT32 i = 0; server_list[i].header.buffer[0]; i++) { // Make sure MS version matches our own, to // thwart nefarious servers who lie to the MS. @@ -781,7 +775,6 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) { boolean waitmore; - INT32 i; switch (cl_mode) { @@ -808,7 +801,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic break; case CL_DOWNLOADFILES: waitmore = false; - for (i = 0; i < fileneedednum; i++) + for (INT32 i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_DOWNLOADING || fileneeded[i].status == FS_REQUESTED) { diff --git a/src/netcode/commands.c b/src/netcode/commands.c index 834e1c666..4d9a48b6b 100644 --- a/src/netcode/commands.c +++ b/src/netcode/commands.c @@ -79,7 +79,6 @@ static void Ban_Clear(void) void Ban_Load_File(boolean warning) { FILE *f; - size_t i; const char *address, *mask; char buffer[MAX_WADPATH]; @@ -97,7 +96,7 @@ void Ban_Load_File(boolean warning) Ban_Clear(); - for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) + for (size_t i=0; fgets(buffer, (int)sizeof(buffer), f); i++) { address = strtok(buffer, " \t\r\n"); mask = strtok(NULL, " \t\r\n"); @@ -113,7 +112,6 @@ void Ban_Load_File(boolean warning) void D_SaveBan(void) { FILE *f; - size_t i; banreason_t *reasonlist = reasonhead; const char *address, *mask; const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); @@ -132,7 +130,7 @@ void D_SaveBan(void) return; } - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + for (size_t i = 0;(address = I_GetBanAddress(i)) != NULL;i++) { if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) fprintf(f, "%s 0", address); @@ -236,12 +234,12 @@ void Command_Ban(void) } else { - size_t i, j = COM_Argc(); + size_t j = COM_Argc(); char message[MAX_REASONLENGTH]; //Steal from the motd code so you don't have to put the reason in quotes. strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) + for (size_t i = 3; i < j; i++) { strlcat(message, " ", sizeof message); strlcat(message, COM_Argv(i), sizeof message); @@ -340,12 +338,12 @@ void Command_Kick(void) } else { - size_t i, j = COM_Argc(); + size_t j = COM_Argc(); char message[MAX_REASONLENGTH]; //Steal from the motd code so you don't have to put the reason in quotes. strlcpy(message, COM_Argv(2), sizeof message); - for (i = 3; i < j; i++) + for (size_t i = 3; i < j; i++) { strlcat(message, " ", sizeof message); strlcat(message, COM_Argv(i), sizeof message); @@ -435,9 +433,7 @@ void Command_connect(void) void Command_GetPlayerNum(void) { - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) { if (serverplayer == i) @@ -453,18 +449,17 @@ void Command_GetPlayerNum(void) */ void Command_Nodes(void) { - INT32 i; size_t maxlen = 0; const char *address; - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { const size_t plen = strlen(player_names[i]); if (playeringame[i] && plen > maxlen) maxlen = plen; } - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { if (playeringame[i]) { diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 1658856b6..105526dd2 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -440,11 +440,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (M_CheckParm("-consisdump")) // Helps debugging some problems { - INT32 i; - CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum); - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) continue; @@ -545,8 +543,8 @@ static void RedistributeSpecialStageSpheres(INT32 playernum) INT32 sincrement = max(spheres / count, 1); INT32 rincrement = max(rings / count, 1); - INT32 i, n; - for (i = 0; i < MAXPLAYERS; i++) + INT32 n; + for (INT32 i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || i == playernum) continue; @@ -644,10 +642,8 @@ void D_QuitNetGame(void) if (server) { - INT32 i; - netbuffer->packettype = PT_SERVERSHUTDOWN; - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) if (netnodes[i].ingame) HSendPacket(i, true, 0, 0); #ifdef MASTERSERVER @@ -701,8 +697,6 @@ void CL_RemoveSplitscreenPlayer(void) void SV_ResetServer(void) { - INT32 i; - // +1 because this command will be executed in com_executebuffer in // tryruntic so gametic will be incremented, anyway maketic > gametic // is not an issue @@ -713,10 +707,10 @@ void SV_ResetServer(void) joindelay = 0; - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) ResetNode(i); - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { LUA_InvalidatePlayer(&players[i]); playeringame[i] = false; @@ -755,10 +749,9 @@ void SV_ResetServer(void) static inline void SV_GenContext(void) { - UINT8 i; // generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z // (hopefully M_Random is initialized!! if not this will be awfully silly!) - for (i = 0; i < 8; i++) + for (UINT8 i = 0; i < 8; i++) { const char a = M_RandomKey(26*2); if (a < 26) // uppercase @@ -814,8 +807,6 @@ void SV_StartSinglePlayerServer(void) void SV_StopServer(void) { - tic_t i; - if (gamestate == GS_INTERMISSION) Y_EndIntermission(); gamestate = wipegamestate = GS_NULL; @@ -823,7 +814,7 @@ void SV_StopServer(void) localtextcmd[0] = 0; localtextcmd2[0] = 0; - for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++) + for (tic_t i = firstticstosend; i < firstticstosend + BACKUPTICS; i++) D_Clearticcmd(i); consoleplayer = 0; @@ -913,7 +904,6 @@ If they're not lagging, decrement the timer by 1. Of course, reset all of this i static inline void PingUpdate(void) { - INT32 i; boolean laggers[MAXPLAYERS]; UINT8 numlaggers = 0; memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); @@ -923,7 +913,7 @@ static inline void PingUpdate(void) //check for ping limit breakage. if (cv_maxping.value) { - for (i = 1; i < MAXPLAYERS; i++) + for (INT32 i = 1; i < MAXPLAYERS; i++) { if (playeringame[i] && !players[i].quittime && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) @@ -940,7 +930,7 @@ static inline void PingUpdate(void) //in that case, it is probably the server's fault. if (numlaggers < D_NumPlayers() - 1) { - for (i = 1; i < MAXPLAYERS; i++) + for (INT32 i = 1; i < MAXPLAYERS; i++) { if (playeringame[i] && laggers[i]) { @@ -965,7 +955,7 @@ static inline void PingUpdate(void) } //make the ping packet and clear server data for next one - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; //server takes a snapshot of the real ping for display. @@ -978,7 +968,7 @@ static inline void PingUpdate(void) netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value; //send out our ping packets - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) if (netnodes[i].ingame) HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); @@ -999,8 +989,7 @@ static void PT_Ping(SINT8 node, INT32 netconsole) //Update client ping table from the server. if (client) { - UINT8 i; - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; @@ -1225,7 +1214,6 @@ void NetUpdate(void) static tic_t gametime = 0; static tic_t resptime = 0; tic_t nowtime; - INT32 i; INT32 realtics; nowtime = I_GetTime(); @@ -1248,7 +1236,7 @@ void NetUpdate(void) if (netgame && !(gametime % 35)) // update once per second. PingUpdate(); // update node latency values so we can take an average later. - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && playernode[i] != UINT8_MAX) realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); pingmeasurecount++; @@ -1287,7 +1275,7 @@ void NetUpdate(void) hu_redownloadinggamestate = false; firstticstosend = gametic; - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) if (netnodes[i].ingame && netnodes[i].tic < firstticstosend) { firstticstosend = netnodes[i].tic; @@ -1301,7 +1289,7 @@ void NetUpdate(void) if (maketic + counts >= firstticstosend + BACKUPTICS) counts = firstticstosend+BACKUPTICS-maketic-1; - for (i = 0; i < counts; i++) + for (INT32 i = 0; i < counts; i++) SV_Maketic(); // Create missed tics and increment maketic for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged @@ -1318,7 +1306,7 @@ void NetUpdate(void) // Handle timeouts to prevent definitive freezes from happenning if (server) { - for (i = 1; i < MAXNETNODES; i++) + for (INT32 i = 1; i < MAXNETNODES; i++) if (netnodes[i].ingame && netnodes[i].freezetimeout < I_GetTime()) Net_ConnectionTimeout(i); @@ -1387,7 +1375,7 @@ void D_ClientServerInit(void) SINT8 nametonum(const char *name) { - INT32 playernum, i; + INT32 playernum; if (!strcmp(name, "0")) return 0; @@ -1405,7 +1393,7 @@ SINT8 nametonum(const char *name) return -1; } - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && !stricmp(player_names[i], name)) return (SINT8)i; @@ -1427,8 +1415,8 @@ boolean Playing(void) */ INT32 D_NumPlayers(void) { - INT32 num = 0, ix; - for (ix = 0; ix < MAXPLAYERS; ix++) + INT32 num = 0; + for (INT32 ix = 0; ix < MAXPLAYERS; ix++) if (playeringame[ix]) num++; return num; @@ -1444,7 +1432,6 @@ INT32 D_NumPlayers(void) // INT16 Consistancy(void) { - INT32 i; UINT32 ret = 0; #ifdef MOBJCONSISTANCY thinker_t *th; @@ -1453,7 +1440,7 @@ INT16 Consistancy(void) DEBFILE(va("TIC %u ", gametic)); - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) ret ^= 0xCCCC; diff --git a/src/netcode/d_net.c b/src/netcode/d_net.c index 3c16392d0..a4b0778e3 100644 --- a/src/netcode/d_net.c +++ b/src/netcode/d_net.c @@ -211,7 +211,7 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b) static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) { node_t *node = &nodes[doomcom->remotenode]; - INT32 i, numfreeslot = 0; + INT32 numfreeslot = 0; if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0) { @@ -219,7 +219,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) return false; } - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) if (!ackpak[i].acknum) { // For low priority packets, make sure to let freeslots so urgent packets can be sent @@ -276,10 +276,10 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) */ INT32 Net_GetFreeAcks(boolean urgent) { - INT32 i, numfreeslot = 0; + INT32 numfreeslot = 0; INT32 n = 0; // Number of free acks found - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) if (!ackpak[i].acknum) { // For low priority packets, make sure to let freeslots so urgent packets can be sent @@ -315,7 +315,6 @@ static void RemoveAck(INT32 i) // We have got a packet, proceed the ack request and ack return static boolean Processackpak(void) { - INT32 i; boolean goodpacket = true; node_t *node = &nodes[doomcom->remotenode]; @@ -324,7 +323,7 @@ static boolean Processackpak(void) { node->remotefirstack = netbuffer->ackreturn; // Search the ackbuffer and free it - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes && cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0) { @@ -346,7 +345,7 @@ static boolean Processackpak(void) else { // Check if it is not already in the queue - for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) + for (INT32 i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) if (node->acktosend[i] == ack) { DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack)); @@ -374,7 +373,7 @@ static boolean Processackpak(void) while (change) { change = false; - for (i = node->acktosend_tail; i != node->acktosend_head; + for (INT32 i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) { if (cmpack(node->acktosend[i], nextfirstack) <= 0) @@ -434,11 +433,9 @@ void Net_SendAcks(INT32 node) static void GotAcks(void) { - INT32 i, j; - - for (j = 0; j < MAXACKTOSEND; j++) + for (INT32 j = 0; j < MAXACKTOSEND; j++) if (netbuffer->u.textcmd[j]) - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode) { if (ackpak[i].acknum == netbuffer->u.textcmd[j]) @@ -475,9 +472,8 @@ void Net_ConnectionTimeout(INT32 node) // Resend the data if needed void Net_AckTicker(void) { - INT32 i; - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) { const INT32 nodei = ackpak[i].destinationnode; node_t *node = &nodes[nodei]; @@ -504,7 +500,7 @@ void Net_AckTicker(void) } } - for (i = 1; i < MAXNETNODES; i++) + for (INT32 i = 1; i < MAXNETNODES; i++) { // This is something like node open flag if (nodes[i].firstacktosend) @@ -567,9 +563,7 @@ void Net_UnAcknowledgePacket(INT32 node) */ static boolean Net_AllAcksReceived(void) { - INT32 i; - - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum) return false; @@ -611,12 +605,10 @@ static void InitNode(node_t *node) static void InitAck(void) { - INT32 i; - - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) ackpak[i].acknum = 0; - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) InitNode(&nodes[i]); } @@ -627,8 +619,7 @@ static void InitAck(void) */ void Net_AbortPacketType(UINT8 packettype) { - INT32 i; - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && (ackpak[i].pak.data.packettype == packettype || packettype == UINT8_MAX)) { @@ -643,7 +634,6 @@ void Net_AbortPacketType(UINT8 packettype) // remove a node, clear all ack from this node and reset askret void Net_CloseConnection(INT32 node) { - INT32 i; boolean forceclose = (node & FORCECLOSE) != 0; if (node == -1) @@ -673,7 +663,7 @@ void Net_CloseConnection(INT32 node) } // check if we are waiting for an ack from this node - for (i = 0; i < MAXACKPACKETS; i++) + for (INT32 i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == node) { if (!forceclose) @@ -697,9 +687,8 @@ static UINT32 NetbufferChecksum(void) UINT32 c = 0x1234567; const INT32 l = doomcom->datalength - 4; const UINT8 *buf = (UINT8 *)netbuffer + 4; - INT32 i; - for (i = 0; i < l; i++, buf++) + for (INT32 i = 0; i < l; i++, buf++) c += (*buf) * (i+1); return LONG(c); @@ -710,9 +699,8 @@ static UINT32 NetbufferChecksum(void) static void fprintfstring(char *s, size_t len) { INT32 mode = 0; - size_t i; - for (i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) if (s[i] < 32) { if (!mode) @@ -879,7 +867,6 @@ void Command_Drop(void) { INT32 packetquantity; const char *packetname; - size_t i; if (COM_Argc() < 2) { @@ -909,11 +896,11 @@ void Command_Drop(void) packetname = COM_Argv(1); if (!(stricmp(packetname, "all") && stricmp(packetname, "any"))) - for (i = 0; i < NUMPACKETTYPE; i++) + for (size_t i = 0; i < NUMPACKETTYPE; i++) packetdropquantity[i] = packetquantity; else { - for (i = 0; i < NUMPACKETTYPE; i++) + for (size_t i = 0; i < NUMPACKETTYPE; i++) if (!stricmp(packetname, packettypename[i])) { packetdropquantity[i] = packetquantity; @@ -1336,13 +1323,12 @@ void Command_Ping_f(void) int name_width = 0; int ms_width = 0; - int n; - INT32 i; - pingc = 0; - for (i = 1; i < MAXPLAYERS; ++i) + for (INT32 i = 1; i < MAXPLAYERS; ++i) if (playeringame[i]) { + int n; + n = strlen(player_names[i]); if (n > name_width) name_width = n; @@ -1362,7 +1348,7 @@ void Command_Ping_f(void) qsort(pingv, pingc, sizeof (struct pingcell), &pingcellcmp); - for (i = 0; i < pingc; ++i) + for (INT32 i = 0; i < pingc; ++i) { CONS_Printf("%02d : %-*s %*d ms\n", pingv[i].num, @@ -1378,15 +1364,13 @@ void Command_Ping_f(void) void D_CloseConnection(void) { - INT32 i; - if (netgame) { // wait the ackreturn with timout of 5 Sec Net_WaitAllAckReceived(5); // close all connection - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) Net_CloseConnection(i|FORCECLOSE); InitAck(); diff --git a/src/netcode/d_netfil.c b/src/netcode/d_netfil.c index 205abf713..10b7359ad 100644 --- a/src/netcode/d_netfil.c +++ b/src/netcode/d_netfil.c @@ -128,9 +128,7 @@ consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR static UINT16 GetWadNumFromFileNeededId(UINT8 id) { - UINT16 wadnum; - - for (wadnum = mainwads; wadnum < numwadfiles; wadnum++) + for (UINT16 wadnum = mainwads; wadnum < numwadfiles; wadnum++) { if (!wadfiles[wadnum]->important) continue; @@ -150,14 +148,13 @@ static UINT16 GetWadNumFromFileNeededId(UINT8 id) */ UINT8 *PutFileNeeded(UINT16 firstfile) { - size_t i; UINT8 count = 0; UINT8 *p_start = netbuffer->packettype == PT_MOREFILESNEEDED ? netbuffer->u.filesneededcfg.files : netbuffer->u.serverinfo.fileneeded; UINT8 *p = p_start; char wadfilename[MAX_WADPATH] = ""; UINT8 filestatus, folder; - for (i = mainwads; i < numwadfiles; i++) //mainwads, otherwise we start on the first mainwad + for (size_t i = mainwads; i < numwadfiles; i++) //mainwads, 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) @@ -232,7 +229,6 @@ void FreeFileNeeded(void) */ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 firstfile) { - INT32 i; UINT8 *p; UINT8 filestatus; @@ -241,7 +237,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi AllocFileNeeded(fileneedednum); - for (i = firstfile; i < fileneedednum; i++) + for (INT32 i = firstfile; i < fileneedednum; i++) { fileneeded[i].type = FILENEEDED_WAD; fileneeded[i].status = FS_NOTCHECKED; // We haven't even started looking for the file yet @@ -281,9 +277,9 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave) */ boolean CL_CheckDownloadable(void) { - UINT8 i,dlstatus = 0; + UINT8 dlstatus = 0; - for (i = 0; i < fileneedednum; i++) + for (UINT8 i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN) { if (fileneeded[i].willsend == 1) @@ -304,7 +300,7 @@ boolean CL_CheckDownloadable(void) // not downloadable, put reason in console CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n")); - for (i = 0; i < fileneedednum; i++) + for (UINT8 i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN) { CONS_Printf(" * \"%s\" (%dK)", fileneeded[i].filename, fileneeded[i].totalsize >> 10); @@ -374,14 +370,13 @@ void CL_AbortDownloadResume(void) boolean CL_SendFileRequest(void) { char *p; - INT32 i; INT64 totalfreespaceneeded = 0, availablefreespace; #ifdef PARANOIA if (M_CheckParm("-nodownload")) I_Error("Attempted to download files in -nodownload mode"); - for (i = 0; i < fileneedednum; i++) + for (INT32 i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2)) { @@ -391,7 +386,7 @@ boolean CL_SendFileRequest(void) netbuffer->packettype = PT_REQUESTFILE; p = (char *)netbuffer->u.textcmd; - for (i = 0; i < fileneedednum; i++) + for (INT32 i = 0; i < fileneedednum; i++) if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)) { totalfreespaceneeded += fileneeded[i].totalsize; @@ -422,7 +417,6 @@ boolean CL_SendFileRequest(void) void PT_RequestFile(SINT8 node) { UINT8 *p = netbuffer->u.textcmd; - UINT8 id; if (client || !cv_downloading.value) { @@ -432,7 +426,7 @@ void PT_RequestFile(SINT8 node) while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow { - id = READUINT8(p); + UINT8 id = READUINT8(p); if (id == 0xFF) break; @@ -543,9 +537,7 @@ INT32 CL_CheckFiles(void) // Load it now boolean CL_LoadServerFiles(void) { - INT32 i; - - for (i = 0; i < fileneedednum; i++) + for (INT32 i = 0; i < fileneedednum; i++) { if (fileneeded[i].status == FS_OPEN) continue; // Already loaded @@ -641,11 +633,10 @@ void AddLuaFileTransfer(const char *filename, const char *mode) static void SV_PrepareSendLuaFileToNextNode(void) { - INT32 i; UINT8 success = 1; // Find a client to send the file to - for (i = 1; i < MAXNETNODES; i++) + for (INT32 i = 1; i < MAXNETNODES; i++) if (luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting { // Tell the client we're about to send them the file @@ -667,12 +658,11 @@ static void SV_PrepareSendLuaFileToNextNode(void) void SV_PrepareSendLuaFile(void) { char *binfilename; - INT32 i; luafiletransfers->ongoing = true; // Set status to "waiting" for everyone - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) luafiletransfers->nodestatus[i] = (netnodes[i].ingame ? LFTNS_WAITING : LFTNS_NONE); if (FIL_ReadFileOK(luafiletransfers->realfilename)) @@ -1153,7 +1143,6 @@ void PT_FileAck(SINT8 node) { fileack_pak *packet = &netbuffer->u.fileack; filetran_t *trans = &transfer[node]; - INT32 i, j; if (client) return; @@ -1175,11 +1164,11 @@ void PT_FileAck(SINT8 node) trans->dontsenduntil = 0; } - for (i = 0; i < packet->numsegments; i++) + for (INT32 i = 0; i < packet->numsegments; i++) { fileacksegment_t *segment = &packet->segments[i]; - for (j = 0; j < 32; j++) + for (INT32 j = 0; j < 32; j++) if (LONG(segment->acks) & (1 << j)) { if (LONG(segment->start) * FILEFRAGMENTSIZE >= trans->txlist->size) @@ -1215,13 +1204,12 @@ void PT_FileReceived(SINT8 node) static void SendAckPacket(fileack_pak *packet, UINT8 fileid) { size_t packetsize; - INT32 i; packetsize = sizeof(*packet) + packet->numsegments * sizeof(*packet->segments); // Finalise the packet packet->fileid = fileid; - for (i = 0; i < packet->numsegments; i++) + for (INT32 i = 0; i < packet->numsegments; i++) { packet->segments[i].start = LONG(packet->segments[i].start); packet->segments[i].acks = LONG(packet->segments[i].acks); @@ -1261,9 +1249,7 @@ static void AddFragmentToAckPacket(fileack_pak *packet, UINT8 iteration, UINT32 void FileReceiveTicker(void) { - INT32 i; - - for (i = 0; i < fileneedednum; i++) + for (INT32 i = 0; i < fileneedednum; i++) { fileneeded_t *file = &fileneeded[i]; @@ -1277,8 +1263,7 @@ void FileReceiveTicker(void) if (file->ackresendposition != UINT32_MAX && file->status == FS_DOWNLOADING) { // Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s - INT32 j; - for (j = 0; j < 2048; j++) + for (INT32 j = 0; j < 2048; j++) { if (file->receivedfragments[file->ackresendposition]) AddFragmentToAckPacket(file->ackpacket, file->iteration, file->ackresendposition, i); @@ -1500,15 +1485,14 @@ void SV_AbortSendFiles(INT32 node) void CloseNetFile(void) { - INT32 i; // Is sending? - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) SV_AbortSendFiles(i); // Receiving a file? if (fileneeded) { - for (i = 0; i < fileneedednum; i++) + for (INT32 i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file) { fclose(fileneeded[i].file); @@ -1541,9 +1525,7 @@ void CloseNetFile(void) void Command_Downloads_f(void) { - INT32 node; - - for (node = 0; node < MAXNETNODES; node++) + for (INT32 node = 0; node < MAXNETNODES; node++) if (transfer[node].txlist && transfer[node].txlist->ram == SF_FILE) // Node is downloading a file? { @@ -1577,14 +1559,11 @@ void Command_Downloads_f(void) void nameonly(char *s) { - size_t j, len; - void *ns; - - for (j = strlen(s); j != (size_t)-1; j--) + for (size_t j = strlen(s); j != (size_t)-1; j--) if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/')) { - ns = &(s[j+1]); - len = strlen(ns); + void *ns = &(s[j+1]); + size_t len = strlen(ns); #if 0 M_Memcpy(s, ns, len+1); #else @@ -1597,9 +1576,9 @@ void nameonly(char *s) // Returns the length in characters of the last element of a path. size_t nameonlylength(const char *s) { - size_t j, len = strlen(s); + size_t len = strlen(s); - for (j = len; j != (size_t)-1; j--) + for (size_t j = len; j != (size_t)-1; j--) if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/')) return len - j - 1; diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c index 4a3e9f3af..d284164c0 100644 --- a/src/netcode/gamestate.c +++ b/src/netcode/gamestate.c @@ -43,9 +43,7 @@ boolean cl_redownloadinggamestate = false; boolean SV_ResendingSavegameToAnyone(void) { - INT32 i; - - for (i = 0; i < MAXNETNODES; i++) + for (INT32 i = 0; i < MAXNETNODES; i++) if (netnodes[i].resendingsavegame) return true; return false; @@ -236,9 +234,7 @@ void CL_LoadReceivedSavegame(boolean reloading) void CL_ReloadReceivedSavegame(void) { - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { LUA_InvalidatePlayer(&players[i]); sprintf(player_names[i], "Player %d", i + 1); diff --git a/src/netcode/i_tcp.c b/src/netcode/i_tcp.c index 6baba6275..bd950c355 100644 --- a/src/netcode/i_tcp.c +++ b/src/netcode/i_tcp.c @@ -416,24 +416,20 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) */ static void cleanupnodes(void) { - SINT8 j; - if (!Playing()) return; // Why can't I start at zero? - for (j = 1; j < MAXNETNODES; j++) + for (SINT8 j = 1; j < MAXNETNODES; j++) if (!(netnodes[j].ingame || SendingFile(j))) nodeconnected[j] = false; } static SINT8 getfreenode(void) { - SINT8 j; - cleanupnodes(); - for (j = 0; j < MAXNETNODES; j++) + for (SINT8 j = 0; j < MAXNETNODES; j++) if (!nodeconnected[j]) { nodeconnected[j] = true; @@ -446,7 +442,7 @@ static SINT8 getfreenode(void) * downloading a needed wad, but it's better than not letting anyone join... */ /*I_Error("No more free nodes!!1!11!11!!1111\n"); - for (j = 1; j < MAXNETNODES; j++) + for (SINT8 j = 1; j < MAXNETNODES; j++) if (!netnodes[j].ingame) return j;*/ @@ -458,9 +454,8 @@ void Command_Numnodes(void) { INT32 connected = 0; INT32 ingame = 0; - INT32 i; - for (i = 1; i < MAXNETNODES; i++) + for (INT32 i = 1; i < MAXNETNODES; i++) { if (!(nodeconnected[i] || netnodes[i].ingame)) continue; @@ -496,13 +491,13 @@ void Command_Numnodes(void) // Returns true if a packet was received from a new node, false in all other cases static boolean SOCK_Get(void) { - size_t i, n; + size_t i; int j; ssize_t c; mysockaddr_t fromaddress; socklen_t fromlen; - for (n = 0; n < mysocketses; n++) + for (size_t n = 0; n < mysocketses; n++) { fromlen = (socklen_t)sizeof(fromaddress); c = recvfrom(mysockets[n], (char *)&doomcom->data, MAXPACKETLENGTH, 0, @@ -563,10 +558,9 @@ static fd_set masterset; #ifdef SELECTTEST static boolean FD_CPY(fd_set *src, fd_set *dst, SOCKET_TYPE *fd, size_t len) { - size_t i; boolean testset = false; FD_ZERO(dst); - for (i = 0; i < len;i++) + for (size_t i = 0; i < len;i++) { if(fd[i] != (SOCKET_TYPE)ERRSOCKET && FD_ISSET(fd[i], src) && !FD_ISSET(fd[i], dst)) // no checking for dups @@ -630,16 +624,15 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr static void SOCK_Send(void) { ssize_t c = ERRSOCKET; - size_t i, j; if (!nodeconnected[doomcom->remotenode]) return; if (doomcom->remotenode == BROADCASTADDR) { - for (i = 0; i < mysocketses; i++) + for (size_t i = 0; i < mysocketses; i++) { - for (j = 0; j < broadcastaddresses; j++) + for (size_t j = 0; j < broadcastaddresses; j++) { if (myfamily[i] == broadcastaddress[j].any.sa_family) SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]); @@ -649,7 +642,7 @@ static void SOCK_Send(void) } else if (nodesocket[doomcom->remotenode] == (SOCKET_TYPE)ERRSOCKET) { - for (i = 0; i < mysocketses; i++) + for (size_t i = 0; i < mysocketses; i++) { if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family) SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]); @@ -1082,8 +1075,7 @@ boolean I_InitTcpDriver(void) static void SOCK_CloseSocket(void) { - size_t i; - for (i=0; i < MAXNETNODES+1; i++) + for (size_t i=0; i < MAXNETNODES+1; i++) { if (mysockets[i] != (SOCKET_TYPE)ERRSOCKET && FD_ISSET(mysockets[i], &masterset)) @@ -1154,12 +1146,10 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) static boolean SOCK_OpenSocket(void) { - size_t i; - memset(clientaddress, 0, sizeof (clientaddress)); nodeconnected[0] = true; // always connected to self - for (i = 1; i < MAXNETNODES; i++) + for (size_t i = 1; i < MAXNETNODES; i++) nodeconnected[i] = false; nodeconnected[BROADCASTADDR] = true; I_NetSend = SOCK_Send; diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 11b351af7..887b7fa2a 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -90,13 +90,11 @@ void D_FreeTextcmd(tic_t tic) if (textcmdtic) { - INT32 i; - // Remove this tic from the list. *tctprev = textcmdtic->next; // Free all players. - for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + for (INT32 i = 0; i < TEXTCMD_HASH_SIZE; i++) { textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i]; @@ -174,9 +172,7 @@ UINT8* D_GetTextcmd(tic_t tic, INT32 playernum) void ExtraDataTicker(void) { - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) if (playeringame[i] || i == 0) { UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i); @@ -221,10 +217,9 @@ void ExtraDataTicker(void) // used at txtcmds received to check packetsize bound size_t TotalTextCmdPerTic(tic_t tic) { - INT32 i; size_t total = 1; // num of textcmds in the tic (ntextcmd byte) - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { UINT8 *textcmd = D_GetExistingTextcmd(tic, i); if ((!i || playeringame[i]) && textcmd) diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c index ed0e28309..f8ec3c7bd 100644 --- a/src/netcode/server_connection.c +++ b/src/netcode/server_connection.c @@ -55,7 +55,6 @@ static INT32 FindRejoinerNum(SINT8 node) char strippednodeaddress[64]; const char *nodeaddress; char *port; - INT32 i; // Make sure there is no dead dress before proceeding to the stripping if (!I_GetNodeAddress) @@ -71,7 +70,7 @@ static INT32 FindRejoinerNum(SINT8 node) *port = '\0'; // Check if any player matches the stripped address - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX && !strcmp(playeraddress[i], strippednodeaddress)) @@ -162,10 +161,9 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) static void SV_SendPlayerInfo(INT32 node) { - UINT8 i; netbuffer->packettype = PT_PLAYERINFO; - for (i = 0; i < MAXPLAYERS; i++) + for (UINT8 i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) { @@ -425,7 +423,6 @@ void PT_ClientJoin(SINT8 node) char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; INT32 numplayers = netbuffer->u.clientcfg.localplayers; INT32 rejoinernum; - INT32 i; // Ignore duplicate packets if (client || netnodes[node].ingame) @@ -440,7 +437,7 @@ void PT_ClientJoin(SINT8 node) return; } - for (i = 0; i < numplayers; i++) + for (INT32 i = 0; i < numplayers; i++) { strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) @@ -469,7 +466,7 @@ void PT_ClientJoin(SINT8 node) } // Splitscreen can allow 2 players in one node - for (i = 0; i < numplayers; i++) + for (INT32 i = 0; i < numplayers; i++) SV_AddPlayer(node, names[i]); joindelay += cv_joindelay.value * TICRATE; diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index bf8c5d21d..befe8a5cd 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -80,11 +80,9 @@ tic_t ExpandTics(INT32 low, INT32 node) void D_Clearticcmd(tic_t tic) { - INT32 i; - D_FreeTextcmd(tic); - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) netcmds[tic%BACKUPTICS][i].angleturn = 0; DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); @@ -92,13 +90,11 @@ void D_Clearticcmd(tic_t tic) void D_ResetTiccmds(void) { - INT32 i; - memset(&localcmds, 0, sizeof(ticcmd_t)); memset(&localcmds2, 0, sizeof(ticcmd_t)); // Reset the net command list - for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + for (INT32 i = 0; i < TEXTCMD_HASH_SIZE; i++) while (textcmds[i]) D_Clearticcmd(textcmds[i]->tic); } @@ -229,8 +225,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole) realstart = netbuffer->u.serverpak.starttic; realend = realstart + netbuffer->u.serverpak.numtics; - if (realend > gametic + CLIENTBACKUPTICS) - realend = gametic + CLIENTBACKUPTICS; + realend = min(realend, gametic + CLIENTBACKUPTICS); cl_packetmissed = realstart > neededtic; if (realstart <= neededtic && realend > neededtic) @@ -304,9 +299,8 @@ void CL_SendClientCmd(void) static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t lasttic) { size_t size = BASESERVERTICSSIZE; - tic_t tic; - for (tic = firsttic; tic < lasttic; tic++) + for (tic_t tic = firsttic; tic < lasttic; tic++) { size += sizeof (ticcmd_t) * doomcom->numslots; size += TotalTextCmdPerTic(tic); @@ -344,15 +338,12 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l // send tic from firstticstosend to maketic-1 void SV_SendTics(void) { - tic_t realfirsttic, lasttictosend, i; - UINT32 n; - size_t packsize; - UINT8 *bufpos; + tic_t realfirsttic, lasttictosend; // send to all client but not to me // for each node create a packet with x tics and send it // x is computed using netnodes[n].supposedtic, max packet size and maketic - for (n = 1; n < MAXNETNODES; n++) + for (INT32 n = 1; n < MAXNETNODES; n++) if (netnodes[n].ingame) { // assert netnodes[n].supposedtic>=netnodes[n].tic @@ -367,42 +358,42 @@ void SV_SendTics(void) // (getpacket servertics case) DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", n, maketic, netnodes[n].supposedtic, netnodes[n].tic)); + realfirsttic = netnodes[n].tic; + if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) // all tic are ok continue; + DEBFILE(va("Sent %d anyway\n", realfirsttic)); } - if (realfirsttic < firstticstosend) - realfirsttic = firstticstosend; + realfirsttic = max(realfirsttic, firstticstosend); lasttictosend = realfirsttic + SV_CalculateNumTicsForPacket(n, realfirsttic, lasttictosend); - // Send the tics + // Prepare the packet header netbuffer->packettype = PT_SERVERTICS; netbuffer->u.serverpak.starttic = realfirsttic; netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); - bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; - for (i = realfirsttic; i < lasttictosend; i++) - { + // Fill and send the packet + UINT8 *bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; + for (tic_t i = realfirsttic; i < lasttictosend; i++) bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); - } - - // add textcmds - for (i = realfirsttic; i < lasttictosend; i++) - SV_WriteNetCommandsForTic(); - packsize = bufpos - (UINT8 *)&(netbuffer->u); - + for (tic_t i = realfirsttic; i < lasttictosend; i++) + SV_WriteNetCommandsForTic(i, &bufpos); + size_t packsize = bufpos - (UINT8 *)&(netbuffer->u); HSendPacket(n, false, 0, packsize); + // when tic are too large, only one tic is sent so don't go backward! if (lasttictosend-doomcom->extratics > realfirsttic) netnodes[n].supposedtic = lasttictosend-doomcom->extratics; else netnodes[n].supposedtic = lasttictosend; - if (netnodes[n].supposedtic < netnodes[n].tic) netnodes[n].supposedtic = netnodes[n].tic; + netnodes[n].supposedtic = max(netnodes[n].supposedtic, netnodes[n].tic); } + // node 0 is me! netnodes[0].supposedtic = maketic; } @@ -413,7 +404,8 @@ void Local_Maketic(INT32 realtics) D_ProcessEvents(); // menu responder, cons responder, // game responder calls HU_Responder, AM_Responder, // and G_MapEventsToControls - if (!dedicated) rendergametic = gametic; + if (!dedicated) + rendergametic = gametic; // translate inputs (keyboard/mouse/gamepad) into game controls G_BuildTiccmd(&localcmds, realtics, 1); if (splitscreen || botingame) @@ -426,9 +418,7 @@ void Local_Maketic(INT32 realtics) // create missed tic void SV_Maketic(void) { - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) + for (INT32 i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i]) continue; From 425fce69c97dc61b1709ceaff03d984561a6c733 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sat, 14 Jan 2023 20:02:20 +0100 Subject: [PATCH 59/89] Add missing include directive --- src/netcode/net_command.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c index 887b7fa2a..efc8bd0ef 100644 --- a/src/netcode/net_command.c +++ b/src/netcode/net_command.c @@ -16,6 +16,7 @@ #include "server_connection.h" #include "d_clisrv.h" #include "i_net.h" +#include "../byteptr.h" #include "../g_game.h" #include "../z_zone.h" #include "../doomtype.h" From d1ba9e0afd0ea7c02a0566cd30e730d65c38b0d8 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 15 Jan 2023 13:08:31 +0100 Subject: [PATCH 60/89] Cleanup --- src/netcode/gamestate.c | 3 +-- src/netcode/tic_command.c | 43 +++++++++++++++++++++------------------ src/netcode/tic_command.h | 2 +- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c index d284164c0..c1ceb95b5 100644 --- a/src/netcode/gamestate.c +++ b/src/netcode/gamestate.c @@ -242,8 +242,7 @@ void CL_ReloadReceivedSavegame(void) CL_LoadReceivedSavegame(true); - if (neededtic < gametic) - neededtic = gametic; + neededtic = max(neededtic, gametic); maketic = neededtic; ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index befe8a5cd..0fbf87c0c 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -148,8 +148,9 @@ static void CheckConsistancy(SINT8 nodenum, tic_t tic) } } -void PT_ClientCmd(SINT8 node, INT32 netconsole) +void PT_ClientCmd(SINT8 nodenum, INT32 netconsole) { + netnode_t *node = &netnodes[nodenum]; tic_t realend, realstart; if (client) @@ -157,24 +158,24 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole) // To save bytes, only the low byte of tic numbers are sent // Use ExpandTics to figure out what the rest of the bytes are - realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); + realstart = ExpandTics(netbuffer->u.clientpak.client_tic, nodenum); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom, nodenum); if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS || netbuffer->packettype == PT_NODEKEEPALIVEMIS - || netnodes[node].supposedtic < realend) + || node->supposedtic < realend) { - netnodes[node].supposedtic = realend; + node->supposedtic = realend; } // Discard out of order packet - if (netnodes[node].tic > realend) + if (node->tic > realend) { - DEBFILE(va("out of order ticcmd discarded nettics = %u\n", netnodes[node].tic)); + DEBFILE(va("out of order ticcmd discarded nettics = %u\n", node->tic)); return; } // Update the nettics - netnodes[node].tic = realend; + node->tic = realend; // Don't do anything for packets of type NODEKEEPALIVE? if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE @@ -183,19 +184,19 @@ void PT_ClientCmd(SINT8 node, INT32 netconsole) // As long as clients send valid ticcmds, the server can keep running, so reset the timeout /// \todo Use a separate cvar for that kind of timeout? - netnodes[node].freezetimeout = I_GetTime() + connectiontimeout; + node->freezetimeout = I_GetTime() + connectiontimeout; // Copy ticcmd G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); // Splitscreen cmd if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - && netnodes[node].player2 >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)netnodes[node].player2], + && node->player2 >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)node->player2], &netbuffer->u.client2pak.cmd2, 1); CheckTiccmdHacks(netconsole); - CheckConsistancy(node, realstart); + CheckConsistancy(nodenum, realstart); } void PT_ServerTics(SINT8 node, INT32 netconsole) @@ -346,9 +347,11 @@ void SV_SendTics(void) for (INT32 n = 1; n < MAXNETNODES; n++) if (netnodes[n].ingame) { - // assert netnodes[n].supposedtic>=netnodes[n].tic - realfirsttic = netnodes[n].supposedtic; - lasttictosend = min(maketic, netnodes[n].tic + CLIENTBACKUPTICS); + netnode_t *node = netnodes[n]; + + // assert node->supposedtic>=node->tic + realfirsttic = node->supposedtic; + lasttictosend = min(maketic, node->tic + CLIENTBACKUPTICS); if (realfirsttic >= lasttictosend) { @@ -357,9 +360,9 @@ void SV_SendTics(void) // packet detection work when we have received packet with firsttic > neededtic // (getpacket servertics case) DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", - n, maketic, netnodes[n].supposedtic, netnodes[n].tic)); + n, maketic, node->supposedtic, node->tic)); - realfirsttic = netnodes[n].tic; + realfirsttic = node->tic; if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) // all tic are ok @@ -388,10 +391,10 @@ void SV_SendTics(void) // when tic are too large, only one tic is sent so don't go backward! if (lasttictosend-doomcom->extratics > realfirsttic) - netnodes[n].supposedtic = lasttictosend-doomcom->extratics; + node->supposedtic = lasttictosend-doomcom->extratics; else - netnodes[n].supposedtic = lasttictosend; - netnodes[n].supposedtic = max(netnodes[n].supposedtic, netnodes[n].tic); + node->supposedtic = lasttictosend; + node->supposedtic = max(node->supposedtic, node->tic); } // node 0 is me! diff --git a/src/netcode/tic_command.h b/src/netcode/tic_command.h index d19f4c1aa..289750fb3 100644 --- a/src/netcode/tic_command.h +++ b/src/netcode/tic_command.h @@ -31,7 +31,7 @@ tic_t ExpandTics(INT32 low, INT32 node); void D_Clearticcmd(tic_t tic); void D_ResetTiccmds(void); -void PT_ClientCmd(SINT8 node, INT32 netconsole); +void PT_ClientCmd(SINT8 nodenum, INT32 netconsole); void PT_ServerTics(SINT8 node, INT32 netconsole); // send the client packet to the server From fe304ae2db6b81c630e0dae82463d03ea98f4c48 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 15 Jan 2023 13:10:23 +0100 Subject: [PATCH 61/89] Cleanup comments --- src/netcode/client_connection.c | 41 ++++++++++++++------------------- src/netcode/d_clisrv.c | 11 ++++----- src/netcode/protocol.h | 3 +-- src/netcode/tic_command.c | 35 ++++++++++++++-------------- 4 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c index faa91b6d7..d363d7d5a 100644 --- a/src/netcode/client_connection.c +++ b/src/netcode/client_connection.c @@ -34,7 +34,7 @@ cl_mode_t cl_mode = CL_SEARCHING; static UINT16 cl_lastcheckedfilecount = 0; // used for full file list -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 +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; UINT8 mynode; static void *snake = NULL; @@ -137,7 +137,7 @@ static inline void CL_DrawConnectionStatus(void) V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); - //ima just count files here + // ima just count files here if (fileneeded) { for (INT32 i = 0; i < fileneedednum; i++) @@ -248,7 +248,7 @@ boolean CL_SendJoin(void) CleanupPlayerName(consoleplayer, cv_playername.zstring); if (splitscreen) - CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */ + CleanupPlayerName(1, cv_playername2.zstring); // 1 is a HACK? oh no strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); @@ -306,14 +306,14 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) if (serverlistcount >= MAXSERVERLIST) return; // list full - /* check it later if connecting to this one */ + // check it later if connecting to this one if (node != servernode) { if (info->_255 != 255) - return;/* old packet format */ + return; // Old packet format if (info->packetversion != PACKETVERSION) - return;/* old new packet format */ + return; // Old new packet format if (info->version != VERSION) return; // Not same version. @@ -322,7 +322,7 @@ static void SL_InsertServer(serverinfo_pak* info, SINT8 node) return; // Close, but no cigar. if (strcmp(info->application, SRB2APPLICATION)) - return;/* that's a different mod */ + return; // That's a different mod } i = serverlistcount++; @@ -380,7 +380,7 @@ Fetch_servers_thread (struct Fetch_servers_ctx *ctx) free(ctx); } -#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ +#endif // defined (MASTERSERVER) && defined (HAVE_THREADS) void CL_QueryServerList (msg_server_t *server_list) { @@ -389,7 +389,7 @@ void CL_QueryServerList (msg_server_t *server_list) // Make sure MS version matches our own, to // thwart nefarious servers who lie to the MS. - /* lol bruh, that version COMES from the servers */ + // lol bruh, that version COMES from the servers //if (strcmp(version, server_list[i].version) == 0) { INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); @@ -441,7 +441,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) ctx = malloc(sizeof *ctx); - /* This called from M_Refresh so I don't use a mutex */ + // This called from M_Refresh so I don't use a mutex m_waiting_mode = M_WAITING_SERVERS; I_lock_mutex(&ms_QueryId_mutex); @@ -465,7 +465,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) } #endif } -#endif/*MASTERSERVER*/ +#endif // MASTERSERVER } static void M_ConfirmConnect(event_t *ev) @@ -619,7 +619,7 @@ static const char * InvalidServerReason (serverinfo_pak *info) { #define EOT "\nPress ESC\n" - /* magic number for new packet format */ + // Magic number for new packet format if (info->_255 != 255) { return @@ -691,10 +691,10 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) { INT32 i; - // serverlist is updated by GetPacket function + // serverlist is updated by GetPackets if (serverlistcount > 0) { - // this can be a responce to our broadcast request + // This can be a response to our broadcast request if (servernode == -1 || servernode >= MAXNETNODES) { i = 0; @@ -819,7 +819,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic if (CL_LoadServerFiles()) { FreeFileNeeded(); - *asksent = 0; //This ensure the first join ask is right away + *asksent = 0; // This ensures the first join request is right away firstconnectattempttime = I_GetTime(); cl_mode = CL_ASKJOIN; } @@ -841,9 +841,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic 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... + // Prepare structures to save the file CL_PrepareDownloadSaveGame(tmpsave); if (I_GetTime() >= *asksent && CL_SendJoin()) @@ -954,11 +952,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic #define TMPSAVENAME "$$$.sav" -/** Use adaptive send using net_bandwidth and stat.sendbytes - * - * \todo Better description... - * - */ void CL_ConnectToServer(void) { INT32 pnumnodes, nodewaited = doomcom->numnodes, i; @@ -1158,7 +1151,7 @@ void PT_ServerCFG(SINT8 node) DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? - /// Shouldn't them be downloaded even at intermission time? + /// Shouldn't they be downloaded even at intermission time? /// Also, according to PT_ClientJoin, the server will send the savegame even during intermission... if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c index 105526dd2..18eae580c 100644 --- a/src/netcode/d_clisrv.c +++ b/src/netcode/d_clisrv.c @@ -1252,8 +1252,8 @@ void NetUpdate(void) GetPackets(); // get packet from client or from server - // client send the command after a receive of the server - // the server send before because in single player is beter + // The client sends the command after receiving from the server + // The server sends it before because this is better in single player #ifdef MASTERSERVER MasterClient_Ticker(); // Acking the Master Server @@ -1402,7 +1402,7 @@ SINT8 nametonum(const char *name) return -1; } -// is there a game running +// Is there a game running? boolean Playing(void) { return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); @@ -1423,11 +1423,8 @@ INT32 D_NumPlayers(void) } // -// NetUpdate -// Builds ticcmds for console player, -// sends out a packet +// Consistancy // -// no more use random generator, because at very first tic isn't yet synchronized // Note: It is called consistAncy on purpose. // INT16 Consistancy(void) diff --git a/src/netcode/protocol.h b/src/netcode/protocol.h index 374cc5bff..9866e4c5a 100644 --- a/src/netcode/protocol.h +++ b/src/netcode/protocol.h @@ -123,13 +123,12 @@ typedef struct #endif // Server to client packet -// this packet is too large typedef struct { tic_t starttic; UINT8 numtics; UINT8 numslots; // "Slots filled": Highest player number in use plus one. - ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large + ticcmd_t cmds[45]; } ATTRPACK servertics_pak; typedef struct diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c index 0fbf87c0c..620a10f7a 100644 --- a/src/netcode/tic_command.c +++ b/src/netcode/tic_command.c @@ -24,8 +24,8 @@ #include "../doomstat.h" #include "../doomtype.h" -tic_t firstticstosend; // min of the nettics -tic_t tictoclear = 0; // optimize d_clearticcmd +tic_t firstticstosend; // Smallest netnode.tic +tic_t tictoclear = 0; // Optimize D_ClearTiccmd ticcmd_t localcmds; ticcmd_t localcmds2; boolean cl_packetmissed; @@ -312,10 +312,9 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l sizeu1(size), tic, firsttic, lasttic)); lasttic = tic; - // too bad: too much player have send extradata and there is too - // much data in one tic. - // To avoid it put the data on the next tic. (see getpacket - // textcmd case) but when numplayer changes the computation can be different + // Too bad: too many players have sent extra data + // and there is too much data for a single tic. + // To avoid that, keep the data for the next tic (see PT_TEXTCMD). if (lasttic == firsttic) { if (size > MAXPACKETLENGTH) @@ -335,15 +334,15 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l return lasttic - firsttic; } -// send the server packet -// send tic from firstticstosend to maketic-1 +// Sends the server packet +// Sends tic/net commands from firstticstosend to maketic-1 void SV_SendTics(void) { tic_t realfirsttic, lasttictosend; - // send to all client but not to me - // for each node create a packet with x tics and send it - // x is computed using netnodes[n].supposedtic, max packet size and maketic + // Send to all clients except yourself + // For each node, create a packet with x tics and send it + // x is computed using node.supposedtic, max packet size and maketic for (INT32 n = 1; n < MAXNETNODES; n++) if (netnodes[n].ingame) { @@ -355,17 +354,17 @@ void SV_SendTics(void) if (realfirsttic >= lasttictosend) { - // well we have sent all tics we will so use extrabandwidth - // to resent packet that are supposed lost (this is necessary since lost - // packet detection work when we have received packet with firsttic > neededtic - // (getpacket servertics case) + // Well, we have sent all the tics, so we will use extra bandwidth + // to resend packets that are supposed lost. + // This is necessary since lost packet detection + // works when we receive a packet with firsttic > neededtic (PT_SERVERTICS) DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", n, maketic, node->supposedtic, node->tic)); realfirsttic = node->tic; if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) - // all tic are ok + // All tics are Ok continue; DEBFILE(va("Sent %d anyway\n", realfirsttic)); @@ -389,7 +388,7 @@ void SV_SendTics(void) size_t packsize = bufpos - (UINT8 *)&(netbuffer->u); HSendPacket(n, false, 0, packsize); - // when tic are too large, only one tic is sent so don't go backward! + // When tics are too large, only one tic is sent so don't go backwards! if (lasttictosend-doomcom->extratics > realfirsttic) node->supposedtic = lasttictosend-doomcom->extratics; else @@ -450,6 +449,6 @@ void SV_Maketic(void) } } - // all tic are now proceed make the next + // All tics have been processed, make the next maketic++; } From 379cc4207a6c8df5c7ffe77ceb3b86828735d729 Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Sun, 15 Jan 2023 17:57:23 +0000 Subject: [PATCH 62/89] Revert "Merge branch 'netcode-refactor' into merging" This reverts merge request !1920 --- src/Makefile | 2 +- src/Makefile.d/features.mk | 4 +- src/Makefile.d/win32.mk | 2 + src/Sourcefile | 8 +- src/android/i_net.c | 2 +- src/blua/liolib.c | 3 +- src/command.c | 5 +- src/d_clisrv.c | 5590 ++++++++++++++++++++++++ src/{netcode/protocol.h => d_clisrv.h} | 185 +- src/d_main.c | 6 +- src/{netcode => }/d_net.c | 152 +- src/{netcode => }/d_net.h | 27 +- src/{netcode => }/d_netcmd.c | 76 +- src/{netcode => }/d_netcmd.h | 2 +- src/{netcode => }/d_netfil.c | 168 +- src/{netcode => }/d_netfil.h | 14 +- src/deh_soc.c | 2 +- src/deh_soc.h | 2 +- src/deh_tables.c | 2 +- src/doomdef.h | 3 +- src/doomstat.h | 2 +- src/dummy/i_net.c | 2 +- src/f_finale.c | 2 +- src/filesrch.c | 2 +- src/filesrch.h | 2 +- src/g_demo.c | 2 +- src/g_game.c | 3 +- src/g_input.c | 2 +- src/hardware/hw_main.c | 6 +- src/{netcode => }/http-mserv.c | 11 +- src/hu_stuff.c | 35 +- src/{netcode => }/i_addrinfo.c | 0 src/{netcode => }/i_addrinfo.h | 0 src/{netcode => }/i_net.h | 4 +- src/{netcode => }/i_tcp.c | 294 +- src/{netcode => }/i_tcp.h | 0 src/i_time.c | 2 +- src/lua_baselib.c | 4 +- src/lua_consolelib.c | 1 - src/lua_hooklib.c | 2 +- src/lua_script.c | 2 +- src/m_cheat.c | 2 +- src/m_menu.c | 63 +- src/m_menu.h | 4 +- src/m_perfstats.c | 2 +- src/{netcode => }/mserv.c | 19 +- src/{netcode => }/mserv.h | 2 +- src/netcode/Sourcefile | 13 - src/netcode/client_connection.c | 1161 ----- src/netcode/client_connection.h | 61 - src/netcode/commands.c | 484 -- src/netcode/commands.h | 33 - src/netcode/d_clisrv.c | 1564 ------- src/netcode/d_clisrv.h | 131 - src/netcode/gamestate.c | 336 -- src/netcode/gamestate.h | 31 - src/netcode/net_command.c | 386 -- src/netcode/net_command.h | 66 - src/netcode/server_connection.c | 507 --- src/netcode/server_connection.h | 30 - src/netcode/tic_command.c | 454 -- src/netcode/tic_command.h | 44 - src/p_ceilng.c | 2 +- src/p_haptic.c | 2 +- src/p_inter.c | 1 - src/p_lights.c | 2 +- src/p_mobj.c | 1 - src/p_setup.c | 2 - src/p_tick.c | 2 - src/p_user.c | 3 +- src/r_segs.c | 2 +- src/r_things.c | 2 +- src/screen.c | 2 +- src/sdl/i_gamepad.c | 2 +- src/sdl/i_net.c | 6 +- src/sdl/i_system.c | 9 +- src/sdl/i_ttf.c | 2 +- src/snake.c | 535 --- src/snake.h | 20 - src/w_wad.c | 4 +- src/y_inter.c | 2 +- 81 files changed, 6349 insertions(+), 6278 deletions(-) create mode 100755 src/d_clisrv.c rename src/{netcode/protocol.h => d_clisrv.h} (60%) rename src/{netcode => }/d_net.c (93%) rename src/{netcode => }/d_net.h (74%) rename src/{netcode => }/d_netcmd.c (99%) rename src/{netcode => }/d_netcmd.h (99%) rename src/{netcode => }/d_netfil.c (93%) rename src/{netcode => }/d_netfil.h (94%) rename src/{netcode => }/http-mserv.c (98%) rename src/{netcode => }/i_addrinfo.c (100%) rename src/{netcode => }/i_addrinfo.h (100%) rename src/{netcode => }/i_net.h (98%) rename src/{netcode => }/i_tcp.c (88%) rename src/{netcode => }/i_tcp.h (100%) rename src/{netcode => }/mserv.c (97%) rename src/{netcode => }/mserv.h (99%) delete mode 100644 src/netcode/Sourcefile delete mode 100644 src/netcode/client_connection.c delete mode 100644 src/netcode/client_connection.h delete mode 100644 src/netcode/commands.c delete mode 100644 src/netcode/commands.h delete mode 100644 src/netcode/d_clisrv.c delete mode 100644 src/netcode/d_clisrv.h delete mode 100644 src/netcode/gamestate.c delete mode 100644 src/netcode/gamestate.h delete mode 100644 src/netcode/net_command.c delete mode 100644 src/netcode/net_command.h delete mode 100644 src/netcode/server_connection.c delete mode 100644 src/netcode/server_connection.h delete mode 100644 src/netcode/tic_command.c delete mode 100644 src/netcode/tic_command.h delete mode 100644 src/snake.c delete mode 100644 src/snake.h diff --git a/src/Makefile b/src/Makefile index 750e8ead5..7571c8089 100644 --- a/src/Makefile +++ b/src/Makefile @@ -64,6 +64,7 @@ # # Netplay incompatible # -------------------- +# NONET=1 - Disable online capability. # NOMD5=1 - Disable MD5 checksum (validation tool). # NOPOSTPROCESSING=1 - ? # MOBJCONSISTANCY=1 - ?? @@ -207,7 +208,6 @@ objdir:=$(makedir)/objs sources+=\ $(call List,Sourcefile)\ $(call List,blua/Sourcefile)\ - $(call List,netcode/Sourcefile)\ depends:=$(basename $(filter %.c %.s,$(sources))) objects:=$(basename $(filter %.c %.s %.nas,$(sources))) diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk index 9acbb4d86..8ba33383b 100644 --- a/src/Makefile.d/features.mk +++ b/src/Makefile.d/features.mk @@ -3,7 +3,7 @@ # passthru_opts+=\ - NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ + NONET NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ MOBJCONSISTANCY PACKETDROP ZDEBUG\ HAVE_MINIUPNPC\ @@ -46,11 +46,13 @@ sources+=apng.c endif endif +ifndef NONET ifndef NOCURL CURLCONFIG?=curl-config $(eval $(call Configure,CURL,$(CURLCONFIG))) opts+=-DHAVE_CURL endif +endif ifdef HAVE_MINIUPNPC libs+=-lminiupnpc diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk index e7269e1e7..0e48ed683 100644 --- a/src/Makefile.d/win32.mk +++ b/src/Makefile.d/win32.mk @@ -35,10 +35,12 @@ libs+=-lws2_32 endif endif +ifndef NONET ifndef MINGW64 # miniupnc is broken with MINGW64 opts+=-I../libs -DSTATIC_MINIUPNPC libs+=-L../libs/miniupnpc/mingw$(32) -lws2_32 -liphlpapi endif +endif ifndef MINGW64 32=32 diff --git a/src/Sourcefile b/src/Sourcefile index 7d2146411..9de90eee4 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -1,5 +1,9 @@ string.c d_main.c +d_clisrv.c +d_net.c +d_netfil.c +d_netcmd.c dehacked.c deh_soc.c deh_lua.c @@ -73,10 +77,12 @@ s_sound.c sounds.c w_wad.c filesrch.c +mserv.c +http-mserv.c +i_tcp.c lzf.c vid_copy.s b_bot.c -snake.c lua_script.c lua_baselib.c lua_mathlib.c diff --git a/src/android/i_net.c b/src/android/i_net.c index 4c30dc767..f6e642022 100644 --- a/src/android/i_net.c +++ b/src/android/i_net.c @@ -1,4 +1,4 @@ -#include "../netcode/i_net.h" +#include "../i_net.h" boolean I_InitNetwork(void) { diff --git a/src/blua/liolib.c b/src/blua/liolib.c index bce8e87cc..e029650c0 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -19,8 +19,7 @@ #include "lualib.h" #include "../i_system.h" #include "../g_game.h" -#include "../netcode/d_netfil.h" -#include "../netcode/net_command.h" +#include "../d_netfil.h" #include "../lua_libs.h" #include "../byteptr.h" #include "../lua_script.h" diff --git a/src/command.c b/src/command.c index 58d69e24d..d1db9f9c9 100644 --- a/src/command.c +++ b/src/command.c @@ -28,12 +28,11 @@ #include "byteptr.h" #include "p_saveg.h" #include "g_game.h" // for player_names -#include "netcode/d_netcmd.h" -#include "netcode/net_command.h" +#include "d_netcmd.h" #include "hu_stuff.h" #include "p_setup.h" #include "lua_script.h" -#include "netcode/d_netfil.h" // findfile +#include "d_netfil.h" // findfile #include "r_data.h" // Color_cons_t //======== diff --git a/src/d_clisrv.c b/src/d_clisrv.c new file mode 100755 index 000000000..3091f3344 --- /dev/null +++ b/src/d_clisrv.c @@ -0,0 +1,5590 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 d_clisrv.c +/// \brief SRB2 Network game communication and protocol, all OS independent parts. + +#include +#ifdef __GNUC__ +#include //for unlink +#endif + +#include "i_time.h" +#include "i_net.h" +#include "i_system.h" +#include "i_video.h" +#include "d_net.h" +#include "d_main.h" +#include "g_game.h" +#include "st_stuff.h" +#include "hu_stuff.h" +#include "keys.h" +#include "g_input.h" +#include "i_gamepad.h" +#include "m_menu.h" +#include "console.h" +#include "d_netfil.h" +#include "byteptr.h" +#include "p_saveg.h" +#include "z_zone.h" +#include "p_local.h" +#include "p_haptic.h" +#include "m_misc.h" +#include "am_map.h" +#include "m_random.h" +#include "mserv.h" +#include "y_inter.h" +#include "r_local.h" +#include "m_argv.h" +#include "p_setup.h" +#include "lzf.h" +#include "lua_script.h" +#include "lua_hook.h" +#include "lua_libs.h" +#include "md5.h" +#include "m_perfstats.h" + +// aaaaaa +#include "i_gamepad.h" + +#ifndef NONET +// cl loading screen +#include "v_video.h" +#include "f_finale.h" +#endif + +// +// NETWORKING +// +// gametic is the tic about to (or currently being) run +// Server: +// maketic is the tic that hasn't had control made for it yet +// nettics is the tic for each node +// firstticstosend is the lowest value of nettics +// Client: +// neededtic is the tic needed by the client to run the game +// firstticstosend is used to optimize a condition +// Normally maketic >= gametic > 0 + +#define PREDICTIONQUEUE BACKUPTICS +#define PREDICTIONMASK (PREDICTIONQUEUE-1) +#define MAX_REASONLENGTH 30 + +boolean server = true; // true or false but !server == client +#define client (!server) +boolean nodownload = false; +boolean serverrunning = false; +INT32 serverplayer = 0; +char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) + +// Server specific vars +UINT8 playernode[MAXPLAYERS]; +char playeraddress[MAXPLAYERS][64]; + +// Minimum timeout for sending the savegame +// The actual timeout will be longer depending on the savegame length +tic_t jointimeout = (10*TICRATE); +static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? +static boolean resendingsavegame[MAXNETNODES]; // Are we resending the savegame? +static tic_t savegameresendcooldown[MAXNETNODES]; // How long before we can resend again? +static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? + +// Incremented by cv_joindelay when a client joins, decremented each tic. +// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. +static tic_t joindelay = 0; + +UINT16 pingmeasurecount = 1; +UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. +UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. +SINT8 nodetoplayer[MAXNETNODES]; +SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) +UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen +boolean nodeingame[MAXNETNODES]; // set false as nodes leave game +tic_t servermaxping = 800; // server's max ping. Defaults to 800 +static tic_t nettics[MAXNETNODES]; // what tic the client have received +static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet +static UINT8 nodewaiting[MAXNETNODES]; +static tic_t firstticstosend; // min of the nettics +static tic_t tictoclear = 0; // optimize d_clearticcmd +static tic_t maketic; + +static INT16 consistancy[BACKUPTICS]; + +static UINT8 player_joining = false; +UINT8 hu_redownloadinggamestate = 0; + +// true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks +boolean hu_stopped = false; + +UINT8 adminpassmd5[16]; +boolean adminpasswordset = false; + +// Client specific +static ticcmd_t localcmds; +static ticcmd_t localcmds2; +static boolean cl_packetmissed; +// here it is for the secondary local player (splitscreen) +static UINT8 mynode; // my address pointofview server +static boolean cl_redownloadinggamestate = false; + +static UINT8 localtextcmd[MAXTEXTCMD]; +static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen +static tic_t neededtic; +SINT8 servernode = 0; // the number of the server node + +/// \brief do we accept new players? +/// \todo WORK! +boolean acceptnewnode = true; + +static 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 +static tic_t firstconnectattempttime = 0; + +// engine + +// Must be a power of two +#define TEXTCMD_HASH_SIZE 4 + +typedef struct textcmdplayer_s +{ + INT32 playernum; + UINT8 cmd[MAXTEXTCMD]; + struct textcmdplayer_s *next; +} textcmdplayer_t; + +typedef struct textcmdtic_s +{ + tic_t tic; + textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE]; + struct textcmdtic_s *next; +} textcmdtic_t; + +ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; +static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; + + +consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; +consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); + +static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) +{ + const size_t d = n / sizeof(ticcmd_t); + const size_t r = n % sizeof(ticcmd_t); + UINT8 *ret = dest; + + if (r) + M_Memcpy(dest, src, n); + else if (d) + G_MoveTiccmd(dest, src, d); + return ret+n; +} + +static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) +{ + const size_t d = n / sizeof(ticcmd_t); + const size_t r = n % sizeof(ticcmd_t); + UINT8 *ret = src; + + if (r) + M_Memcpy(dest, src, n); + else if (d) + G_MoveTiccmd(dest, src, d); + return ret+n; +} + + + +// Some software don't support largest packet +// (original sersetup, not exactely, but the probability of sending a packet +// of 512 bytes is like 0.1) +UINT16 software_MAXPACKETLENGTH; + +/** Guesses the full value of a tic from its lowest byte, for a specific node + * + * \param low The lowest byte of the tic value + * \param node The node to deduce the tic for + * \return The full tic value + * + */ +tic_t ExpandTics(INT32 low, INT32 node) +{ + INT32 delta; + + delta = low - (nettics[node] & UINT8_MAX); + + if (delta >= -64 && delta <= 64) + return (nettics[node] & ~UINT8_MAX) + low; + else if (delta > 64) + return (nettics[node] & ~UINT8_MAX) - 256 + low; + else //if (delta < -64) + return (nettics[node] & ~UINT8_MAX) + 256 + low; +} + +// ----------------------------------------------------------------- +// Some extra data function for handle textcmd buffer +// ----------------------------------------------------------------- + +static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum); + +void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) +{ +#ifdef PARANOIA + if (id >= MAXNETXCMD) + I_Error("Command id %d too big", id); + if (listnetxcmd[id] != 0) + I_Error("Command id %d already used", id); +#endif + listnetxcmd[id] = cmd_f; +} + +void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam) +{ + if (localtextcmd[0]+2+nparam > MAXTEXTCMD) + { + // for future reference: if (cv_debug) != debug disabled. + CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam)); + return; + } + localtextcmd[0]++; + localtextcmd[localtextcmd[0]] = (UINT8)id; + if (param && nparam) + { + M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam); + localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam); + } +} + +// splitscreen player +void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam) +{ + if (localtextcmd2[0]+2+nparam > MAXTEXTCMD) + { + I_Error("No more place in the buffer for netcmd %d\n",id); + return; + } + localtextcmd2[0]++; + localtextcmd2[localtextcmd2[0]] = (UINT8)id; + if (param && nparam) + { + M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam); + localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam); + } +} + +UINT8 GetFreeXCmdSize(void) +{ + // -1 for the size and another -1 for the ID. + return (UINT8)(localtextcmd[0] - 2); +} + +// Frees all textcmd memory for the specified tic +static void D_FreeTextcmd(tic_t tic) +{ + textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdtic_t *textcmdtic = *tctprev; + + while (textcmdtic && textcmdtic->tic != tic) + { + tctprev = &textcmdtic->next; + textcmdtic = textcmdtic->next; + } + + if (textcmdtic) + { + INT32 i; + + // Remove this tic from the list. + *tctprev = textcmdtic->next; + + // Free all players. + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + { + textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i]; + + while (textcmdplayer) + { + textcmdplayer_t *tcpnext = textcmdplayer->next; + Z_Free(textcmdplayer); + textcmdplayer = tcpnext; + } + } + + // Free this tic's own memory. + Z_Free(textcmdtic); + } +} + +// Gets the buffer for the specified ticcmd, or NULL if there isn't one +static UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum) +{ + textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next; + + // Do we have an entry for the tic? If so, look for player. + if (textcmdtic) + { + textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; + while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next; + + if (textcmdplayer) return textcmdplayer->cmd; + } + + return NULL; +} + +// Gets the buffer for the specified ticcmd, creating one if necessary +static UINT8* D_GetTextcmd(tic_t tic, INT32 playernum) +{ + textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; + textcmdplayer_t *textcmdplayer, **tcpprev; + + // Look for the tic. + while (textcmdtic && textcmdtic->tic != tic) + { + tctprev = &textcmdtic->next; + textcmdtic = textcmdtic->next; + } + + // If we don't have an entry for the tic, make it. + if (!textcmdtic) + { + textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL); + textcmdtic->tic = tic; + } + + tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; + textcmdplayer = *tcpprev; + + // Look for the player. + while (textcmdplayer && textcmdplayer->playernum != playernum) + { + tcpprev = &textcmdplayer->next; + textcmdplayer = textcmdplayer->next; + } + + // If we don't have an entry for the player, make it. + if (!textcmdplayer) + { + textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL); + textcmdplayer->playernum = playernum; + } + + return textcmdplayer->cmd; +} + +static void ExtraDataTicker(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] || i == 0) + { + UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i); + + if (bufferstart) + { + UINT8 *curpos = bufferstart; + UINT8 *bufferend = &curpos[curpos[0]+1]; + + curpos++; + while (curpos < bufferend) + { + if (*curpos < MAXNETXCMD && listnetxcmd[*curpos]) + { + const UINT8 id = *curpos; + curpos++; + DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); + (listnetxcmd[id])(&curpos, i); + DEBFILE("done\n"); + } + else + { + if (server) + { + SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); + } + CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); + break; + } + } + } + } + + // If you are a client, you can safely forget the net commands for this tic + // If you are the server, you need to remember them until every client has been acknowledged, + // because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it + if (client) + D_FreeTextcmd(gametic); +} + +static void D_Clearticcmd(tic_t tic) +{ + INT32 i; + + D_FreeTextcmd(tic); + + for (i = 0; i < MAXPLAYERS; i++) + netcmds[tic%BACKUPTICS][i].angleturn = 0; + + DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); +} + +void D_ResetTiccmds(void) +{ + INT32 i; + + memset(&localcmds, 0, sizeof(ticcmd_t)); + memset(&localcmds2, 0, sizeof(ticcmd_t)); + + // Reset the net command list + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + while (textcmds[i]) + D_Clearticcmd(textcmds[i]->tic); +} + +void SendKick(UINT8 playernum, UINT8 msg) +{ + UINT8 buf[2]; + + if (!(server && cv_rejointimeout.value)) + msg &= ~KICK_MSG_KEEP_BODY; + + buf[0] = playernum; + buf[1] = msg; + SendNetXCmd(XD_KICK, &buf, 2); +} + +// ----------------------------------------------------------------- +// end of extra data function +// ----------------------------------------------------------------- + +// ----------------------------------------------------------------- +// extra data function for lmps +// ----------------------------------------------------------------- + +// if extradatabit is set, after the ziped tic you find this: +// +// type | description +// ---------+-------------- +// byte | size of the extradata +// byte | the extradata (xd) bits: see XD_... +// with this byte you know what parameter folow +// if (xd & XDNAMEANDCOLOR) +// byte | color +// char[MAXPLAYERNAME] | name of the player +// endif +// if (xd & XD_WEAPON_PREF) +// byte | original weapon switch: boolean, true if use the old +// | weapon switch methode +// char[NUMWEAPONS] | the weapon switch priority +// byte | autoaim: true if use the old autoaim system +// endif +/*boolean AddLmpExtradata(UINT8 **demo_point, INT32 playernum) +{ + UINT8 *textcmd = D_GetExistingTextcmd(gametic, playernum); + + if (!textcmd) + return false; + + M_Memcpy(*demo_point, textcmd, textcmd[0]+1); + *demo_point += textcmd[0]+1; + return true; +} + +void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum) +{ + UINT8 nextra; + UINT8 *textcmd; + + if (!demo_pointer) + return; + + textcmd = D_GetTextcmd(gametic, playernum); + nextra = **demo_pointer; + M_Memcpy(textcmd, *demo_pointer, nextra + 1); + // increment demo pointer + *demo_pointer += nextra + 1; +}*/ + +// ----------------------------------------------------------------- +// end extra data function for lmps +// ----------------------------------------------------------------- + +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 +} cl_mode_t; + +static void GetPackets(void); + +static cl_mode_t cl_mode = CL_SEARCHING; + +static UINT16 cl_lastcheckedfilecount = 0; // used for full file list + +#ifndef NONET +#define SNAKE_SPEED 5 + +#define SNAKE_NUM_BLOCKS_X 20 +#define SNAKE_NUM_BLOCKS_Y 10 +#define SNAKE_BLOCK_SIZE 12 +#define SNAKE_BORDER_SIZE 12 + +#define SNAKE_MAP_WIDTH (SNAKE_NUM_BLOCKS_X * SNAKE_BLOCK_SIZE) +#define SNAKE_MAP_HEIGHT (SNAKE_NUM_BLOCKS_Y * SNAKE_BLOCK_SIZE) + +#define SNAKE_LEFT_X ((BASEVIDWIDTH - SNAKE_MAP_WIDTH) / 2 - SNAKE_BORDER_SIZE) +#define SNAKE_RIGHT_X (SNAKE_LEFT_X + SNAKE_MAP_WIDTH + SNAKE_BORDER_SIZE * 2 - 1) +#define SNAKE_BOTTOM_Y (BASEVIDHEIGHT - 48) +#define SNAKE_TOP_Y (SNAKE_BOTTOM_Y - SNAKE_MAP_HEIGHT - SNAKE_BORDER_SIZE * 2 + 1) + +enum snake_bonustype_s { + SNAKE_BONUS_NONE = 0, + SNAKE_BONUS_SLOW, + SNAKE_BONUS_FAST, + SNAKE_BONUS_GHOST, + SNAKE_BONUS_NUKE, + SNAKE_BONUS_SCISSORS, + SNAKE_BONUS_REVERSE, + SNAKE_BONUS_EGGMAN, + SNAKE_NUM_BONUSES, +}; + +static const char *snake_bonuspatches[] = { + NULL, + "DL_SLOW", + "TVSSC0", + "TVIVC0", + "TVARC0", + "DL_SCISSORS", + "TVRCC0", + "TVEGC0", +}; + +static const char *snake_backgrounds[] = { + "RVPUMICF", + "FRSTRCKF", + "TAR", + "MMFLRB4", + "RVDARKF1", + "RVZWALF1", + "RVZWALF4", + "RVZWALF5", + "RVZGRS02", + "RVZGRS04", +}; + +typedef struct snake_s +{ + boolean paused; + boolean pausepressed; + tic_t time; + tic_t nextupdate; + boolean gameover; + UINT8 background; + + UINT16 snakelength; + enum snake_bonustype_s snakebonus; + tic_t snakebonustime; + UINT8 snakex[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + UINT8 snakey[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + UINT8 snakedir[SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y]; + + UINT8 applex; + UINT8 appley; + + enum snake_bonustype_s bonustype; + UINT8 bonusx; + UINT8 bonusy; +} snake_t; + +static snake_t *snake = NULL; + +static void Snake_Initialise(void) +{ + if (!snake) + snake = malloc(sizeof(snake_t)); + + snake->paused = false; + snake->pausepressed = false; + snake->time = 0; + snake->nextupdate = SNAKE_SPEED; + snake->gameover = false; + snake->background = M_RandomKey(sizeof(snake_backgrounds) / sizeof(*snake_backgrounds)); + + snake->snakelength = 1; + snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakex[0] = M_RandomKey(SNAKE_NUM_BLOCKS_X); + snake->snakey[0] = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + snake->snakedir[0] = 0; + snake->snakedir[1] = 0; + + snake->applex = M_RandomKey(SNAKE_NUM_BLOCKS_X); + snake->appley = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + + snake->bonustype = SNAKE_BONUS_NONE; +} + +static UINT8 Snake_GetOppositeDir(UINT8 dir) +{ + if (dir == 1 || dir == 3) + return dir + 1; + else if (dir == 2 || dir == 4) + return dir - 1; + else + return 12 + 5 - dir; +} + +static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) +{ + UINT8 x, y; + UINT16 i; + + do + { + x = M_RandomKey(SNAKE_NUM_BLOCKS_X); + y = M_RandomKey(SNAKE_NUM_BLOCKS_Y); + + for (i = 0; i < snake->snakelength; i++) + if (x == snake->snakex[i] && y == snake->snakey[i]) + break; + } while (i < snake->snakelength || (x == headx && y == heady) + || (x == snake->applex && y == snake->appley) + || (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); + + *freex = x; + *freey = y; +} + +static void Snake_Handle(void) +{ + UINT8 x, y; + UINT8 oldx, oldy; + UINT16 i; + UINT16 joystate = 0; + + // Handle retry + if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER])) + { + Snake_Initialise(); + snake->pausepressed = true; // Avoid accidental pause on respawn + } + + // Handle pause + if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER]) + { + if (!snake->pausepressed) + snake->paused = !snake->paused; + snake->pausepressed = true; + } + else + snake->pausepressed = false; + + if (snake->paused) + return; + + snake->time++; + + x = snake->snakex[0]; + y = snake->snakey[0]; + oldx = snake->snakex[1]; + oldy = snake->snakey[1]; + + // Update direction + if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) + { + if (snake->snakelength < 2 || x <= oldx) + snake->snakedir[0] = 1; + } + else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) + { + if (snake->snakelength < 2 || x >= oldx) + snake->snakedir[0] = 2; + } + else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) + { + if (snake->snakelength < 2 || y <= oldy) + snake->snakedir[0] = 3; + } + else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) + { + if (snake->snakelength < 2 || y >= oldy) + snake->snakedir[0] = 4; + } + + if (snake->snakebonustime) + { + snake->snakebonustime--; + if (!snake->snakebonustime) + snake->snakebonus = SNAKE_BONUS_NONE; + } + + snake->nextupdate--; + if (snake->nextupdate) + return; + if (snake->snakebonus == SNAKE_BONUS_SLOW) + snake->nextupdate = SNAKE_SPEED * 2; + else if (snake->snakebonus == SNAKE_BONUS_FAST) + snake->nextupdate = SNAKE_SPEED * 2 / 3; + else + snake->nextupdate = SNAKE_SPEED; + + if (snake->gameover) + return; + + // Find new position + switch (snake->snakedir[0]) + { + case 1: + if (x > 0) + x--; + else + snake->gameover = true; + break; + case 2: + if (x < SNAKE_NUM_BLOCKS_X - 1) + x++; + else + snake->gameover = true; + break; + case 3: + if (y > 0) + y--; + else + snake->gameover = true; + break; + case 4: + if (y < SNAKE_NUM_BLOCKS_Y - 1) + y++; + else + snake->gameover = true; + break; + } + + // Check collision with snake + if (snake->snakebonus != SNAKE_BONUS_GHOST) + for (i = 1; i < snake->snakelength - 1; i++) + if (x == snake->snakex[i] && y == snake->snakey[i]) + { + if (snake->snakebonus == SNAKE_BONUS_SCISSORS) + { + snake->snakebonus = SNAKE_BONUS_NONE; + snake->snakelength = i; + S_StartSound(NULL, sfx_adderr); + } + else + snake->gameover = true; + } + + if (snake->gameover) + { + S_StartSound(NULL, sfx_lose); + return; + } + + // Check collision with apple + if (x == snake->applex && y == snake->appley) + { + if (snake->snakelength + 3 < SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y) + { + snake->snakelength++; + snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; + snake->snakey [snake->snakelength - 1] = snake->snakey [snake->snakelength - 2]; + snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2]; + } + + // Spawn new apple + Snake_FindFreeSlot(&snake->applex, &snake->appley, x, y); + + // Spawn new bonus + if (!(snake->snakelength % 5)) + { + do + { + snake->bonustype = M_RandomKey(SNAKE_NUM_BONUSES - 1) + 1; + } while (snake->snakelength > SNAKE_NUM_BLOCKS_X * SNAKE_NUM_BLOCKS_Y * 3 / 4 + && (snake->bonustype == SNAKE_BONUS_EGGMAN || snake->bonustype == SNAKE_BONUS_FAST || snake->bonustype == SNAKE_BONUS_REVERSE)); + + Snake_FindFreeSlot(&snake->bonusx, &snake->bonusy, x, y); + } + + S_StartSound(NULL, sfx_s3k6b); + } + + if (snake->snakelength > 1 && snake->snakedir[0]) + { + UINT8 dir = snake->snakedir[0]; + + oldx = snake->snakex[1]; + oldy = snake->snakey[1]; + + // Move + for (i = snake->snakelength - 1; i > 0; i--) + { + snake->snakex[i] = snake->snakex[i - 1]; + snake->snakey[i] = snake->snakey[i - 1]; + snake->snakedir[i] = snake->snakedir[i - 1]; + } + + // Handle corners + if (x < oldx && dir == 3) + dir = 5; + else if (x > oldx && dir == 3) + dir = 6; + else if (x < oldx && dir == 4) + dir = 7; + else if (x > oldx && dir == 4) + dir = 8; + else if (y < oldy && dir == 1) + dir = 9; + else if (y < oldy && dir == 2) + dir = 10; + else if (y > oldy && dir == 1) + dir = 11; + else if (y > oldy && dir == 2) + dir = 12; + snake->snakedir[1] = dir; + } + + snake->snakex[0] = x; + snake->snakey[0] = y; + + // Check collision with bonus + if (snake->bonustype != SNAKE_BONUS_NONE && x == snake->bonusx && y == snake->bonusy) + { + S_StartSound(NULL, sfx_ncchip); + + switch (snake->bonustype) + { + case SNAKE_BONUS_SLOW: + snake->snakebonus = SNAKE_BONUS_SLOW; + snake->snakebonustime = 20 * TICRATE; + break; + case SNAKE_BONUS_FAST: + snake->snakebonus = SNAKE_BONUS_FAST; + snake->snakebonustime = 20 * TICRATE; + break; + case SNAKE_BONUS_GHOST: + snake->snakebonus = SNAKE_BONUS_GHOST; + snake->snakebonustime = 10 * TICRATE; + break; + case SNAKE_BONUS_NUKE: + for (i = 0; i < snake->snakelength; i++) + { + snake->snakex [i] = snake->snakex [0]; + snake->snakey [i] = snake->snakey [0]; + snake->snakedir[i] = snake->snakedir[0]; + } + + S_StartSound(NULL, sfx_bkpoof); + break; + case SNAKE_BONUS_SCISSORS: + snake->snakebonus = SNAKE_BONUS_SCISSORS; + snake->snakebonustime = 60 * TICRATE; + break; + case SNAKE_BONUS_REVERSE: + for (i = 0; i < (snake->snakelength + 1) / 2; i++) + { + UINT16 i2 = snake->snakelength - 1 - i; + UINT8 tmpx = snake->snakex [i]; + UINT8 tmpy = snake->snakey [i]; + UINT8 tmpdir = snake->snakedir[i]; + + // Swap first segment with last segment + snake->snakex [i] = snake->snakex [i2]; + snake->snakey [i] = snake->snakey [i2]; + snake->snakedir[i] = Snake_GetOppositeDir(snake->snakedir[i2]); + snake->snakex [i2] = tmpx; + snake->snakey [i2] = tmpy; + snake->snakedir[i2] = Snake_GetOppositeDir(tmpdir); + } + + snake->snakedir[0] = 0; + + S_StartSound(NULL, sfx_gravch); + break; + default: + if (snake->snakebonus != SNAKE_BONUS_GHOST) + { + snake->gameover = true; + S_StartSound(NULL, sfx_lose); + } + } + + snake->bonustype = SNAKE_BONUS_NONE; + } +} + +static void Snake_Draw(void) +{ + INT16 i; + + // Background + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + + V_DrawFlatFill( + SNAKE_LEFT_X + SNAKE_BORDER_SIZE, + SNAKE_TOP_Y + SNAKE_BORDER_SIZE, + SNAKE_MAP_WIDTH, + SNAKE_MAP_HEIGHT, + W_GetNumForName(snake_backgrounds[snake->background]) + ); + + // Borders + V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Top + V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_TOP_Y, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Right + V_DrawFill(SNAKE_LEFT_X + SNAKE_BORDER_SIZE, SNAKE_TOP_Y + SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, SNAKE_BORDER_SIZE + SNAKE_MAP_WIDTH, SNAKE_BORDER_SIZE, 242); // Bottom + V_DrawFill(SNAKE_LEFT_X, SNAKE_TOP_Y + SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE, SNAKE_BORDER_SIZE + SNAKE_MAP_HEIGHT, 242); // Left + + // Apple + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->applex * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->appley * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + FRACUNIT / 4, + 0, + W_CachePatchLongName("DL_APPLE", PU_HUDGFX), + NULL + ); + + // Bonus + if (snake->bonustype != SNAKE_BONUS_NONE) + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->bonusx * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 ) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->bonusy * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2 + 4) * FRACUNIT, + FRACUNIT / 2, + 0, + W_CachePatchLongName(snake_bonuspatches[snake->bonustype], PU_HUDGFX), + NULL + ); + + // Snake + if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over + { + for (i = snake->snakelength - 1; i >= 0; i--) + { + const char *patchname; + UINT8 dir = snake->snakedir[i]; + + if (i == 0) // Head + { + switch (dir) + { + case 1: patchname = "DL_SNAKEHEAD_L"; break; + case 2: patchname = "DL_SNAKEHEAD_R"; break; + case 3: patchname = "DL_SNAKEHEAD_T"; break; + case 4: patchname = "DL_SNAKEHEAD_B"; break; + default: patchname = "DL_SNAKEHEAD_M"; + } + } + else // Body + { + switch (dir) + { + case 1: patchname = "DL_SNAKEBODY_L"; break; + case 2: patchname = "DL_SNAKEBODY_R"; break; + case 3: patchname = "DL_SNAKEBODY_T"; break; + case 4: patchname = "DL_SNAKEBODY_B"; break; + case 5: patchname = "DL_SNAKEBODY_LT"; break; + case 6: patchname = "DL_SNAKEBODY_RT"; break; + case 7: patchname = "DL_SNAKEBODY_LB"; break; + case 8: patchname = "DL_SNAKEBODY_RB"; break; + case 9: patchname = "DL_SNAKEBODY_TL"; break; + case 10: patchname = "DL_SNAKEBODY_TR"; break; + case 11: patchname = "DL_SNAKEBODY_BL"; break; + case 12: patchname = "DL_SNAKEBODY_BR"; break; + default: patchname = "DL_SNAKEBODY_B"; + } + } + + V_DrawFixedPatch( + (SNAKE_LEFT_X + SNAKE_BORDER_SIZE + snake->snakex[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + (SNAKE_TOP_Y + SNAKE_BORDER_SIZE + snake->snakey[i] * SNAKE_BLOCK_SIZE + SNAKE_BLOCK_SIZE / 2) * FRACUNIT, + i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2, + snake->snakebonus == SNAKE_BONUS_GHOST ? V_TRANSLUCENT : 0, + W_CachePatchLongName(patchname, PU_HUDGFX), + NULL + ); + } + } + + // Length + V_DrawString(SNAKE_RIGHT_X + 4, SNAKE_TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); + + // Bonus + if (snake->snakebonus != SNAKE_BONUS_NONE + && (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2)) + V_DrawFixedPatch( + (SNAKE_RIGHT_X + 10) * FRACUNIT, + (SNAKE_TOP_Y + 24) * FRACUNIT, + FRACUNIT / 2, + 0, + W_CachePatchLongName(snake_bonuspatches[snake->snakebonus], PU_HUDGFX), + NULL + ); +} + +static void CL_DrawConnectionStatusBox(void) +{ + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); + if (cl_mode != CL_CONFIRMCONNECT) + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); +} + +// +// CL_DrawConnectionStatus +// +// Keep the local client informed of our status. +// +static inline void CL_DrawConnectionStatus(void) +{ + INT32 ccstime = I_GetTime(); + + // Draw background fade + V_DrawFadeScreen(0xFF00, 16); // force default + + if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES) + { + INT32 i, animtime = ((ccstime / 4) & 15) + 16; + UINT8 palstart; + const char *cltext; + + // Draw the bottom box. + CL_DrawConnectionStatusBox(); + + if (cl_mode == CL_SEARCHING) + palstart = 32; // Red + else if (cl_mode == CL_CONFIRMCONNECT) + palstart = 48; // Orange + else + palstart = 96; // Green + + if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) + for (i = 0; i < 16; ++i) // 15 pal entries total. + V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); + + switch (cl_mode) + { + case CL_DOWNLOADSAVEGAME: + if (fileneeded && lastfilenum != -1) + { + UINT32 currentsize = fileneeded[lastfilenum].currentsize; + UINT32 totalsize = fileneeded[lastfilenum].totalsize; + INT32 dldlength; + + cltext = M_GetText("Downloading game state..."); + Net_GetNetStat(); + + dldlength = (INT32)((currentsize/(double)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_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va(" %4uK/%4uK",currentsize>>10,totalsize>>10)); + + V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + cltext = M_GetText("Waiting to download game state..."); + break; + case CL_ASKFULLFILELIST: + 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: + if (serverisfull) + cltext = M_GetText("Server full, waiting for a slot..."); + else + cltext = M_GetText("Requesting to join..."); + break; + default: + cltext = M_GetText("Connecting to server..."); + break; + } + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext); + } + else + { + if (cl_mode == CL_LOADFILES) + { + INT32 totalfileslength; + INT32 loadcompletednum = 0; + INT32 i; + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); + + //ima just count files here + if (fileneeded) + { + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_OPEN) + loadcompletednum++; + } + + // Loading progress + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, "Loading server addons..."); + totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256); + M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, + va(" %2u/%2u Files",loadcompletednum,fileneedednum)); + } + else if (lastfilenum != -1) + { + INT32 dldlength; + static char tempname[28]; + fileneeded_t *file; + char *filename; + + if (snake) + Snake_Draw(); + + // Draw the bottom box. + CL_DrawConnectionStatusBox(); + + if (fileneeded) + { + file = &fileneeded[lastfilenum]; + filename = file->filename; + } + else + return; + + 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); + + memset(tempname, 0, sizeof(tempname)); + // offset filename to just the name only part + filename += strlen(filename) - nameonlylength(filename); + + if (strlen(filename) > sizeof(tempname)-1) // too long to display fully + { + size_t endhalfpos = strlen(filename)-10; + // display as first 14 chars + ... + last 10 chars + // which should add up to 27 if our math(s) is correct + snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos); + } + else // we can copy the whole thing in safely + { + strncpy(tempname, filename, sizeof(tempname)-1); + } + + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, + va(M_GetText("Downloading \"%s\""), tempname)); + V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 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, + va("%3.1fK/s ", ((double)getbps)/1024)); + } + else + { + if (snake) + Snake_Draw(); + + CL_DrawConnectionStatusBox(); + V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, + M_GetText("Waiting to download files...")); + } + } +} +#endif + +static boolean CL_AskFileList(INT32 firstfile) +{ + netbuffer->packettype = PT_TELLFILESNEEDED; + netbuffer->u.filesneedednum = firstfile; + + return HSendPacket(servernode, false, 0, sizeof (INT32)); +} + +/** Sends a special packet to declare how many players in local + * Used only in arbitratrenetstart() + * Sends a PT_CLIENTJOIN packet to the server + * + * \return True if the packet was successfully sent + * \todo Improve the description... + * + */ +static boolean CL_SendJoin(void) +{ + UINT8 localplayers = 1; + if (netgame) + CONS_Printf(M_GetText("Sending join request...\n")); + netbuffer->packettype = PT_CLIENTJOIN; + + netbuffer->u.clientcfg.modversion = MODVERSION; + strncpy(netbuffer->u.clientcfg.application, + SRB2APPLICATION, + sizeof netbuffer->u.clientcfg.application); + + if (splitscreen || botingame) + localplayers++; + netbuffer->u.clientcfg.localplayers = localplayers; + + CleanupPlayerName(consoleplayer, cv_playername.zstring); + if (splitscreen) + CleanupPlayerName(1, cv_playername2.zstring);/* 1 is a HACK? oh no */ + + strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); + strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); + + return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); +} + +static INT32 FindRejoinerNum(SINT8 node) +{ + char strippednodeaddress[64]; + const char *nodeaddress; + char *port; + INT32 i; + + // Make sure there is no dead dress before proceeding to the stripping + if (!I_GetNodeAddress) + return -1; + nodeaddress = I_GetNodeAddress(node); + if (!nodeaddress) + return -1; + + // Strip the address of its port + strcpy(strippednodeaddress, nodeaddress); + port = strchr(strippednodeaddress, ':'); + if (port) + *port = '\0'; + + // Check if any player matches the stripped address + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX + && !strcmp(playeraddress[i], strippednodeaddress)) + return i; + } + + return -1; +} + +static UINT8 +GetRefuseReason (INT32 node) +{ + if (!node || FindRejoinerNum(node) != -1) + return 0; + else if (bannednode && bannednode[node]) + return REFUSE_BANNED; + else if (!cv_allownewplayer.value) + return REFUSE_JOINS_DISABLED; + else if (D_NumPlayers() >= cv_maxplayers.value) + return REFUSE_SLOTS_FULL; + else + return 0; +} + +static void SV_SendServerInfo(INT32 node, tic_t servertime) +{ + UINT8 *p; + + netbuffer->packettype = PT_SERVERINFO; + netbuffer->u.serverinfo._255 = 255; + netbuffer->u.serverinfo.packetversion = PACKETVERSION; + netbuffer->u.serverinfo.version = VERSION; + netbuffer->u.serverinfo.subversion = SUBVERSION; + strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION, + sizeof netbuffer->u.serverinfo.application); + // return back the time value so client can compute their ping + netbuffer->u.serverinfo.time = (tic_t)LONG(servertime); + netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); + + netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); + netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; + + netbuffer->u.serverinfo.refusereason = GetRefuseReason(node); + + strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], + sizeof netbuffer->u.serverinfo.gametypename); + netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; + netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); + netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0); + strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, + MAXSERVERNAME); + strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); + + M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); + + memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle); + + if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl) + { + char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle; + while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0') + { + if (!(*read & 0x80)) + { + *writ = toupper(*read); + writ++; + } + read++; + } + *writ = '\0'; + //strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); + } + else + strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32); + + if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + netbuffer->u.serverinfo.iszone = 1; + else + netbuffer->u.serverinfo.iszone = 0; + + if (mapheaderinfo[gamemap-1]) + netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum; + + p = PutFileNeeded(0); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); +} + +static void SV_SendPlayerInfo(INT32 node) +{ + UINT8 i; + netbuffer->packettype = PT_PLAYERINFO; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + { + netbuffer->u.playerinfo[i].num = 255; // This slot is empty. + continue; + } + + netbuffer->u.playerinfo[i].num = i; + strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); + netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; + + //fetch IP address + //No, don't do that, you fuckface. + memset(netbuffer->u.playerinfo[i].address, 0, 4); + + if (G_GametypeHasTeams()) + { + if (!players[i].ctfteam) + netbuffer->u.playerinfo[i].team = 255; + else + netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam; + } + else + { + if (players[i].spectator) + netbuffer->u.playerinfo[i].team = 255; + else + netbuffer->u.playerinfo[i].team = 0; + } + + netbuffer->u.playerinfo[i].score = LONG(players[i].score); + netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); + netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin +#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself + % 3 +#endif + ); + + // Extra data + netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; + + if (players[i].pflags & PF_TAGIT) + netbuffer->u.playerinfo[i].data |= 0x20; + + if (players[i].gotflag) + netbuffer->u.playerinfo[i].data |= 0x40; + + if (players[i].powers[pw_super]) + netbuffer->u.playerinfo[i].data |= 0x80; + } + + HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); +} + +/** Sends a PT_SERVERCFG packet + * + * \param node The destination + * \return True if the packet was successfully sent + * + */ +static boolean SV_SendServerConfig(INT32 node) +{ + boolean waspacketsent; + + netbuffer->packettype = PT_SERVERCFG; + + netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; + netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); + netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); + netbuffer->u.servercfg.clientnode = (UINT8)node; + netbuffer->u.servercfg.gamestate = (UINT8)gamestate; + netbuffer->u.servercfg.gametype = (UINT8)gametype; + netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; + + memcpy(netbuffer->u.servercfg.server_context, server_context, 8); + + { + const size_t len = sizeof (serverconfig_pak); + +#ifdef DEBUGFILE + if (debugfile) + { + fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n", + sizeu1(len), node); + } +#endif + + waspacketsent = HSendPacket(node, true, 0, len); + } + +#ifdef DEBUGFILE + if (debugfile) + { + if (waspacketsent) + { + fprintf(debugfile, "ServerConfig Packet was sent\n"); + } + else + { + fprintf(debugfile, "ServerConfig Packet could not be sent right now\n"); + } + } +#endif + + return waspacketsent; +} + +#ifndef NONET +#define SAVEGAMESIZE (768*1024) + +static boolean SV_ResendingSavegameToAnyone(void) +{ + INT32 i; + + for (i = 0; i < MAXNETNODES; i++) + if (resendingsavegame[i]) + return true; + return false; +} + +static void SV_SendSaveGame(INT32 node, boolean resending) +{ + size_t length, compressedlen; + UINT8 *savebuffer; + UINT8 *compressedsave; + UINT8 *buffertosend; + + // first save it in a malloced buffer + savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); + if (!savebuffer) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + // Leave room for the uncompressed length. + save_p = savebuffer + sizeof(UINT32); + + P_SaveNetGame(resending); + + length = save_p - savebuffer; + if (length > SAVEGAMESIZE) + { + free(savebuffer); + save_p = NULL; + I_Error("Savegame buffer overrun"); + } + + // Allocate space for compressed save: one byte fewer than for the + // uncompressed data to ensure that the compression is worthwhile. + compressedsave = malloc(length - 1); + if (!compressedsave) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + // Attempt to compress it. + if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) + { + // Compressing succeeded; send compressed data + + free(savebuffer); + + // State that we're compressed. + buffertosend = compressedsave; + WRITEUINT32(compressedsave, length - sizeof(UINT32)); + length = compressedlen + sizeof(UINT32); + } + else + { + // Compression failed to make it smaller; send original + + free(compressedsave); + + // State that we're not compressed + buffertosend = savebuffer; + WRITEUINT32(savebuffer, 0); + } + + AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); + save_p = NULL; + + // Remember when we started sending the savegame so we can handle timeouts + sendingsavegame[node] = true; + freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte +} + +#ifdef DUMPCONSISTENCY +#define TMPSAVENAME "badmath.sav" +static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +static void SV_SavedGame(void) +{ + size_t length; + UINT8 *savebuffer; + char tmpsave[256]; + + if (!cv_dumpconsistency.value) + return; + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + // first save it in a malloced buffer + save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); + if (!save_p) + { + CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); + return; + } + + P_SaveNetGame(false); + + length = save_p - savebuffer; + if (length > SAVEGAMESIZE) + { + free(savebuffer); + save_p = NULL; + I_Error("Savegame buffer overrun"); + } + + // then save it! + if (!FIL_WriteFile(tmpsave, savebuffer, length)) + CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); + + free(savebuffer); + save_p = NULL; +} + +#undef TMPSAVENAME +#endif +#define TMPSAVENAME "$$$.sav" + + +static void CL_LoadReceivedSavegame(boolean reloading) +{ + UINT8 *savebuffer = NULL; + size_t length, decompressedlen; + char tmpsave[256]; + + FreeFileNeeded(); + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + length = FIL_ReadFile(tmpsave, &savebuffer); + + CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); + if (!length) + { + I_Error("Can't read savegame sent"); + return; + } + + save_p = savebuffer; + + // Decompress saved game if necessary. + decompressedlen = READUINT32(save_p); + if(decompressedlen > 0) + { + UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); + lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); + Z_Free(savebuffer); + save_p = savebuffer = decompressedbuffer; + } + + paused = false; + demoplayback = false; + titlemapinaction = TITLEMAP_OFF; + titledemo = false; + automapactive = false; + + P_StopRumble(NULL); + + // load a base level + if (P_LoadNetGame(reloading)) + { + const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; + CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); + if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) + { + CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); + if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + CONS_Printf(M_GetText(" Zone")); + if (actnum > 0) + CONS_Printf(" %2d", actnum); + } + CONS_Printf("\"\n"); + } + + // done + Z_Free(savebuffer); + save_p = NULL; + if (unlink(tmpsave) == -1) + CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); + consistancy[gametic%BACKUPTICS] = Consistancy(); + CON_ToggleOff(); + + // Tell the server we have received and reloaded the gamestate + // so they know they can resume the game + netbuffer->packettype = PT_RECEIVEDGAMESTATE; + HSendPacket(servernode, true, 0, 0); +} + +static void CL_ReloadReceivedSavegame(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + LUA_InvalidatePlayer(&players[i]); + sprintf(player_names[i], "Player %d", i + 1); + } + + CL_LoadReceivedSavegame(true); + + if (neededtic < gametic) + neededtic = gametic; + maketic = neededtic; + + ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; + P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16)); + if (splitscreen) + { + ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn; + P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16)); + } + + camera.subsector = R_PointInSubsector(camera.x, camera.y); + camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); + + cl_redownloadinggamestate = false; + + CONS_Printf(M_GetText("Game state reloaded\n")); +} +#endif + +#ifndef NONET +static void SendAskInfo(INT32 node) +{ + const tic_t asktime = I_GetTime(); + netbuffer->packettype = PT_ASKINFO; + netbuffer->u.askinfo.version = VERSION; + netbuffer->u.askinfo.time = (tic_t)LONG(asktime); + + // Even if this never arrives due to the host being firewalled, we've + // now allowed traffic from the host to us in, so once the MS relays + // our address to the host, it'll be able to speak to us. + HSendPacket(node, false, 0, sizeof (askinfo_pak)); +} + +serverelem_t serverlist[MAXSERVERLIST]; +UINT32 serverlistcount = 0; + +#define FORCECLOSE 0x8000 + +static void SL_ClearServerList(INT32 connectedserver) +{ + UINT32 i; + + for (i = 0; i < serverlistcount; i++) + if (connectedserver != serverlist[i].node) + { + Net_CloseConnection(serverlist[i].node|FORCECLOSE); + serverlist[i].node = 0; + } + serverlistcount = 0; +} + +static UINT32 SL_SearchServer(INT32 node) +{ + UINT32 i; + for (i = 0; i < serverlistcount; i++) + if (serverlist[i].node == node) + return i; + + return UINT32_MAX; +} + +static void SL_InsertServer(serverinfo_pak* info, SINT8 node) +{ + UINT32 i; + + // search if not already on it + i = SL_SearchServer(node); + if (i == UINT32_MAX) + { + // not found add it + if (serverlistcount >= MAXSERVERLIST) + return; // list full + + /* check it later if connecting to this one */ + if (node != servernode) + { + if (info->_255 != 255) + return;/* old packet format */ + + if (info->packetversion != PACKETVERSION) + return;/* old new packet format */ + + if (info->version != VERSION) + return; // Not same version. + + if (info->subversion != SUBVERSION) + return; // Close, but no cigar. + + if (strcmp(info->application, SRB2APPLICATION)) + return;/* that's a different mod */ + } + + i = serverlistcount++; + } + + serverlist[i].info = *info; + serverlist[i].node = node; + + // resort server list + M_SortServerList(); +} + +#if defined (MASTERSERVER) && defined (HAVE_THREADS) +struct Fetch_servers_ctx +{ + int room; + int id; +}; + +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) + { + I_lock_mutex(&ms_QueryId_mutex); + { + 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); + } + } + + free(ctx); +} +#endif/*defined (MASTERSERVER) && defined (HAVE_THREADS)*/ + +void CL_QueryServerList (msg_server_t *server_list) +{ + INT32 i; + + for (i = 0; server_list[i].header.buffer[0]; i++) + { + // Make sure MS version matches our own, to + // thwart nefarious servers who lie to the MS. + + /* lol bruh, that version COMES from the servers */ + //if (strcmp(version, server_list[i].version) == 0) + { + INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); + if (node == -1) + break; // no more node free + SendAskInfo(node); + // Force close the connection so that servers can't eat + // up nodes forever if we never get a reply back from them + // (usually when they've not forwarded their ports). + // + // Don't worry, we'll get in contact with the working + // servers again when they send SERVERINFO to us later! + // + // (Note: as a side effect this probably means every + // server in the list will probably be using the same node (e.g. node 1), + // not that it matters which nodes they use when + // the connections are closed afterwards anyway) + // -- Monster Iestyn 12/11/18 + Net_CloseConnection(node|FORCECLOSE); + } + } +} + +void CL_UpdateServerList(boolean internetsearch, INT32 room) +{ + (void)internetsearch; + (void)room; + + SL_ClearServerList(0); + + if (!netgame && I_NetOpenSocket) + { + if (I_NetOpenSocket()) + { + netgame = true; + multiplayer = true; + } + } + + // search for local servers + if (netgame) + SendAskInfo(BROADCASTADDR); + +#ifdef MASTERSERVER + if (internetsearch) + { +#ifdef HAVE_THREADS + struct Fetch_servers_ctx *ctx; + + 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/*MASTERSERVER*/ +} + +#endif // ifndef NONET + +static void M_ConfirmConnect(event_t *ev) +{ +#ifndef NONET + + if (ev->type == ev_keydown || ev->type == ev_gamepad_down) + { + if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A)) + { + if (totalfilesrequestednum > 0) + { + if (CL_SendFileRequest()) + { + cl_mode = CL_DOWNLOADFILES; + Snake_Initialise(); + } + } + else + cl_mode = CL_LOADFILES; + + M_ClearMenus(true); + } + else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B)) + { + cl_mode = CL_ABORTED; + M_ClearMenus(true); + } + } +#else + (void)ev; +#endif +} + +static boolean CL_FinishedFileList(void) +{ + INT32 i; + char *downloadsize = NULL; + + //CONS_Printf(M_GetText("Checking files...\n")); + i = CL_CheckFiles(); + if (i == 4) // still checking ... + { + return true; + } + else if (i == 3) // too many files + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have too many WAD files loaded\n" + "to add ones the server is using.\n" + "Please restart SRB2 before connecting.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 2) // cannot join for some reason + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have the wrong addons loaded.\n\n" + "To play on this server, restart\n" + "the game and don't load any addons.\n" + "SRB2 will automatically add\n" + "everything you need when you join.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 1) + { + 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 ENTER to continue\nor ESC to cancel.\n\n" + ), M_ConfirmConnect, MM_EVENTHANDLER); + cl_mode = CL_CONFIRMCONNECT; + curfadevalue = 0; + } + else + cl_mode = CL_LOADFILES; + } + else + { + // must download something + // can we, though? + if (!CL_CheckDownloadable()) // nope! + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "An error occured when trying to\n" + "download missing addons.\n" + "(This is almost always a problem\n" + "with the server, not your game.)\n\n" + "See the console or log file\n" + "for additional details.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + +#ifndef NONET + downloadcompletednum = 0; + downloadcompletedsize = 0; + totalfilesrequestednum = 0; + totalfilesrequestedsize = 0; + + if (fileneeded == NULL) + I_Error("CL_FinishedFileList: fileneeded == NULL"); + + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) + { + totalfilesrequestednum++; + totalfilesrequestedsize += fileneeded[i].totalsize; + } + + 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\nis required to join.\n" + "\n" + "You may download, load server addons,\nand wait for a slot.\n" + "\n" + "Press ENTER to continue\nor ESC to cancel.\n" + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + else + M_StartMessage(va(M_GetText( + "Download of %s additional content\nis required to join.\n" + "\n" + "Press ENTER to continue\nor ESC to cancel.\n" + ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); + + Z_Free(downloadsize); + cl_mode = CL_CONFIRMCONNECT; + curfadevalue = 0; + } + return true; +} + +#ifndef NONET +static const char * InvalidServerReason (serverinfo_pak *info) +{ +#define EOT "\nPress ESC\n" + + /* magic number for new packet format */ + if (info->_255 != 255) + { + return + "Outdated server (version unknown).\n" EOT; + } + + if (strncmp(info->application, SRB2APPLICATION, sizeof + info->application)) + { + return va( + "%s cannot connect\n" + "to %s servers.\n" EOT, + SRB2APPLICATION, + info->application); + } + + if ( + info->packetversion != PACKETVERSION || + info->version != VERSION || + info->subversion != SUBVERSION + ){ + return va( + "Incompatible %s versions.\n" + "(server version %d.%d.%d)\n" EOT, + SRB2APPLICATION, + info->version / 100, + info->version % 100, + info->subversion); + } + + switch (info->refusereason) + { + case REFUSE_BANNED: + return + "You have been banned\n" + "from the server.\n" EOT; + case REFUSE_JOINS_DISABLED: + return + "The server is not accepting\n" + "joins for the moment.\n" EOT; + case REFUSE_SLOTS_FULL: + return va( + "Maximum players reached: %d\n" EOT, + info->maxplayer); + default: + if (info->refusereason) + { + return + "You can't join.\n" + "I don't know why,\n" + "but you can't join.\n" EOT; + } + } + + return NULL; + +#undef EOT +} +#endif // ifndef NONET + +/** Called by CL_ServerConnectionTicker + * + * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. + * \return False if the connection was aborted + * \sa CL_ServerConnectionTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) +{ +#ifndef NONET + INT32 i; + + // serverlist is updated by GetPacket function + if (serverlistcount > 0) + { + // this can be a responce to our broadcast request + if (servernode == -1 || servernode >= MAXNETNODES) + { + i = 0; + servernode = serverlist[i].node; + CONS_Printf(M_GetText("Found, ")); + } + else + { + i = SL_SearchServer(servernode); + if (i < 0) + return true; + } + + if (client) + { + serverinfo_pak *info = &serverlist[i].info; + + if (info->refusereason == REFUSE_SLOTS_FULL) + serverisfull = true; + else + { + const char *reason = InvalidServerReason(info); + + // Quit here rather than downloading files + // and being refused later. + if (reason) + { + char *message = Z_StrDup(reason); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(message, NULL, MM_NOTHING); + Z_Free(message); + return false; + } + } + + D_ParseFileneeded(info->fileneedednum, info->fileneeded, 0); + + if (info->flags & SV_LOTSOFADDONS) + { + cl_mode = CL_ASKFULLFILELIST; + cl_lastcheckedfilecount = 0; + return true; + } + + cl_mode = CL_CHECKFILES; + } + else + { + cl_mode = CL_ASKJOIN; // files need not be checked for the server. + *asksent = 0; + } + + return true; + } + + // Ask the info to the server (askinfo packet) + if (*asksent + NEWTICRATE < I_GetTime()) + { + SendAskInfo(servernode); + *asksent = I_GetTime(); + } +#else + (void)asksent; + // No netgames, so we skip this state. + cl_mode = CL_ASKJOIN; +#endif // ifndef NONET/else + + return true; +} + +/** Called by CL_ConnectToServer + * + * \param tmpsave The name of the gamestate file??? + * \param oldtic Used for knowing when to poll events and redraw + * \param asksent ??? + * \return False if the connection was aborted + * \sa CL_ServerConnectionSearchTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) +{ + boolean waitmore; + INT32 i; + +#ifdef NONET + (void)tmpsave; +#endif + + switch (cl_mode) + { + case CL_SEARCHING: + if (!CL_ServerConnectionSearchTicker(asksent)) + return false; + break; + + case CL_ASKFULLFILELIST: + if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved + cl_mode = CL_CHECKFILES; + else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent) + { + if (CL_AskFileList(fileneedednum)) + { + cl_lastcheckedfilecount = fileneedednum; + *asksent = I_GetTime() + NEWTICRATE; + } + } + break; + case CL_CHECKFILES: + if (!CL_FinishedFileList()) + return false; + break; + case CL_DOWNLOADFILES: + waitmore = false; + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_DOWNLOADING + || fileneeded[i].status == FS_REQUESTED) + { + waitmore = true; + break; + } + if (waitmore) + break; // exit the case + +#ifndef NONET + if (snake) + { + free(snake); + snake = NULL; + } +#endif + + cl_mode = CL_LOADFILES; + break; + case CL_LOADFILES: + if (CL_LoadServerFiles()) + { + FreeFileNeeded(); + *asksent = 0; //This ensure the first join ask is right away + firstconnectattempttime = I_GetTime(); + cl_mode = CL_ASKJOIN; + } + break; + case CL_ASKJOIN: + 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; + } +#ifndef NONET + // 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 (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) + { + // Gamestate is now handled within CL_LoadReceivedSavegame() + CL_LoadReceivedSavegame(false); + cl_mode = CL_CONNECTED; + } // don't break case continue to CL_CONNECTED + else + break; +#endif + + case CL_CONNECTED: + case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect + default: + break; + + // Connection closed by cancel, timeout or refusal. + case CL_ABORTED: + cl_mode = CL_SEARCHING; + return false; + } + + GetPackets(); + Net_AckTicker(); + + // Call it only once by tic + if (*oldtic != I_GetTime()) + { + I_OsPolling(); + + if (cl_mode == CL_CONFIRMCONNECT) + D_ProcessEvents(); //needed for menu system to receive inputs + else + { + // my hand has been forced and I am dearly sorry for this awful hack :vomit: + for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) + { + G_MapEventsToControls(&events[eventtail]); + } + } + + if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || 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); + +#ifndef NONET + if (snake) + { + free(snake); + snake = NULL; + } +#endif + + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + memset(gamekeydown, 0, NUMKEYS); + return false; + } +#ifndef NONET + else if (cl_mode == CL_DOWNLOADFILES && snake) + Snake_Handle(); +#endif + + if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) + FileReceiveTicker(); + + // why are these here? this is for servers, we're a client + //if (key == 's' && server) + // doomcom->numnodes = (INT16)pnumnodes; + //FileSendTicker(); + *oldtic = I_GetTime(); + +#ifndef NONET + if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) + { + if (!snake) + { + F_MenuPresTicker(true); // title sky + F_TitleScreenTicker(true); + 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(); + S_UpdateSounds(); + S_UpdateClosedCaptions(); + } +#else + CON_Drawer(); + I_UpdateNoVsync(); +#endif + } + else + { + I_Sleep(cv_sleep.value); + I_UpdateTime(cv_timescale.value); + } + + return true; +} + +/** Use adaptive send using net_bandwidth and stat.sendbytes + * + * \todo Better description... + * + */ +static void CL_ConnectToServer(void) +{ + INT32 pnumnodes, nodewaited = doomcom->numnodes, i; + tic_t oldtic; +#ifndef NONET + tic_t asksent; + char tmpsave[256]; + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + lastfilenum = -1; +#endif + + cl_mode = CL_SEARCHING; + +#ifndef NONET + // Don't get a corrupt savegame error because tmpsave already exists + if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) + I_Error("Can't delete %s\n", tmpsave); +#endif + + if (netgame) + { + if (servernode < 0 || servernode >= MAXNETNODES) + CONS_Printf(M_GetText("Searching for a server...\n")); + else + CONS_Printf(M_GetText("Contacting the server...\n")); + } + + if (gamestate == GS_INTERMISSION) + Y_EndIntermission(); // clean up intermission graphics etc + + DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); + G_SetGamestate(GS_WAITINGPLAYERS); + wipegamestate = GS_WAITINGPLAYERS; + + ClearAdminPlayers(); + pnumnodes = 1; + oldtic = I_GetTime() - 1; + +#ifndef NONET + asksent = (tic_t) - TICRATE; + firstconnectattempttime = I_GetTime(); + + i = SL_SearchServer(servernode); + + if (i != -1) + { + char *gametypestr = serverlist[i].info.gametypename; + CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); + gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0'; + CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); + CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, + serverlist[i].info.version%100, serverlist[i].info.subversion); + } + SL_ClearServerList(servernode); +#endif + + do + { + // If the connection was aborted for some reason, leave +#ifndef NONET + if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) +#else + if (!CL_ServerConnectionTicker((char*)NULL, &oldtic, (tic_t *)NULL)) +#endif + return; + + if (server) + { + pnumnodes = 0; + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i]) + pnumnodes++; + } + } + while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); + + DEBFILE(va("Synchronisation Finished\n")); + + displayplayer = consoleplayer; +} + +#ifndef NONET +typedef struct banreason_s +{ + char *reason; + struct banreason_s *prev; //-1 + struct banreason_s *next; //+1 +} banreason_t; + +static banreason_t *reasontail = NULL; //last entry, use prev +static banreason_t *reasonhead = NULL; //1st entry, use next + +static void Command_ShowBan(void) //Print out ban list +{ + size_t i; + const char *address, *mask; + banreason_t *reasonlist = reasonhead; + + if (I_GetBanAddress) + CONS_Printf(M_GetText("Ban List:\n")); + else + return; + + for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + { + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) + CONS_Printf("%s: %s ", sizeu1(i+1), address); + else + CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); + + if (reasonlist && reasonlist->reason) + CONS_Printf("(%s)\n", reasonlist->reason); + else + CONS_Printf("\n"); + + if (reasonlist) reasonlist = reasonlist->next; + } + + if (i == 0 && !address) + CONS_Printf(M_GetText("(empty)\n")); +} + +void D_SaveBan(void) +{ + FILE *f; + size_t i; + banreason_t *reasonlist = reasonhead; + const char *address, *mask; + const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); + + if (!reasonhead) + { + remove(path); + return; + } + + f = fopen(path, "w"); + + if (!f) + { + CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n")); + return; + } + + for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) + { + if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) + fprintf(f, "%s 0", address); + else + fprintf(f, "%s %s", address, mask); + + if (reasonlist && reasonlist->reason) + fprintf(f, " %s\n", reasonlist->reason); + else + fprintf(f, " %s\n", "NA"); + + if (reasonlist) reasonlist = reasonlist->next; + } + + fclose(f); +} + +static void Ban_Add(const char *reason) +{ + banreason_t *reasonlist = malloc(sizeof(*reasonlist)); + + if (!reasonlist) + return; + if (!reason) + reason = "NA"; + + reasonlist->next = NULL; + reasonlist->reason = Z_StrDup(reason); + if ((reasonlist->prev = reasontail) == NULL) + reasonhead = reasonlist; + else + reasontail->next = reasonlist; + reasontail = reasonlist; +} + +static void Ban_Clear(void) +{ + banreason_t *temp; + + I_ClearBans(); + + reasontail = NULL; + + while (reasonhead) + { + temp = reasonhead->next; + Z_Free(reasonhead->reason); + free(reasonhead); + reasonhead = temp; + } +} + +static void Command_ClearBans(void) +{ + if (!I_ClearBans) + return; + + Ban_Clear(); + D_SaveBan(); +} + +static void Ban_Load_File(boolean warning) +{ + FILE *f; + size_t i; + const char *address, *mask; + char buffer[MAX_WADPATH]; + + if (!I_ClearBans) + return; + + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); + + if (!f) + { + if (warning) + CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n")); + return; + } + + Ban_Clear(); + + for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) + { + address = strtok(buffer, " \t\r\n"); + mask = strtok(NULL, " \t\r\n"); + + I_SetBanAddress(address, mask); + + Ban_Add(strtok(NULL, "\r\n")); + } + + fclose(f); +} + +static void Command_ReloadBan(void) //recheck ban.txt +{ + Ban_Load_File(true); +} + +static void Command_connect(void) +{ + if (COM_Argc() < 2 || *COM_Argv(1) == 0) + { + CONS_Printf(M_GetText( + "Connect (port): connect to a server\n" + "Connect ANY: connect to the first lan server found\n" + //"Connect SELF: connect to your own server.\n" + )); + return; + } + + if (Playing() || titledemo) + { + CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); + return; + } + + // modified game check: no longer handled + // we don't request a restart unless the filelist differs + + server = false; +/* + if (!stricmp(COM_Argv(1), "self")) + { + servernode = 0; + server = true; + /// \bug should be but... + //SV_SpawnServer(); + } + else +*/ + { + // used in menu to connect to a server in the list + if (netgame && !stricmp(COM_Argv(1), "node")) + { + servernode = (SINT8)atoi(COM_Argv(2)); + } + else if (netgame) + { + CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); + return; + } + else if (I_NetOpenSocket) + { + I_NetOpenSocket(); + netgame = true; + multiplayer = true; + + if (!stricmp(COM_Argv(1), "any")) + servernode = BROADCASTADDR; + else if (I_NetMakeNodewPort) + { + if (COM_Argc() >= 3) // address AND port + servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2)); + else // address only, or address:port + servernode = I_NetMakeNode(COM_Argv(1)); + } + else + { + CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); + D_CloseConnection(); + return; + } + } + else + CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n")); + } + + splitscreen = false; + SplitScreen_OnChange(); + botingame = false; + botskin = 0; + CL_ConnectToServer(); +} +#endif + +static void ResetNode(INT32 node); + +// +// CL_ClearPlayer +// +// Clears the player data so that a future client can use this slot +// +void CL_ClearPlayer(INT32 playernum) +{ + if (players[playernum].mo) + P_RemoveMobj(players[playernum].mo); + memset(&players[playernum], 0, sizeof (player_t)); + memset(playeraddress[playernum], 0, sizeof(*playeraddress)); +} + +// +// CL_RemovePlayer +// +// Removes a player from the current game +// +void CL_RemovePlayer(INT32 playernum, kickreason_t reason) +{ + // Sanity check: exceptional cases (i.e. c-fails) can cause multiple + // kick commands to be issued for the same player. + if (!playeringame[playernum]) + return; + + if (server && !demoplayback && playernode[playernum] != UINT8_MAX) + { + INT32 node = playernode[playernum]; + playerpernode[node]--; + if (playerpernode[node] <= 0) + { + nodeingame[node] = false; + Net_CloseConnection(node); + ResetNode(node); + } + } + + if (gametyperules & GTR_TEAMFLAGS) + P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! + + // If in a special stage, redistribute the player's spheres across + // the remaining players. + if (G_IsSpecialStage(gamemap)) + { + INT32 i, count, sincrement, spheres, rincrement, rings; + + for (i = 0, count = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + count++; + } + + count--; + sincrement = spheres = players[playernum].spheres; + rincrement = rings = players[playernum].rings; + + if (count) + { + sincrement /= count; + rincrement /= count; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && i != playernum) + { + if (spheres < 2*sincrement) + { + P_GivePlayerSpheres(&players[i], spheres); + spheres = 0; + } + else + { + P_GivePlayerSpheres(&players[i], sincrement); + spheres -= sincrement; + } + + if (rings < 2*rincrement) + { + P_GivePlayerRings(&players[i], rings); + rings = 0; + } + else + { + P_GivePlayerRings(&players[i], rincrement); + rings -= rincrement; + } + } + } + } + + LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting + + // don't look through someone's view who isn't there + if (playernum == displayplayer) + { + // Call ViewpointSwitch hooks here. + // The viewpoint was forcibly changed. + LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); + displayplayer = consoleplayer; + } + + // Reset player data + CL_ClearPlayer(playernum); + + // remove avatar of player + playeringame[playernum] = false; + playernode[playernum] = UINT8_MAX; + while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) + doomcom->numslots--; + + // 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 + } + + LUA_InvalidatePlayer(&players[playernum]); + + if (G_TagGametype()) //Check if you still have a game. Location flexible. =P + P_CheckSurvivors(); + else if (gametyperules & GTR_RACE) + P_CheckRacers(); +} + +void CL_Reset(void) +{ + if (metalrecording) + G_StopMetalRecording(false); + if (metalplayback) + G_StopMetalDemo(); + if (demorecording) + G_CheckDemoStatus(); + + // reset client/server code + DEBFILE(va("\n-=-=-=-=-=-=-= Client reset =-=-=-=-=-=-=-\n\n")); + + if (servernode > 0 && servernode < MAXNETNODES) + { + nodeingame[(UINT8)servernode] = false; + Net_CloseConnection(servernode); + } + D_CloseConnection(); // netgame = false + multiplayer = false; + servernode = 0; + server = true; + doomcom->numnodes = 1; + doomcom->numslots = 1; + SV_StopServer(); + SV_ResetServer(); + + // make sure we don't leave any fileneeded gunk over from a failed join + FreeFileNeeded(); + fileneedednum = 0; + +#ifndef NONET + totalfilesrequestednum = 0; + totalfilesrequestedsize = 0; +#endif + firstconnectattempttime = 0; + serverisfull = false; + connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack + + // D_StartTitle should get done now, but the calling function will handle it +} + +#ifndef NONET +static void Command_GetPlayerNum(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + if (serverplayer == i) + CONS_Printf(M_GetText("num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); + else + CONS_Printf(M_GetText("\x82num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); + } +} + +SINT8 nametonum(const char *name) +{ + INT32 playernum, i; + + if (!strcmp(name, "0")) + return 0; + + playernum = (SINT8)atoi(name); + + if (playernum < 0 || playernum >= MAXPLAYERS) + return -1; + + if (playernum) + { + if (playeringame[playernum]) + return (SINT8)playernum; + else + return -1; + } + + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && !stricmp(player_names[i], name)) + return (SINT8)i; + + CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name); + + return -1; +} + +/** Lists all players and their player numbers. + * + * \sa Command_GetPlayerNum + */ +static void Command_Nodes(void) +{ + INT32 i; + size_t maxlen = 0; + const char *address; + + for (i = 0; i < MAXPLAYERS; i++) + { + const size_t plen = strlen(player_names[i]); + if (playeringame[i] && plen > maxlen) + maxlen = plen; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]); + + if (playernode[i] != UINT8_MAX) + { + CONS_Printf(" - node %.2d", playernode[i]); + if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL) + CONS_Printf(" - %s", address); + } + + if (IsPlayerAdmin(i)) + CONS_Printf(M_GetText(" (verified admin)")); + + if (players[i].spectator) + CONS_Printf(M_GetText(" (spectator)")); + + CONS_Printf("\n"); + } + } +} + +static void Command_Ban(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("Ban : ban and kick a player\n")); + return; + } + + if (!netgame) // Don't kick Tails in splitscreen! + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + + if (server || IsPlayerAdmin(consoleplayer)) + { + UINT8 buf[3 + MAX_REASONLENGTH]; + UINT8 *p = buf; + const SINT8 pn = nametonum(COM_Argv(1)); + const INT32 node = playernode[(INT32)pn]; + + if (pn == -1 || pn == 0) + return; + + WRITEUINT8(p, pn); + + if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now + { + CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); + WRITEUINT8(p, KICK_MSG_GO_AWAY); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + if (server) // only the server is allowed to do this right now + { + Ban_Add(COM_Argv(2)); + D_SaveBan(); // save the ban list + } + + if (COM_Argc() == 2) + { + WRITEUINT8(p, KICK_MSG_BANNED); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) + { + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); + } + + WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); + } + } + } + else + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); + +} + +static void Command_BanIP(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("banip : ban an ip address\n")); + return; + } + + if (server) // Only the server can use this, otherwise does nothing. + { + const char *address = (COM_Argv(1)); + const char *reason; + + if (COM_Argc() == 2) + reason = NULL; + else + reason = COM_Argv(2); + + + if (I_SetBanAddress && I_SetBanAddress(address, NULL)) + { + if (reason) + CONS_Printf("Banned IP address %s for: %s\n", address, reason); + else + CONS_Printf("Banned IP address %s\n", address); + + Ban_Add(reason); + D_SaveBan(); + } + else + { + return; + } + } +} + +static void Command_Kick(void) +{ + if (COM_Argc() < 2) + { + CONS_Printf(M_GetText("kick : kick a player\n")); + return; + } + + if (!netgame) // Don't kick Tails in splitscreen! + { + CONS_Printf(M_GetText("This only works in a netgame.\n")); + return; + } + + if (server || IsPlayerAdmin(consoleplayer)) + { + UINT8 buf[3 + MAX_REASONLENGTH]; + UINT8 *p = buf; + const SINT8 pn = nametonum(COM_Argv(1)); + + if (pn == -1 || pn == 0) + return; + + // Special case if we are trying to kick a player who is downloading the game state: + // trigger a timeout instead of kicking them, because a kick would only + // take effect after they have finished downloading + if (server && playernode[pn] != UINT8_MAX && sendingsavegame[playernode[pn]]) + { + Net_ConnectionTimeout(playernode[pn]); + return; + } + + WRITESINT8(p, pn); + + if (COM_Argc() == 2) + { + WRITEUINT8(p, KICK_MSG_GO_AWAY); + SendNetXCmd(XD_KICK, &buf, 2); + } + else + { + size_t i, j = COM_Argc(); + char message[MAX_REASONLENGTH]; + + //Steal from the motd code so you don't have to put the reason in quotes. + strlcpy(message, COM_Argv(2), sizeof message); + for (i = 3; i < j; i++) + { + strlcat(message, " ", sizeof message); + strlcat(message, COM_Argv(i), sizeof message); + } + + WRITEUINT8(p, KICK_MSG_CUSTOM_KICK); + WRITESTRINGN(p, message, MAX_REASONLENGTH); + SendNetXCmd(XD_KICK, &buf, p - buf); + } + } + else + CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); +} + +static void Command_ResendGamestate(void) +{ + SINT8 playernum; + + if (COM_Argc() == 1) + { + CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); + return; + } + else if (client) + { + CONS_Printf(M_GetText("Only the server can use this.\n")); + return; + } + + playernum = nametonum(COM_Argv(1)); + if (playernum == -1 || playernum == 0) + return; + + // Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + if (!HSendPacket(playernode[playernum], true, 0, 0)) + { + CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n")); + return; + } +} +#endif + +static void Got_KickCmd(UINT8 **p, INT32 playernum) +{ + INT32 pnum, msg; + char buf[3 + MAX_REASONLENGTH]; + char *reason = buf; + kickreason_t kickreason = KR_KICK; + boolean keepbody; + + pnum = READUINT8(*p); + msg = READUINT8(*p); + keepbody = (msg & KICK_MSG_KEEP_BODY) != 0; + msg &= ~KICK_MSG_KEEP_BODY; + + if (pnum == serverplayer && IsPlayerAdmin(playernum)) + { + CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n")); + + if (server) + COM_BufAddText("quit\n"); + + return; + } + + // Is playernum authorized to make this kick? + if (playernum != serverplayer && !IsPlayerAdmin(playernum) + && !(playernode[playernum] != UINT8_MAX && playerpernode[playernode[playernum]] == 2 + && nodetoplayer2[playernode[playernum]] == pnum)) + { + // We received a kick command from someone who isn't the + // server or admin, and who isn't in splitscreen removing + // player 2. Thus, it must be someone with a modified + // binary, trying to kick someone but without having + // authorization. + + // We deal with this by changing the kick reason to + // "consistency failure" and kicking the offending user + // instead. + + // Note: Splitscreen in netgames is broken because of + // this. Only the server has any idea of which players + // are using splitscreen on the same computer, so + // clients cannot always determine if a kick is + // legitimate. + + CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum); + + // In debug, print a longer message with more details. + // TODO Callum: Should we translate this? +/* + CONS_Debug(DBG_NETPLAY, + "So, you must be asking, why is this an illegal kick?\n" + "Well, let's take a look at the facts, shall we?\n" + "\n" + "playernum (this is the guy who did it), he's %d.\n" + "pnum (the guy he's trying to kick) is %d.\n" + "playernum's node is %d.\n" + "That node has %d players.\n" + "Player 2 on that node is %d.\n" + "pnum's node is %d.\n" + "That node has %d players.\n" + "Player 2 on that node is %d.\n" + "\n" + "If you think this is a bug, please report it, including all of the details above.\n", + playernum, pnum, + playernode[playernum], playerpernode[playernode[playernum]], + nodetoplayer2[playernode[playernum]], + playernode[pnum], playerpernode[playernode[pnum]], + nodetoplayer2[playernode[pnum]]); +*/ + pnum = playernum; + msg = KICK_MSG_CON_FAIL; + keepbody = true; + } + + //CONS_Printf("\x82%s ", player_names[pnum]); + + // If a verified admin banned someone, the server needs to know about it. + // If the playernum isn't zero (the server) then the server needs to record the ban. + if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) + { + if (I_Ban && !I_Ban(playernode[(INT32)pnum])) + CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); +#ifndef NONET + else + Ban_Add(reason); +#endif + } + + switch (msg) + { + case KICK_MSG_GO_AWAY: + if (!players[pnum].quittime) + HU_AddChatText(va("\x82*%s has been kicked (No reason given)", player_names[pnum]), false); + kickreason = KR_KICK; + break; + case KICK_MSG_PING_HIGH: + HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false); + kickreason = KR_PINGLIMIT; + break; + case KICK_MSG_CON_FAIL: + HU_AddChatText(va("\x82*%s left the game (Synch failure)", player_names[pnum]), false); + kickreason = KR_SYNCH; + + if (M_CheckParm("-consisdump")) // Helps debugging some problems + { + INT32 i; + + CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + CONS_Printf("-------------------------------------\n"); + CONS_Printf("Player %d: %s\n", i, player_names[i]); + CONS_Printf("Skin: %d\n", players[i].skin); + CONS_Printf("Color: %d\n", players[i].skincolor); + CONS_Printf("Speed: %d\n",players[i].speed>>FRACBITS); + if (players[i].mo) + { + if (!players[i].mo->skin) + CONS_Printf("Mobj skin: NULL!\n"); + else + CONS_Printf("Mobj skin: %s\n", ((skin_t *)players[i].mo->skin)->name); + CONS_Printf("Position: %d, %d, %d\n", players[i].mo->x, players[i].mo->y, players[i].mo->z); + if (!players[i].mo->state) + CONS_Printf("State: S_NULL\n"); + else + CONS_Printf("State: %d\n", (statenum_t)(players[i].mo->state-states)); + } + else + CONS_Printf("Mobj: NULL\n"); + CONS_Printf("-------------------------------------\n"); + } + } + break; + case KICK_MSG_TIMEOUT: + HU_AddChatText(va("\x82*%s left the game (Connection timeout)", player_names[pnum]), false); + kickreason = KR_TIMEOUT; + break; + case KICK_MSG_PLAYER_QUIT: + if (netgame && !players[pnum].quittime) // not splitscreen/bots or soulless body + HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false); + kickreason = KR_LEAVE; + break; + case KICK_MSG_BANNED: + HU_AddChatText(va("\x82*%s has been banned (No reason given)", player_names[pnum]), false); + kickreason = KR_BAN; + break; + case KICK_MSG_CUSTOM_KICK: + READSTRINGN(*p, reason, MAX_REASONLENGTH+1); + HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false); + kickreason = KR_KICK; + break; + case KICK_MSG_CUSTOM_BAN: + READSTRINGN(*p, reason, MAX_REASONLENGTH+1); + HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false); + kickreason = KR_BAN; + break; + } + + if (pnum == consoleplayer) + { + LUA_HookBool(false, HOOK(GameQuit)); +#ifdef DUMPCONSISTENCY + if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); +#endif + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + if (msg == KICK_MSG_CON_FAIL) + M_StartMessage(M_GetText("Server closed connection\n(synch failure)\nPress ESC\n"), NULL, MM_NOTHING); + else if (msg == KICK_MSG_PING_HIGH) + M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); + else if (msg == KICK_MSG_BANNED) + M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); + else if (msg == KICK_MSG_CUSTOM_KICK) + M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); + else if (msg == KICK_MSG_CUSTOM_BAN) + M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); + else + M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); + } + else if (keepbody) + { + if (server && !demoplayback && playernode[pnum] != UINT8_MAX) + { + INT32 node = playernode[pnum]; + playerpernode[node]--; + if (playerpernode[node] <= 0) + { + nodeingame[node] = false; + Net_CloseConnection(node); + ResetNode(node); + } + } + + playernode[pnum] = UINT8_MAX; + + players[pnum].quittime = 1; + } + else + CL_RemovePlayer(pnum, kickreason); +} + +static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; +consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); + +consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_joinnextround = CVAR_INIT ("joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); /// \todo not done +static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; +consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); +static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); +static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; +consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); + +static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; +consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); +consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +// max file size to send to a player (in kilobytes) +static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}}; +consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); +consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); + +// Speed of file downloading (in packets per tic) +static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; +consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); + +static void Got_AddPlayer(UINT8 **p, INT32 playernum); + +// called one time at init +void D_ClientServerInit(void) +{ + DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n", + VERSION/100, VERSION%100, SUBVERSION)); + +#ifndef NONET + COM_AddCommand("getplayernum", Command_GetPlayerNum); + COM_AddCommand("kick", Command_Kick); + COM_AddCommand("ban", Command_Ban); + COM_AddCommand("banip", Command_BanIP); + COM_AddCommand("clearbans", Command_ClearBans); + COM_AddCommand("showbanlist", Command_ShowBan); + COM_AddCommand("reloadbans", Command_ReloadBan); + COM_AddCommand("connect", Command_connect); + COM_AddCommand("nodes", Command_Nodes); + COM_AddCommand("resendgamestate", Command_ResendGamestate); +#ifdef PACKETDROP + COM_AddCommand("drop", Command_Drop); + COM_AddCommand("droprate", Command_Droprate); +#endif +#ifdef _DEBUG + COM_AddCommand("numnodes", Command_Numnodes); +#endif +#endif + + RegisterNetXCmd(XD_KICK, Got_KickCmd); + RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); +#ifndef NONET +#ifdef DUMPCONSISTENCY + CV_RegisterVar(&cv_dumpconsistency); +#endif + Ban_Load_File(false); +#endif + + gametic = 0; + localgametic = 0; + + // do not send anything before the real begin + SV_StopServer(); + SV_ResetServer(); + if (dedicated) + SV_SpawnServer(); +} + +static void ResetNode(INT32 node) +{ + nodeingame[node] = false; + nodewaiting[node] = 0; + + nettics[node] = gametic; + supposedtics[node] = gametic; + + nodetoplayer[node] = -1; + nodetoplayer2[node] = -1; + playerpernode[node] = 0; + + sendingsavegame[node] = false; + resendingsavegame[node] = false; + savegameresendcooldown[node] = 0; +} + +void SV_ResetServer(void) +{ + INT32 i; + + // +1 because this command will be executed in com_executebuffer in + // tryruntic so gametic will be incremented, anyway maketic > gametic + // is not an issue + + maketic = gametic + 1; + neededtic = maketic; + tictoclear = maketic; + + joindelay = 0; + + for (i = 0; i < MAXNETNODES; i++) + ResetNode(i); + + for (i = 0; i < MAXPLAYERS; i++) + { + LUA_InvalidatePlayer(&players[i]); + playeringame[i] = false; + playernode[i] = UINT8_MAX; + memset(playeraddress[i], 0, sizeof(*playeraddress)); + sprintf(player_names[i], "Player %d", i + 1); + adminplayers[i] = -1; // Populate the entire adminplayers array with -1. + } + + memset(player_name_changes, 0, sizeof player_name_changes); + + mynode = 0; + cl_packetmissed = false; + cl_redownloadinggamestate = false; + + if (dedicated) + { + nodeingame[0] = true; + serverplayer = 0; + } + else + serverplayer = consoleplayer; + + if (server) + servernode = 0; + + doomcom->numslots = 0; + + // clear server_context + memset(server_context, '-', 8); + + CV_RevertNetVars(); + + DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n"); +} + +static inline void SV_GenContext(void) +{ + UINT8 i; + // generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z + // (hopefully M_Random is initialized!! if not this will be awfully silly!) + for (i = 0; i < 8; i++) + { + const char a = M_RandomKey(26*2); + if (a < 26) // uppercase + server_context[i] = 'A'+a; + else // lowercase + server_context[i] = 'a'+(a-26); + } +} + +// +// D_QuitNetGame +// Called before quitting to leave a net game +// without hanging the other players +// +void D_QuitNetGame(void) +{ + mousegrabbedbylua = true; + I_UpdateMouseGrab(); + + if (!netgame || !netbuffer) + return; + + DEBFILE("===========================================================================\n" + " Quitting Game, closing connection\n" + "===========================================================================\n"); + + // abort send/receive of files + CloseNetFile(); + RemoveAllLuaFileTransfers(); + waitingforluafiletransfer = false; + waitingforluafilecommand = false; + + if (server) + { + INT32 i; + + netbuffer->packettype = PT_SERVERSHUTDOWN; + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i]) + HSendPacket(i, true, 0, 0); +#ifdef MASTERSERVER + if (serverrunning && ms_RoomId > 0) + UnregisterServer(); +#endif + } + else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) + { + netbuffer->packettype = PT_CLIENTQUIT; + HSendPacket(servernode, true, 0, 0); + } + + D_CloseConnection(); + ClearAdminPlayers(); + + DEBFILE("===========================================================================\n" + " Log finish\n" + "===========================================================================\n"); +#ifdef DEBUGFILE + if (debugfile) + { + fclose(debugfile); + debugfile = NULL; + } +#endif +} + +// Adds a node to the game (player will follow at map change or at savegame....) +static inline void SV_AddNode(INT32 node) +{ + nettics[node] = gametic; + supposedtics[node] = gametic; + // little hack because the server connects to itself and puts + // nodeingame when connected not here + if (node) + nodeingame[node] = true; +} + +// Xcmd XD_ADDPLAYER +static void Got_AddPlayer(UINT8 **p, INT32 playernum) +{ + INT16 node, newplayernum; + boolean splitscreenplayer; + boolean rejoined; + player_t *newplayer; + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + // protect against hacked/buggy client + CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); + if (server) + SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + return; + } + + node = READUINT8(*p); + newplayernum = READUINT8(*p); + splitscreenplayer = newplayernum & 0x80; + newplayernum &= ~0x80; + + rejoined = playeringame[newplayernum]; + + if (!rejoined) + { + // Clear player before joining, lest some things get set incorrectly + // HACK: don't do this for splitscreen, it relies on preset values + if (!splitscreen && !botingame) + CL_ClearPlayer(newplayernum); + playeringame[newplayernum] = true; + G_AddPlayer(newplayernum); + if (newplayernum+1 > doomcom->numslots) + doomcom->numslots = (INT16)(newplayernum+1); + + if (server && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + char *port = NULL; + if (address) // MI: fix msvcrt.dll!_mbscat crash? + { + strcpy(playeraddress[newplayernum], address); + port = strchr(playeraddress[newplayernum], ':'); + if (port) + *port = '\0'; + } + } + } + + newplayer = &players[newplayernum]; + + newplayer->jointime = 0; + newplayer->quittime = 0; + + READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); + + // the server is creating my player + if (node == mynode) + { + playernode[newplayernum] = 0; // for information only + if (!splitscreenplayer) + { + consoleplayer = newplayernum; + displayplayer = newplayernum; + secondarydisplayplayer = newplayernum; + DEBFILE("spawning me\n"); + ticcmd_oldangleturn[0] = newplayer->oldrelangleturn; + } + else + { + secondarydisplayplayer = newplayernum; + DEBFILE("spawning my brother\n"); + if (botingame) + newplayer->bot = 1; + ticcmd_oldangleturn[1] = newplayer->oldrelangleturn; + } + P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16)); + D_SendPlayerConfig(); + addedtogame = true; + + if (rejoined) + { + if (newplayer->mo) + { + newplayer->viewheight = 41*newplayer->height/48; + + if (newplayer->mo->eflags & MFE_VERTICALFLIP) + newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; + else + newplayer->viewz = newplayer->mo->z + newplayer->viewheight; + } + + // wake up the status bar + ST_Start(); + // wake up the heads up text + HU_Start(); + + if (camera.chase && !splitscreenplayer) + P_ResetCamera(newplayer, &camera); + if (camera2.chase && splitscreenplayer) + P_ResetCamera(newplayer, &camera2); + } + } + + if (netgame) + { + char joinmsg[256]; + + if (rejoined) + strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); + else + strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); + strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); + + // Merge join notification + IP to avoid clogging console/chat + if (server && cv_showjoinaddress.value && I_GetNodeAddress) + { + const char *address = I_GetNodeAddress(node); + if (address) + strcat(joinmsg, va(" (%s)", address)); + } + + HU_AddChatText(joinmsg, false); + } + + if (server && multiplayer && motd[0] != '\0') + COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); + + if (!rejoined) + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); +} + +static boolean SV_AddWaitingPlayers(const char *name, const char *name2) +{ + INT32 node, n, newplayer = false; + UINT8 buf[2 + MAXPLAYERNAME]; + UINT8 *p; + INT32 newplayernum; + + for (node = 0; node < MAXNETNODES; node++) + { + // splitscreen can allow 2 player in one node + for (; nodewaiting[node] > 0; nodewaiting[node]--) + { + newplayer = true; + + newplayernum = FindRejoinerNum(node); + if (newplayernum == -1) + { + // search for a free playernum + // we can't use playeringame since it is not updated here + for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) + { + if (playeringame[newplayernum]) + continue; + for (n = 0; n < MAXNETNODES; n++) + if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum) + break; + if (n == MAXNETNODES) + break; + } + } + + // should never happen since we check the playernum + // before accepting the join + I_Assert(newplayernum < MAXPLAYERS); + + playernode[newplayernum] = (UINT8)node; + + p = buf + 2; + buf[0] = (UINT8)node; + buf[1] = newplayernum; + if (playerpernode[node] < 1) + { + nodetoplayer[node] = newplayernum; + WRITESTRINGN(p, name, MAXPLAYERNAME); + } + else + { + nodetoplayer2[node] = newplayernum; + buf[1] |= 0x80; + WRITESTRINGN(p, name2, MAXPLAYERNAME); + } + playerpernode[node]++; + + SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); + + DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); + } + } + + return newplayer; +} + +void CL_AddSplitscreenPlayer(void) +{ + if (cl_mode == CL_CONNECTED) + CL_SendJoin(); +} + +void CL_RemoveSplitscreenPlayer(void) +{ + if (cl_mode != CL_CONNECTED) + return; + + SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT); +} + +// is there a game running +boolean Playing(void) +{ + return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); +} + +boolean SV_SpawnServer(void) +{ + if (demoplayback) + G_StopDemo(); // reset engine parameter + if (metalplayback) + G_StopMetalDemo(); + + if (!serverrunning) + { + CONS_Printf(M_GetText("Starting Server....\n")); + serverrunning = true; + SV_ResetServer(); + SV_GenContext(); + if (netgame && I_NetOpenSocket) + { + I_NetOpenSocket(); +#ifdef MASTERSERVER + if (ms_RoomId > 0) + RegisterServer(); +#endif + } + + // non dedicated server just connect to itself + if (!dedicated) + CL_ConnectToServer(); + else doomcom->numslots = 1; + } + + return SV_AddWaitingPlayers(cv_playername.zstring, cv_playername2.zstring); +} + +void SV_StopServer(void) +{ + tic_t i; + + if (gamestate == GS_INTERMISSION) + Y_EndIntermission(); + gamestate = wipegamestate = GS_NULL; + + localtextcmd[0] = 0; + localtextcmd2[0] = 0; + + for (i = firstticstosend; i < firstticstosend + BACKUPTICS; i++) + D_Clearticcmd(i); + + consoleplayer = 0; + cl_mode = CL_SEARCHING; + maketic = gametic+1; + neededtic = maketic; + serverrunning = false; +} + +// called at singleplayer start and stopdemo +void SV_StartSinglePlayerServer(void) +{ + server = true; + netgame = false; + multiplayer = false; + G_SetGametype(GT_COOP); + + // no more tic the game with this settings! + SV_StopServer(); + + if (splitscreen) + multiplayer = true; +} + +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); + Net_CloseConnection(node); +} + +// used at txtcmds received to check packetsize bound +static size_t TotalTextCmdPerTic(tic_t tic) +{ + INT32 i; + size_t total = 1; // num of textcmds in the tic (ntextcmd byte) + + for (i = 0; i < MAXPLAYERS; i++) + { + UINT8 *textcmd = D_GetExistingTextcmd(tic, i); + if ((!i || playeringame[i]) && textcmd) + total += 2 + textcmd[0]; // "+2" for size and playernum + } + + return total; +} + +static const char * +ConnectionRefused (SINT8 node, INT32 rejoinernum) +{ + clientconfig_pak *cc = &netbuffer->u.clientcfg; + + boolean rejoining = (rejoinernum != -1); + + if (!node)/* server connecting to itself */ + return NULL; + + if ( + cc->modversion != MODVERSION || + strncmp(cc->application, SRB2APPLICATION, + sizeof cc->application) + ){ + return/* this is probably client's fault */ + "Incompatible."; + } + else if (bannednode && bannednode[node]) + { + return + "You have been banned\n" + "from the server."; + } + else if (cc->localplayers != 1) + { + return + "Wrong player count."; + } + + if (!rejoining) + { + if (!cv_allownewplayer.value) + { + return + "The server is not accepting\n" + "joins for the moment."; + } + else if (D_NumPlayers() >= cv_maxplayers.value) + { + return va( + "Maximum players reached: %d", + cv_maxplayers.value); + } + } + + if (luafiletransfers) + { + return + "The serveris broadcasting a file\n" + "requested by a Lua script.\n" + "Please wait a bit and then\n" + "try rejoining."; + } + + if (netgame) + { + const tic_t th = 2 * cv_joindelay.value * TICRATE; + + if (joindelay > th) + { + return va( + "Too many people are connecting.\n" + "Please wait %d seconds and then\n" + "try rejoining.", + (joindelay - th) / TICRATE); + } + } + + return NULL; +} + +/** Called when a PT_CLIENTJOIN packet is received + * + * \param node The packet sender + * + */ +static void HandleConnect(SINT8 node) +{ + char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; + INT32 rejoinernum; + INT32 i; + const char *refuse; + + rejoinernum = FindRejoinerNum(node); + + refuse = ConnectionRefused(node, rejoinernum); + + if (refuse) + SV_SendRefuse(node, refuse); + else + { +#ifndef NONET + boolean newnode = false; +#endif + + for (i = 0; i < netbuffer->u.clientcfg.localplayers - playerpernode[node]; i++) + { + strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); + if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) + { + SV_SendRefuse(node, "Bad player name"); + return; + } + } + + // client authorised to join + nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]); + if (!nodeingame[node]) + { + gamestate_t backupstate = gamestate; +#ifndef NONET + newnode = true; +#endif + SV_AddNode(node); + + if (cv_joinnextround.value && gameaction == ga_nothing) + G_SetGamestate(GS_WAITINGPLAYERS); + if (!SV_SendServerConfig(node)) + { + G_SetGamestate(backupstate); + /// \note Shouldn't SV_SendRefuse be called before ResetNode? + ResetNode(node); + SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); + /// \todo fix this !!! + return; // restart the while + } + //if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc? + // SV_SendPlayerConfigs(node); // send bare minimum player info + G_SetGamestate(backupstate); + DEBFILE("new node joined\n"); + } +#ifndef NONET + if (nodewaiting[node]) + { + if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) + { + SV_SendSaveGame(node, false); // send a complete game state + DEBFILE("send savegame\n"); + } + SV_AddWaitingPlayers(names[0], names[1]); + joindelay += cv_joindelay.value * TICRATE; + player_joining = true; + } +#endif + } +} + +/** Called when a PT_SERVERSHUTDOWN packet is received + * + * \param node The packet sender (should be the server) + * + */ +static void HandleShutdown(SINT8 node) +{ + (void)node; + LUA_HookBool(false, HOOK(GameQuit)); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); +} + +/** Called when a PT_NODETIMEOUT packet is received + * + * \param node The packet sender (should be the server) + * + */ +static void HandleTimeout(SINT8 node) +{ + (void)node; + LUA_HookBool(false, HOOK(GameQuit)); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); +} + +#ifndef NONET +/** Called when a PT_SERVERINFO packet is received + * + * \param node The packet sender + * \note What happens if the packet comes from a client or something like that? + * + */ +static void HandleServerInfo(SINT8 node) +{ + // compute ping in ms + const tic_t ticnow = I_GetTime(); + const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time); + const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE; + netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff); + netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; + netbuffer->u.serverinfo.application + [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; + netbuffer->u.serverinfo.gametypename + [sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0'; + + SL_InsertServer(&netbuffer->u.serverinfo, node); +} +#endif + +static void PT_WillResendGamestate(void) +{ +#ifndef NONET + char tmpsave[256]; + + if (server || cl_redownloadinggamestate) + return; + + // Send back a PT_CANRECEIVEGAMESTATE packet to the server + // so they know they can start sending the game state + netbuffer->packettype = PT_CANRECEIVEGAMESTATE; + if (!HSendPacket(servernode, true, 0, 0)) + return; + + CONS_Printf(M_GetText("Reloading game state...\n")); + + sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); + + // Don't get a corrupt savegame error because tmpsave already exists + if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) + I_Error("Can't delete %s\n", tmpsave); + + CL_PrepareDownloadSaveGame(tmpsave); + + cl_redownloadinggamestate = true; +#endif +} + +static void PT_CanReceiveGamestate(SINT8 node) +{ +#ifndef NONET + if (client || sendingsavegame[node]) + return; + + CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[nodetoplayer[node]]); + + SV_SendSaveGame(node, true); // Resend a complete game state + resendingsavegame[node] = true; +#else + (void)node; +#endif +} + +/** Handles a packet received from a node that isn't in game + * + * \param node The packet sender + * \todo Choose a better name, as the packet can also come from the server apparently? + * \sa HandlePacketFromPlayer + * \sa GetPackets + * + */ +static void HandlePacketFromAwayNode(SINT8 node) +{ + if (node != servernode) + DEBFILE(va("Received packet from unknown host %d\n", node)); + +// macro for packets that should only be sent by the server +// if it is NOT from the server, bail out and close the connection! +#define SERVERONLY \ + if (node != servernode) \ + { \ + Net_CloseConnection(node); \ + break; \ + } + switch (netbuffer->packettype) + { + case PT_ASKINFOVIAMS: + Net_CloseConnection(node); + break; + + case PT_TELLFILESNEEDED: + if (server && serverrunning) + { + UINT8 *p; + INT32 firstfile = netbuffer->u.filesneedednum; + + netbuffer->packettype = PT_MOREFILESNEEDED; + netbuffer->u.filesneededcfg.first = firstfile; + netbuffer->u.filesneededcfg.more = 0; + + p = PutFileNeeded(firstfile); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); + } + else // Shouldn't get this if you aren't the server...? + Net_CloseConnection(node); + break; + + case PT_MOREFILESNEEDED: + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) + { + D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); + if (!netbuffer->u.filesneededcfg.more) + cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list + } + break; + + case PT_ASKINFO: + if (server && serverrunning) + { + SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); + SV_SendPlayerInfo(node); // Send extra info + } + Net_CloseConnection(node); + break; + + case PT_SERVERREFUSE: // Negative response of client join request + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + if (cl_mode == CL_WAITJOINRESPONSE) + { + // Save the reason so it can be displayed after quitting the netgame + char *reason = strdup(netbuffer->u.serverrefuse.reason); + if (!reason) + I_Error("Out of memory!\n"); + + 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. + cl_mode = CL_ABORTED; + } + break; + + case PT_SERVERCFG: // Positive response of client join request + { + if (server && serverrunning && node != servernode) + { // but wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + /// \note how would this happen? and is it doing the right thing if it does? + if (cl_mode != CL_WAITJOINRESPONSE) + break; + + if (client) + { + maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); + G_SetGametype(netbuffer->u.servercfg.gametype); + modifiedgame = netbuffer->u.servercfg.modifiedgame; + memcpy(server_context, netbuffer->u.servercfg.server_context, 8); + } + + nodeingame[(UINT8)servernode] = true; + serverplayer = netbuffer->u.servercfg.serverplayer; + doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); + mynode = netbuffer->u.servercfg.clientnode; + if (serverplayer >= 0) + playernode[(UINT8)serverplayer] = servernode; + + if (netgame) +#ifndef NONET + CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); +#else + CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n")); +#endif + DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); + +#ifndef NONET + /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? + /// Shouldn't them be downloaded even at intermission time? + /// Also, according to HandleConnect, the server will send the savegame even during intermission... + if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || + netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) + cl_mode = CL_DOWNLOADSAVEGAME; + else +#endif + cl_mode = CL_CONNECTED; + break; + } + + // Handled in d_netfil.c + case PT_FILEFRAGMENT: + if (server) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + PT_FileFragment(); + break; + + case PT_FILEACK: + if (server) + PT_FileAck(); + break; + + case PT_FILERECEIVED: + if (server) + PT_FileReceived(); + break; + + case PT_REQUESTFILE: + if (server) + { + if (!cv_downloading.value || !PT_RequestFile(node)) + Net_CloseConnection(node); // close connection if one of the requested files could not be sent, or you disabled downloading anyway + } + else + Net_CloseConnection(node); // nope + break; + + case PT_NODETIMEOUT: + case PT_CLIENTQUIT: + if (server) + Net_CloseConnection(node); + break; + + case PT_CLIENTCMD: + break; // This is not an "unknown packet" + + case PT_SERVERTICS: + // Do not remove my own server (we have just get a out of order packet) + if (node == servernode) + break; + /* FALLTHRU */ + + default: + DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); + Net_CloseConnection(node); + break; // Ignore it + + } +#undef SERVERONLY +} + +/** Handles a packet received from a node that is in game + * + * \param node The packet sender + * \todo Choose a better name + * \sa HandlePacketFromAwayNode + * \sa GetPackets + * + */ +static void HandlePacketFromPlayer(SINT8 node) +{ + INT32 netconsole; + tic_t realend, realstart; + UINT8 *pak, *txtpak, numtxtpak; +#ifndef NOMD5 + UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ +#endif + + txtpak = NULL; + + if (dedicated && node == 0) + netconsole = 0; + else + netconsole = nodetoplayer[node]; +#ifdef PARANOIA + if (netconsole >= MAXPLAYERS) + I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); +#endif + + switch (netbuffer->packettype) + { +// -------------------------------------------- SERVER RECEIVE ---------- + case PT_CLIENTCMD: + case PT_CLIENT2CMD: + case PT_CLIENTMIS: + case PT_CLIENT2MIS: + case PT_NODEKEEPALIVE: + case PT_NODEKEEPALIVEMIS: + if (client) + break; + + // To save bytes, only the low byte of tic numbers are sent + // Use ExpandTics to figure out what the rest of the bytes are + realstart = ExpandTics(netbuffer->u.clientpak.client_tic, node); + realend = ExpandTics(netbuffer->u.clientpak.resendfrom, node); + + if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS + || netbuffer->packettype == PT_NODEKEEPALIVEMIS + || supposedtics[node] < realend) + { + supposedtics[node] = realend; + } + // Discard out of order packet + if (nettics[node] > realend) + { + DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node])); + break; + } + + // Update the nettics + nettics[node] = realend; + + // Don't do anything for packets of type NODEKEEPALIVE? + if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE + || netbuffer->packettype == PT_NODEKEEPALIVEMIS) + break; + + // As long as clients send valid ticcmds, the server can keep running, so reset the timeout + /// \todo Use a separate cvar for that kind of timeout? + freezetimeout[node] = I_GetTime() + connectiontimeout; + + // Copy ticcmd + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); + + // Check ticcmd for "speed hacks" + if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE + || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole); + //D_Clearticcmd(k); + + SendKick(netconsole, KICK_MSG_CON_FAIL); + break; + } + + // Splitscreen cmd + if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) + && nodetoplayer2[node] >= 0) + G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]], + &netbuffer->u.client2pak.cmd2, 1); + + // Check player consistancy during the level + if (realstart <= gametic && realstart + BACKUPTICS - 1 > gametic && gamestate == GS_LEVEL + && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy) +#ifndef NONET + && !SV_ResendingSavegameToAnyone() +#endif + && !resendingsavegame[node] && savegameresendcooldown[node] <= I_GetTime()) + { + if (cv_resynchattempts.value) + { + // Tell the client we are about to resend them the gamestate + netbuffer->packettype = PT_WILLRESENDGAMESTATE; + HSendPacket(node, true, 0, 0); + + resendingsavegame[node] = true; + + if (cv_blamecfail.value) + CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), + netconsole+1, player_names[netconsole], + consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy)); + DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + break; + } + else + { + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", + netconsole, realstart, consistancy[realstart%BACKUPTICS], + SHORT(netbuffer->u.clientpak.consistancy))); + break; + } + } + break; + case PT_TEXTCMD2: // splitscreen special + netconsole = nodetoplayer2[node]; + /* FALLTHRU */ + case PT_TEXTCMD: + if (client) + break; + + if (netconsole < 0 || netconsole >= MAXPLAYERS) + Net_UnAcknowledgePacket(node); + else + { + size_t j; + tic_t tic = maketic; + UINT8 *textcmd; + + // ignore if the textcmd has a reported size of zero + // this shouldn't be sent at all + if (!netbuffer->u.textcmd[0]) + { + DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", + node, netconsole)); + Net_UnAcknowledgePacket(node); + break; + } + + // ignore if the textcmd size var is actually larger than it should be + // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength + if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) + { + DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", + netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), + node, netconsole)); + Net_UnAcknowledgePacket(node); + break; + } + + // check if tic that we are making isn't too large else we cannot send it :( + // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time + j = software_MAXPACKETLENGTH + - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE + + (doomcom->numslots+1)*sizeof(ticcmd_t)); + + // search a tic that have enougth space in the ticcmd + while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), + (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) + && tic < firstticstosend + BACKUPTICS) + tic++; + + if (tic >= firstticstosend + BACKUPTICS) + { + DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " + "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), + maketic, firstticstosend, node, netconsole)); + Net_UnAcknowledgePacket(node); + break; + } + + // Make sure we have a buffer + if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); + + DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", + tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); + + M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); + textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; + } + break; + case PT_LOGIN: + if (client) + break; + +#ifndef NOMD5 + if (doomcom->datalength < 16)/* ignore partial sends */ + break; + + if (!adminpasswordset) + { + CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); + break; + } + + // Do the final pass to compare with the sent md5 + D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); + + if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) + { + CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); + COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately + } + else + CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); +#endif + break; + case PT_NODETIMEOUT: + case PT_CLIENTQUIT: + if (client) + break; + + // nodeingame will be put false in the execution of kick command + // this allow to send some packets to the quitting client to have their ack back + nodewaiting[node] = 0; + if (netconsole != -1 && playeringame[netconsole]) + { + UINT8 kickmsg; + + if (netbuffer->packettype == PT_NODETIMEOUT) + kickmsg = KICK_MSG_TIMEOUT; + else + kickmsg = KICK_MSG_PLAYER_QUIT; + kickmsg |= KICK_MSG_KEEP_BODY; + + SendKick(netconsole, kickmsg); + nodetoplayer[node] = -1; + + if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0 + && playeringame[(UINT8)nodetoplayer2[node]]) + { + SendKick(nodetoplayer2[node], kickmsg); + nodetoplayer2[node] = -1; + } + } + Net_CloseConnection(node); + nodeingame[node] = false; + break; + case PT_CANRECEIVEGAMESTATE: + PT_CanReceiveGamestate(node); + break; + case PT_ASKLUAFILE: + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) + AddLuaFileToSendQueue(node, luafiletransfers->realfilename); + break; + case PT_HASLUAFILE: + if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) + SV_HandleLuaFileSent(node); + break; + case PT_RECEIVEDGAMESTATE: + sendingsavegame[node] = false; + resendingsavegame[node] = false; + savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE; + break; +// -------------------------------------------- CLIENT RECEIVE ---------- + case PT_SERVERTICS: + // Only accept PT_SERVERTICS from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + break; + } + + realstart = netbuffer->u.serverpak.starttic; + realend = realstart + netbuffer->u.serverpak.numtics; + + if (!txtpak) + txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots + * netbuffer->u.serverpak.numtics]; + + if (realend > gametic + CLIENTBACKUPTICS) + realend = gametic + CLIENTBACKUPTICS; + cl_packetmissed = realstart > neededtic; + + if (realstart <= neededtic && realend > neededtic) + { + tic_t i, j; + pak = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realstart; i < realend; i++) + { + // clear first + D_Clearticcmd(i); + + // copy the tics + pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, + netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); + + // copy the textcmds + numtxtpak = *txtpak++; + for (j = 0; j < numtxtpak; j++) + { + INT32 k = *txtpak++; // playernum + const size_t txtsize = txtpak[0]+1; + + if (i >= gametic) // Don't copy old net commands + M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize); + txtpak += txtsize; + } + } + + neededtic = realend; + } + else + { + DEBFILE(va("frame not in bound: %u\n", neededtic)); + /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart) + I_Error("Received an out of order PT_SERVERTICS packet!\n" + "Got tics %d-%d, needed tic %d\n\n" + "Please report this crash on the Master Board,\n" + "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/ + } + break; + case PT_PING: + // Only accept PT_PING from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + break; + } + + //Update client ping table from the server. + if (client) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; + + servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; + } + + break; + case PT_SERVERCFG: + break; + case PT_FILEFRAGMENT: + // Only accept PT_FILEFRAGMENT from the server. + if (node != servernode) + { + CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); + if (server) + SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); + break; + } + if (client) + PT_FileFragment(); + break; + case PT_FILEACK: + if (server) + PT_FileAck(); + break; + case PT_FILERECEIVED: + if (server) + PT_FileReceived(); + break; + case PT_WILLRESENDGAMESTATE: + PT_WillResendGamestate(); + break; + case PT_SENDINGLUAFILE: + if (client) + CL_PrepareDownloadLuaFile(); + break; + default: + DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", + netbuffer->packettype, node)); + } // end switch +} + +/** Handles all received packets, if any + * + * \todo Add details to this description (lol) + * + */ +static void GetPackets(void) +{ + SINT8 node; // The packet sender + + player_joining = false; + + while (HGetPacket()) + { + node = (SINT8)doomcom->remotenode; + + if (netbuffer->packettype == PT_CLIENTJOIN && server) + { + HandleConnect(node); + continue; + } + if (node == servernode && client && cl_mode != CL_SEARCHING) + { + if (netbuffer->packettype == PT_SERVERSHUTDOWN) + { + HandleShutdown(node); + continue; + } + if (netbuffer->packettype == PT_NODETIMEOUT) + { + HandleTimeout(node); + continue; + } + } + +#ifndef NONET + if (netbuffer->packettype == PT_SERVERINFO) + { + HandleServerInfo(node); + continue; + } +#endif + + if (netbuffer->packettype == PT_PLAYERINFO) + continue; // We do nothing with PLAYERINFO, that's for the MS browser. + + // Packet received from someone already playing + if (nodeingame[node]) + HandlePacketFromPlayer(node); + // Packet received from someone not playing + else + HandlePacketFromAwayNode(node); + } +} + +// +// NetUpdate +// Builds ticcmds for console player, +// sends out a packet +// +// no more use random generator, because at very first tic isn't yet synchronized +// Note: It is called consistAncy on purpose. +// +static INT16 Consistancy(void) +{ + INT32 i; + UINT32 ret = 0; +#ifdef MOBJCONSISTANCY + thinker_t *th; + mobj_t *mo; +#endif + + DEBFILE(va("TIC %u ", gametic)); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + ret ^= 0xCCCC; + else if (!players[i].mo); + else + { + ret += players[i].mo->x; + ret -= players[i].mo->y; + ret += players[i].powers[pw_shield]; + ret *= i+1; + } + } + // I give up + // Coop desynching enemies is painful + if (!G_PlatformGametype()) + ret += P_GetRandSeed(); + +#ifdef MOBJCONSISTANCY + if (gamestate == GS_LEVEL) + { + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + { + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) + { + ret -= mo->type; + ret += mo->x; + ret -= mo->y; + ret += mo->z; + ret -= mo->momx; + ret += mo->momy; + ret -= mo->momz; + ret += mo->angle; + ret -= mo->flags; + ret += mo->flags2; + ret -= mo->eflags; + if (mo->target) + { + ret += mo->target->type; + ret -= mo->target->x; + ret += mo->target->y; + ret -= mo->target->z; + ret += mo->target->momx; + ret -= mo->target->momy; + ret += mo->target->momz; + ret -= mo->target->angle; + ret += mo->target->flags; + ret -= mo->target->flags2; + ret += mo->target->eflags; + ret -= mo->target->state - states; + ret += mo->target->tics; + ret -= mo->target->sprite; + ret += mo->target->frame; + } + else + ret ^= 0x3333; + if (mo->tracer && mo->tracer->type != MT_OVERLAY) + { + ret += mo->tracer->type; + ret -= mo->tracer->x; + ret += mo->tracer->y; + ret -= mo->tracer->z; + ret += mo->tracer->momx; + ret -= mo->tracer->momy; + ret += mo->tracer->momz; + ret -= mo->tracer->angle; + ret += mo->tracer->flags; + ret -= mo->tracer->flags2; + ret += mo->tracer->eflags; + ret -= mo->tracer->state - states; + ret += mo->tracer->tics; + ret -= mo->tracer->sprite; + ret += mo->tracer->frame; + } + else + ret ^= 0xAAAA; + ret -= mo->state - states; + ret += mo->tics; + ret -= mo->sprite; + ret += mo->frame; + } + } + } +#endif + + DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF))); + + return (INT16)(ret & 0xFFFF); +} + +// send the client packet to the server +static void CL_SendClientCmd(void) +{ + size_t packetsize = 0; + + netbuffer->packettype = PT_CLIENTCMD; + + if (cl_packetmissed) + netbuffer->packettype++; + netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX); + netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX); + + if (gamestate == GS_WAITINGPLAYERS) + { + // Send PT_NODEKEEPALIVE packet + netbuffer->packettype += 4; + packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); + HSendPacket(servernode, false, 0, packetsize); + } + else if (gamestate != GS_NULL && (addedtogame || dedicated)) + { + G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); + netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); + + // Send a special packet with 2 cmd for splitscreen + if (splitscreen || botingame) + { + netbuffer->packettype += 2; + G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1); + packetsize = sizeof (client2cmd_pak); + } + else + packetsize = sizeof (clientcmd_pak); + + HSendPacket(servernode, false, 0, packetsize); + } + + if (cl_mode == CL_CONNECTED || dedicated) + { + // Send extra data if needed + if (localtextcmd[0]) + { + netbuffer->packettype = PT_TEXTCMD; + M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... + localtextcmd[0] = 0; + } + + // Send extra data if needed for player 2 (splitscreen) + if (localtextcmd2[0]) + { + netbuffer->packettype = PT_TEXTCMD2; + M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); + // All extra data have been sent + if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... + localtextcmd2[0] = 0; + } + } +} + +// send the server packet +// send tic from firstticstosend to maketic-1 +static void SV_SendTics(void) +{ + tic_t realfirsttic, lasttictosend, i; + UINT32 n; + INT32 j; + size_t packsize; + UINT8 *bufpos; + UINT8 *ntextcmd; + + // send to all client but not to me + // for each node create a packet with x tics and send it + // x is computed using supposedtics[n], max packet size and maketic + for (n = 1; n < MAXNETNODES; n++) + if (nodeingame[n]) + { + // assert supposedtics[n]>=nettics[n] + realfirsttic = supposedtics[n]; + lasttictosend = min(maketic, nettics[n] + CLIENTBACKUPTICS); + + if (realfirsttic >= lasttictosend) + { + // well we have sent all tics we will so use extrabandwidth + // to resent packet that are supposed lost (this is necessary since lost + // packet detection work when we have received packet with firsttic > neededtic + // (getpacket servertics case) + DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", + n, maketic, supposedtics[n], nettics[n])); + realfirsttic = nettics[n]; + if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) + // all tic are ok + continue; + DEBFILE(va("Sent %d anyway\n", realfirsttic)); + } + if (realfirsttic < firstticstosend) + realfirsttic = firstticstosend; + + // compute the length of the packet and cut it if too large + packsize = BASESERVERTICSSIZE; + for (i = realfirsttic; i < lasttictosend; i++) + { + packsize += sizeof (ticcmd_t) * doomcom->numslots; + packsize += TotalTextCmdPerTic(i); + + if (packsize > software_MAXPACKETLENGTH) + { + DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", + sizeu1(packsize), i, realfirsttic, lasttictosend)); + lasttictosend = i; + + // too bad: too much player have send extradata and there is too + // much data in one tic. + // To avoid it put the data on the next tic. (see getpacket + // textcmd case) but when numplayer changes the computation can be different + if (lasttictosend == realfirsttic) + { + if (packsize > MAXPACKETLENGTH) + I_Error("Too many players: can't send %s data for %d players to node %d\n" + "Well sorry nobody is perfect....\n", + sizeu1(packsize), doomcom->numslots, n); + else + { + lasttictosend++; // send it anyway! + DEBFILE("sending it anyway\n"); + } + } + break; + } + } + + // Send the tics + netbuffer->packettype = PT_SERVERTICS; + netbuffer->u.serverpak.starttic = realfirsttic; + netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); + netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); + bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; + + for (i = realfirsttic; i < lasttictosend; i++) + { + bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); + } + + // add textcmds + for (i = realfirsttic; i < lasttictosend; i++) + { + ntextcmd = bufpos++; + *ntextcmd = 0; + for (j = 0; j < MAXPLAYERS; j++) + { + UINT8 *textcmd = D_GetExistingTextcmd(i, j); + INT32 size = textcmd ? textcmd[0] : 0; + + if ((!j || playeringame[j]) && size) + { + (*ntextcmd)++; + WRITEUINT8(bufpos, j); + M_Memcpy(bufpos, textcmd, size + 1); + bufpos += size + 1; + } + } + } + packsize = bufpos - (UINT8 *)&(netbuffer->u); + + HSendPacket(n, false, 0, packsize); + // when tic are too large, only one tic is sent so don't go backward! + if (lasttictosend-doomcom->extratics > realfirsttic) + supposedtics[n] = lasttictosend-doomcom->extratics; + else + supposedtics[n] = lasttictosend; + if (supposedtics[n] < nettics[n]) supposedtics[n] = nettics[n]; + } + // node 0 is me! + supposedtics[0] = maketic; +} + +// +// TryRunTics +// +static void Local_Maketic(INT32 realtics) +{ + I_OsPolling(); // I_Getevent + D_ProcessEvents(); // menu responder, cons responder, + // game responder calls HU_Responder, AM_Responder, + // and G_MapEventsToControls + if (!dedicated) rendergametic = gametic; + // translate inputs (keyboard/mouse/gamepad) into game controls + G_BuildTiccmd(&localcmds, realtics, 1); + if (splitscreen || botingame) + G_BuildTiccmd(&localcmds2, realtics, 2); + + localcmds.angleturn |= TICCMD_RECEIVED; + localcmds2.angleturn |= TICCMD_RECEIVED; +} + +// create missed tic +static void SV_Maketic(void) +{ + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + // We didn't receive this tic + if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) + { + ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; + ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; + + if (players[i].quittime) + { + // Copy the angle/aiming from the previous tic + // and empty the other inputs + memset(ticcmd, 0, sizeof(netcmds[0][0])); + ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; + ticcmd->aiming = prevticcmd->aiming; + } + else + { + DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); + // Copy the input from the previous tic + *ticcmd = *prevticcmd; + ticcmd->angleturn &= ~TICCMD_RECEIVED; + } + } + } + + // all tic are now proceed make the next + maketic++; +} + +boolean TryRunTics(tic_t realtics) +{ + boolean ticking; + + // the machine has lagged but it is not so bad + if (realtics > TICRATE/7) // FIXME: consistency failure!! + { + if (server) + realtics = 1; + else + realtics = TICRATE/7; + } + + if (singletics) + realtics = 1; + + if (realtics >= 1) + { + COM_BufTicker(); + if (mapchangepending) + D_MapChange(-1, 0, ultimatemode, false, 2, false, fromlevelselect); // finish the map change + } + + NetUpdate(); + + if (demoplayback) + { + neededtic = gametic + realtics; + // start a game after a demo + maketic += realtics; + firstticstosend = maketic; + tictoclear = firstticstosend; + } + + GetPackets(); + +#ifdef DEBUGFILE + if (debugfile && (realtics || neededtic > gametic)) + { + //SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5. + //Shut up stupid warning! + fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n", + realtics, neededtic, gametic, debugload); + debugload = 100000; + } +#endif + + ticking = neededtic > gametic; + + if (ticking) + { + if (realtics) + hu_stopped = false; + } + + if (player_joining) + { + if (realtics) + hu_stopped = true; + return false; + } + + if (ticking) + { + if (advancedemo) + { + if (timedemo_quit) + COM_ImmedExecute("quit"); + else + D_StartTitle(); + } + else + // run the count * tics + while (neededtic > gametic) + { + boolean update_stats = !(paused || P_AutoPause()); + + DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); + + if (update_stats) + PS_START_TIMING(ps_tictime); + + G_Ticker((gametic % NEWTICRATERATIO) == 0); + ExtraDataTicker(); + gametic++; + consistancy[gametic%BACKUPTICS] = Consistancy(); + + if (update_stats) + { + PS_STOP_TIMING(ps_tictime); + PS_UpdateTickStats(); + } + + // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. + if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) + break; + } + } + else + { + if (realtics) + hu_stopped = true; + } + + return ticking; +} + +/* +Ping Update except better: +We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. +If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. +*/ + +static INT32 pingtimeout[MAXPLAYERS]; + +static inline void PingUpdate(void) +{ + INT32 i; + boolean laggers[MAXPLAYERS]; + UINT8 numlaggers = 0; + memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); + + netbuffer->packettype = PT_PING; + + //check for ping limit breakage. + if (cv_maxping.value) + { + for (i = 1; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].quittime + && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) + { + if (players[i].jointime > 30 * TICRATE) + laggers[i] = true; + numlaggers++; + } + else + pingtimeout[i] = 0; + } + + //kick lagging players... unless everyone but the server's ping sucks. + //in that case, it is probably the server's fault. + if (numlaggers < D_NumPlayers() - 1) + { + for (i = 1; i < MAXPLAYERS; i++) + { + if (playeringame[i] && laggers[i]) + { + pingtimeout[i]++; + // ok your net has been bad for too long, you deserve to die. + if (pingtimeout[i] > cv_pingtimeout.value) + { + pingtimeout[i] = 0; + SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); + } + } + /* + you aren't lagging, + but you aren't free yet. + In case you'll keep spiking, + we just make the timer go back down. (Very unstable net must still get kicked). + */ + else + pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); + } + } + } + + //make the ping packet and clear server data for next one + for (i = 0; i < MAXPLAYERS; i++) + { + netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; + //server takes a snapshot of the real ping for display. + //otherwise, pings fluctuate a lot and would be odd to look at. + playerpingtable[i] = realpingtable[i] / pingmeasurecount; + realpingtable[i] = 0; //Reset each as we go. + } + + // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked. + netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value; + + //send out our ping packets + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i]) + HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); + + pingmeasurecount = 1; //Reset count +} + +void NetUpdate(void) +{ + static tic_t gametime = 0; + static tic_t resptime = 0; + tic_t nowtime; + INT32 i; + INT32 realtics; + + nowtime = I_GetTime(); + realtics = nowtime - gametime; + + if (realtics <= 0) // nothing new to update + return; + if (realtics > 5) + { + if (server) + realtics = 1; + else + realtics = 5; + } + + gametime = nowtime; + + if (server) + { + if (netgame && !(gametime % 35)) // update once per second. + PingUpdate(); + // update node latency values so we can take an average later. + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i] && playernode[i] != UINT8_MAX) + realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); + pingmeasurecount++; + } + + if (client) + maketic = neededtic; + + Local_Maketic(realtics); // make local tic, and call menu? + + if (server) + CL_SendClientCmd(); // send it + + GetPackets(); // get packet from client or from server + + // 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) + { +#ifndef NONET + // If the client just finished redownloading the game state, load it + if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) + CL_ReloadReceivedSavegame(); +#endif + + CL_SendClientCmd(); // Send tic cmd + hu_redownloadinggamestate = cl_redownloadinggamestate; + } + else + { + if (!demoplayback) + { + INT32 counts; + + hu_redownloadinggamestate = false; + + firstticstosend = gametic; + for (i = 0; i < MAXNETNODES; i++) + if (nodeingame[i] && nettics[i] < firstticstosend) + { + firstticstosend = nettics[i]; + + if (maketic + 1 >= nettics[i] + BACKUPTICS) + Net_ConnectionTimeout(i); + } + + // Don't erase tics not acknowledged + counts = realtics; + + if (maketic + counts >= firstticstosend + BACKUPTICS) + counts = firstticstosend+BACKUPTICS-maketic-1; + + for (i = 0; i < counts; i++) + SV_Maketic(); // Create missed tics and increment maketic + + for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged + D_Clearticcmd(tictoclear); // Clear the maketic the new tic + + SV_SendTics(); + + neededtic = maketic; // The server is a client too + } + } + + Net_AckTicker(); + + // Handle timeouts to prevent definitive freezes from happenning + if (server) + { + for (i = 1; i < MAXNETNODES; i++) + if (nodeingame[i] && freezetimeout[i] < I_GetTime()) + Net_ConnectionTimeout(i); + + // In case the cvar value was lowered + if (joindelay) + joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE); + } + + nowtime /= NEWTICRATERATIO; + if (nowtime > resptime) + { + resptime = nowtime; +#ifdef HAVE_THREADS + I_lock_mutex(&m_menu_mutex); +#endif + M_Ticker(); +#ifdef HAVE_THREADS + I_unlock_mutex(m_menu_mutex); +#endif + CON_Ticker(); + } + + FileSendTicker(); +} + +/** Returns the number of players playing. + * \return Number of players. Can be zero if we're running a ::dedicated + * server. + * \author Graue + */ +INT32 D_NumPlayers(void) +{ + INT32 num = 0, ix; + for (ix = 0; ix < MAXPLAYERS; ix++) + if (playeringame[ix]) + num++; + return num; +} + +tic_t GetLag(INT32 node) +{ + return gametic - nettics[node]; +} + +void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest) +{ +#ifdef NOMD5 + (void)buffer; + (void)len; + (void)salt; + memset(dest, 0, 16); +#else + char tmpbuf[256]; + const size_t sl = strlen(salt); + + if (len > 256-sl) + len = 256-sl; + + memcpy(tmpbuf, buffer, len); + memmove(&tmpbuf[len], salt, sl); + //strcpy(&tmpbuf[len], salt); + len += strlen(salt); + if (len < 256) + memset(&tmpbuf[len],0,256-len); + + // Yes, we intentionally md5 the ENTIRE buffer regardless of size... + md5_buffer(tmpbuf, 256, dest); +#endif +} diff --git a/src/netcode/protocol.h b/src/d_clisrv.h similarity index 60% rename from src/netcode/protocol.h rename to src/d_clisrv.h index 9866e4c5a..e07864122 100644 --- a/src/netcode/protocol.h +++ b/src/d_clisrv.h @@ -7,15 +7,19 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file protocol.h -/// \brief Data exchanged through the network +/// \file d_clisrv.h +/// \brief high level networking stuff -#ifndef __PROTOCOL__ -#define __PROTOCOL__ +#ifndef __D_CLISRV__ +#define __D_CLISRV__ +#include "d_ticcmd.h" #include "d_net.h" -#include "../d_ticcmd.h" -#include "../doomdef.h" +#include "d_netcmd.h" +#include "d_net.h" +#include "tables.h" +#include "d_player.h" +#include "mserv.h" /* The 'packet version' is used to distinguish packet @@ -34,9 +38,10 @@ therein, increment this number. // one that defines the actual packets to // be transmitted. +// Networking and tick handling related. #define BACKUPTICS 1024 +#define CLIENTBACKUPTICS 32 #define MAXTEXTCMD 256 - // // Packet structure // @@ -85,6 +90,7 @@ typedef enum PT_TEXTCMD, // Extra text commands from the client. PT_TEXTCMD2, // Splitscreen text commands. PT_CLIENTJOIN, // Client wants to join; used in start game. + PT_NODETIMEOUT, // Packet sent to self if the connection times out. PT_LOGIN, // Login attempt from the client. @@ -95,6 +101,14 @@ typedef enum NUMPACKETTYPE } packettype_t; +#ifdef PACKETDROP +void Command_Drop(void); +void Command_Droprate(void); +#endif +#ifdef _DEBUG +void Command_Numnodes(void); +#endif + #if defined(_MSC_VER) #pragma pack(1) #endif @@ -123,12 +137,13 @@ typedef struct #endif // Server to client packet +// this packet is too large typedef struct { tic_t starttic; UINT8 numtics; UINT8 numslots; // "Slots filled": Highest player number in use plus one. - ticcmd_t cmds[45]; + ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large } ATTRPACK servertics_pak; typedef struct @@ -197,7 +212,6 @@ enum { #define MAXSERVERNAME 32 #define MAXFILENEEDED 915 - // This packet is too large typedef struct { @@ -258,7 +272,7 @@ typedef struct UINT8 data; // Color is first four bits, hasflag, isit and issuper have one bit each, the last is unused. UINT32 score; UINT16 timeinserver; // In seconds. -} ATTRPACK plrinfo_pak; +} ATTRPACK plrinfo; // Shortest player information for join during intermission. typedef struct @@ -269,7 +283,7 @@ typedef struct UINT32 pflags; UINT32 score; UINT8 ctfteam; -} ATTRPACK plrconfig_pak; +} ATTRPACK plrconfig; typedef struct { @@ -292,25 +306,25 @@ typedef struct UINT8 reserved; // Padding union { - clientcmd_pak clientpak; - client2cmd_pak client2pak; - servertics_pak serverpak; - serverconfig_pak servercfg; - UINT8 textcmd[MAXTEXTCMD+1]; - filetx_pak filetxpak; + clientcmd_pak clientpak; // 144 bytes + client2cmd_pak client2pak; // 200 bytes + servertics_pak serverpak; // 132495 bytes (more around 360, no?) + serverconfig_pak servercfg; // 773 bytes + UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) + filetx_pak filetxpak; // 139 bytes fileack_pak fileack; UINT8 filereceived; - clientconfig_pak clientcfg; + clientconfig_pak clientcfg; // 136 bytes UINT8 md5sum[16]; - serverinfo_pak serverinfo; - serverrefuse_pak serverrefuse; - askinfo_pak askinfo; - msaskinfo_pak msaskinfo; - plrinfo_pak playerinfo[MAXPLAYERS]; - plrconfig_pak playerconfig[MAXPLAYERS]; - INT32 filesneedednum; - filesneededconfig_pak filesneededcfg; - UINT32 pingtable[MAXPLAYERS+1]; + serverinfo_pak serverinfo; // 1024 bytes + serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) + askinfo_pak askinfo; // 61 bytes + msaskinfo_pak msaskinfo; // 22 bytes + plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?) + plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) + INT32 filesneedednum; // 4 bytes + filesneededconfig_pak filesneededcfg; // ??? bytes + UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes } u; // This is needed to pack diff packet types data together } ATTRPACK doomdata_t; @@ -318,7 +332,26 @@ typedef struct #pragma pack() #endif +#define MAXSERVERLIST (MAXNETNODES-1) +typedef struct +{ + SINT8 node; + serverinfo_pak info; +} serverelem_t; + +extern serverelem_t serverlist[MAXSERVERLIST]; +extern UINT32 serverlistcount; +extern INT32 mapchangepending; + +// Points inside doomcom +extern doomdata_t *netbuffer; + +extern consvar_t cv_showjoinaddress; +extern consvar_t cv_playbackspeed; + +#define BASEPACKETSIZE offsetof(doomdata_t, u) #define FILETXHEADER offsetof(filetx_pak, data) +#define BASESERVERTICSSIZE offsetof(doomdata_t, u.serverpak.cmds[0]) #define KICK_MSG_GO_AWAY 1 #define KICK_MSG_CON_FAIL 2 @@ -330,4 +363,102 @@ typedef struct #define KICK_MSG_CUSTOM_BAN 8 #define KICK_MSG_KEEP_BODY 0x80 +typedef enum +{ + KR_KICK = 1, //Kicked by server + KR_PINGLIMIT = 2, //Broke Ping Limit + KR_SYNCH = 3, //Synch Failure + KR_TIMEOUT = 4, //Connection Timeout + KR_BAN = 5, //Banned by server + KR_LEAVE = 6, //Quit the game + +} 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) +extern boolean dedicated; // For dedicated server +extern UINT16 software_MAXPACKETLENGTH; +extern boolean acceptnewnode; +extern SINT8 servernode; + +void Command_Ping_f(void); +extern tic_t connectiontimeout; +extern tic_t jointimeout; +extern UINT16 pingmeasurecount; +extern UINT32 realpingtable[MAXPLAYERS]; +extern UINT32 playerpingtable[MAXPLAYERS]; +extern tic_t servermaxping; + +extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout; +extern consvar_t cv_resynchattempts, cv_blamecfail; +extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; + +// Used in d_net, the only dependence +tic_t ExpandTics(INT32 low, INT32 node); +void D_ClientServerInit(void); + +// Initialise the other field +void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); +void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); +void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player +void SendKick(UINT8 playernum, UINT8 msg); + +// Create any new ticcmds and broadcast to other players. +void NetUpdate(void); + +void SV_StartSinglePlayerServer(void); +boolean SV_SpawnServer(void); +void SV_StopServer(void); +void SV_ResetServer(void); +void CL_AddSplitscreenPlayer(void); +void CL_RemoveSplitscreenPlayer(void); +void CL_Reset(void); +void CL_ClearPlayer(INT32 playernum); +void CL_QueryServerList(msg_server_t *list); +void CL_UpdateServerList(boolean internetsearch, INT32 room); +void CL_RemovePlayer(INT32 playernum, kickreason_t reason); +// Is there a game running +boolean Playing(void); + +// Broadcasts special packets to other players +// to notify of game exit +void D_QuitNetGame(void); + +//? How many ticks to run? +boolean TryRunTics(tic_t realtic); + +// extra data for lmps +// these functions scare me. they contain magic. +/*boolean AddLmpExtradata(UINT8 **demo_p, INT32 playernum); +void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum);*/ + +#ifndef NONET +// translate a playername in a player number return -1 if not found and +// print a error message in the console +SINT8 nametonum(const char *name); +#endif + +extern char motd[254], server_context[8]; +extern UINT8 playernode[MAXPLAYERS]; + +INT32 D_NumPlayers(void); +void D_ResetTiccmds(void); + +tic_t GetLag(INT32 node); +UINT8 GetFreeXCmdSize(void); + +void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest); + +extern UINT8 hu_redownloadinggamestate; + +extern UINT8 adminpassmd5[16]; +extern boolean adminpasswordset; + +extern boolean hu_stopped; + #endif diff --git a/src/d_main.c b/src/d_main.c index 8b13dba89..1af8d090c 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -34,7 +34,7 @@ #include "doomdef.h" #include "am_map.h" #include "console.h" -#include "netcode/d_net.h" +#include "d_net.h" #include "f_finale.h" #include "g_game.h" #include "hu_stuff.h" @@ -57,11 +57,11 @@ #include "w_wad.h" #include "z_zone.h" #include "d_main.h" -#include "netcode/d_netfil.h" +#include "d_netfil.h" #include "m_cheat.h" #include "y_inter.h" #include "p_local.h" // chasecam -#include "netcode/mserv.h" // ms_RoomId +#include "mserv.h" // ms_RoomId #include "m_misc.h" // screenshot functionality #include "deh_tables.h" // Dehacked list test #include "m_cond.h" // condition initialization diff --git a/src/netcode/d_net.c b/src/d_net.c similarity index 93% rename from src/netcode/d_net.c rename to src/d_net.c index a4b0778e3..a7e1eb16d 100644 --- a/src/netcode/d_net.c +++ b/src/d_net.c @@ -16,21 +16,19 @@ /// This protocol uses a mix of "goback n" and "selective repeat" implementation /// The NOTHING packet is sent when connection is idle to acknowledge packets -#include "../doomdef.h" -#include "../g_game.h" -#include "../i_time.h" +#include "doomdef.h" +#include "g_game.h" +#include "i_time.h" #include "i_net.h" -#include "../i_system.h" -#include "../m_argv.h" +#include "i_system.h" +#include "m_argv.h" #include "d_net.h" -#include "../w_wad.h" +#include "w_wad.h" #include "d_netfil.h" #include "d_clisrv.h" -#include "tic_command.h" -#include "net_command.h" -#include "../z_zone.h" +#include "z_zone.h" #include "i_tcp.h" -#include "../d_main.h" // srb2home +#include "d_main.h" // srb2home // // NETWORKING @@ -140,6 +138,7 @@ boolean Net_GetNetStat(void) #define URGENTFREESLOTNUM 10 #define ACKTOSENDTIMEOUT (TICRATE/11) +#ifndef NONET typedef struct { UINT8 acknum; @@ -153,6 +152,7 @@ typedef struct doomdata_t data; } pak; } ackpak_t; +#endif typedef enum { @@ -160,8 +160,10 @@ typedef enum NF_TIMEOUT = 2, // Flag is set when the node got a timeout } node_flags_t; +#ifndef NONET // Table of packets that were not acknowleged can be resent (the sender window) static ackpak_t ackpak[MAXACKPACKETS]; +#endif typedef struct { @@ -189,6 +191,7 @@ typedef struct static node_t nodes[MAXNETNODES]; #define NODETIMEOUT 14 +#ifndef NONET // return <0 if a < b (mod 256) // 0 if a = n (mod 256) // >0 if a > b (mod 256) @@ -211,7 +214,7 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b) static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) { node_t *node = &nodes[doomcom->remotenode]; - INT32 numfreeslot = 0; + INT32 i, numfreeslot = 0; if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0) { @@ -219,7 +222,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) return false; } - for (INT32 i = 0; i < MAXACKPACKETS; i++) + for (i = 0; i < MAXACKPACKETS; i++) if (!ackpak[i].acknum) { // For low priority packets, make sure to let freeslots so urgent packets can be sent @@ -276,10 +279,10 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) */ INT32 Net_GetFreeAcks(boolean urgent) { - INT32 numfreeslot = 0; + INT32 i, numfreeslot = 0; INT32 n = 0; // Number of free acks found - for (INT32 i = 0; i < MAXACKPACKETS; i++) + for (i = 0; i < MAXACKPACKETS; i++) if (!ackpak[i].acknum) { // For low priority packets, make sure to let freeslots so urgent packets can be sent @@ -315,6 +318,7 @@ static void RemoveAck(INT32 i) // We have got a packet, proceed the ack request and ack return static boolean Processackpak(void) { + INT32 i; boolean goodpacket = true; node_t *node = &nodes[doomcom->remotenode]; @@ -323,7 +327,7 @@ static boolean Processackpak(void) { node->remotefirstack = netbuffer->ackreturn; // Search the ackbuffer and free it - for (INT32 i = 0; i < MAXACKPACKETS; i++) + for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes && cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0) { @@ -345,7 +349,7 @@ static boolean Processackpak(void) else { // Check if it is not already in the queue - for (INT32 i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) + for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) if (node->acktosend[i] == ack) { DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack)); @@ -373,7 +377,7 @@ static boolean Processackpak(void) while (change) { change = false; - for (INT32 i = node->acktosend_tail; i != node->acktosend_head; + for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND) { if (cmpack(node->acktosend[i], nextfirstack) <= 0) @@ -422,20 +426,28 @@ static boolean Processackpak(void) } return goodpacket; } +#endif // send special packet with only ack on it void Net_SendAcks(INT32 node) { +#ifdef NONET + (void)node; +#else netbuffer->packettype = PT_NOTHING; M_Memcpy(netbuffer->u.textcmd, nodes[node].acktosend, MAXACKTOSEND); HSendPacket(node, false, 0, MAXACKTOSEND); +#endif } +#ifndef NONET static void GotAcks(void) { - for (INT32 j = 0; j < MAXACKTOSEND; j++) + INT32 i, j; + + for (j = 0; j < MAXACKTOSEND; j++) if (netbuffer->u.textcmd[j]) - for (INT32 i = 0; i < MAXACKPACKETS; i++) + for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode) { if (ackpak[i].acknum == netbuffer->u.textcmd[j]) @@ -451,6 +463,7 @@ static void GotAcks(void) } } } +#endif void Net_ConnectionTimeout(INT32 node) { @@ -459,10 +472,14 @@ void Net_ConnectionTimeout(INT32 node) return; nodes[node].flags |= NF_TIMEOUT; - if (server) - SendKicksForNode(node, KICK_MSG_TIMEOUT | KICK_MSG_KEEP_BODY); - else - CL_HandleTimeout(); + // Send a very special packet to self (hack the reboundstore queue) + // Main code will handle it + reboundstore[rebound_head].packettype = PT_NODETIMEOUT; + reboundstore[rebound_head].ack = 0; + reboundstore[rebound_head].ackreturn = 0; + reboundstore[rebound_head].u.textcmd[0] = (UINT8)node; + reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1); + rebound_head = (rebound_head+1) % MAXREBOUND; // Do not redo it quickly (if we do not close connection it is // for a good reason!) @@ -472,8 +489,10 @@ void Net_ConnectionTimeout(INT32 node) // Resend the data if needed void Net_AckTicker(void) { +#ifndef NONET + INT32 i; - for (INT32 i = 0; i < MAXACKPACKETS; i++) + for (i = 0; i < MAXACKPACKETS; i++) { const INT32 nodei = ackpak[i].destinationnode; node_t *node = &nodes[nodei]; @@ -500,7 +519,7 @@ void Net_AckTicker(void) } } - for (INT32 i = 1; i < MAXNETNODES; i++) + for (i = 1; i < MAXNETNODES; i++) { // This is something like node open flag if (nodes[i].firstacktosend) @@ -517,12 +536,16 @@ void Net_AckTicker(void) } } } +#endif } // Remove last packet received ack before resending the ackreturn // (the higher layer doesn't have room, or something else ....) void Net_UnAcknowledgePacket(INT32 node) { +#ifdef NONET + (void)node; +#else INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND; DEBFILE(va("UnAcknowledge node %d\n", node)); if (!node) @@ -554,8 +577,10 @@ void Net_UnAcknowledgePacket(INT32 node) if (!nodes[node].firstacktosend) nodes[node].firstacktosend = 1; } +#endif } +#ifndef NONET /** Checks if all acks have been received * * \return True if all acks have been received @@ -563,12 +588,15 @@ void Net_UnAcknowledgePacket(INT32 node) */ static boolean Net_AllAcksReceived(void) { - for (INT32 i = 0; i < MAXACKPACKETS; i++) + INT32 i; + + for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum) return false; return true; } +#endif /** Waits for all ackreturns * @@ -577,6 +605,9 @@ static boolean Net_AllAcksReceived(void) */ void Net_WaitAllAckReceived(UINT32 timeout) { +#ifdef NONET + (void)timeout; +#else tic_t tictac = I_GetTime(); timeout = tictac + timeout*NEWTICRATE; @@ -592,6 +623,7 @@ void Net_WaitAllAckReceived(UINT32 timeout) HGetPacket(); Net_AckTicker(); } +#endif } static void InitNode(node_t *node) @@ -605,10 +637,14 @@ static void InitNode(node_t *node) static void InitAck(void) { - for (INT32 i = 0; i < MAXACKPACKETS; i++) - ackpak[i].acknum = 0; + INT32 i; - for (INT32 i = 0; i < MAXNETNODES; i++) +#ifndef NONET + for (i = 0; i < MAXACKPACKETS; i++) + ackpak[i].acknum = 0; +#endif + + for (i = 0; i < MAXNETNODES; i++) InitNode(&nodes[i]); } @@ -619,12 +655,17 @@ static void InitAck(void) */ void Net_AbortPacketType(UINT8 packettype) { - for (INT32 i = 0; i < MAXACKPACKETS; i++) +#ifdef NONET + (void)packettype; +#else + INT32 i; + for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && (ackpak[i].pak.data.packettype == packettype || packettype == UINT8_MAX)) { ackpak[i].acknum = 0; } +#endif } // ----------------------------------------------------------------- @@ -634,6 +675,10 @@ void Net_AbortPacketType(UINT8 packettype) // remove a node, clear all ack from this node and reset askret void Net_CloseConnection(INT32 node) { +#ifdef NONET + (void)node; +#else + INT32 i; boolean forceclose = (node & FORCECLOSE) != 0; if (node == -1) @@ -663,7 +708,7 @@ void Net_CloseConnection(INT32 node) } // check if we are waiting for an ack from this node - for (INT32 i = 0; i < MAXACKPACKETS; i++) + for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum && ackpak[i].destinationnode == node) { if (!forceclose) @@ -677,8 +722,10 @@ void Net_CloseConnection(INT32 node) if (server) SV_AbortLuaFileTransfer(node); I_NetFreeNodenum(node); +#endif } +#ifndef NONET // // Checksum // @@ -687,20 +734,23 @@ static UINT32 NetbufferChecksum(void) UINT32 c = 0x1234567; const INT32 l = doomcom->datalength - 4; const UINT8 *buf = (UINT8 *)netbuffer + 4; + INT32 i; - for (INT32 i = 0; i < l; i++, buf++) + for (i = 0; i < l; i++, buf++) c += (*buf) * (i+1); return LONG(c); } +#endif #ifdef DEBUGFILE static void fprintfstring(char *s, size_t len) { INT32 mode = 0; + size_t i; - for (size_t i = 0; i < len; i++) + for (i = 0; i < len; i++) if (s[i] < 32) { if (!mode) @@ -767,6 +817,7 @@ static const char *packettypename[NUMPACKETTYPE] = "TEXTCMD", "TEXTCMD2", "CLIENTJOIN", + "NODETIMEOUT", "LOGIN", "TELLFILESNEEDED", "MOREFILESNEEDED", @@ -867,6 +918,7 @@ void Command_Drop(void) { INT32 packetquantity; const char *packetname; + size_t i; if (COM_Argc() < 2) { @@ -896,11 +948,11 @@ void Command_Drop(void) packetname = COM_Argv(1); if (!(stricmp(packetname, "all") && stricmp(packetname, "any"))) - for (size_t i = 0; i < NUMPACKETTYPE; i++) + for (i = 0; i < NUMPACKETTYPE; i++) packetdropquantity[i] = packetquantity; else { - for (size_t i = 0; i < NUMPACKETTYPE; i++) + for (i = 0; i < NUMPACKETTYPE; i++) if (!stricmp(packetname, packettypename[i])) { packetdropquantity[i] = packetquantity; @@ -931,12 +983,14 @@ void Command_Droprate(void) packetdroprate = droprate; } +#ifndef NONET static boolean ShouldDropPacket(void) { return (packetdropquantity[netbuffer->packettype]) || (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100; } #endif +#endif // // HSendPacket @@ -971,6 +1025,11 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen if (!netgame) I_Error("Tried to transmit to another node"); +#ifdef NONET + (void)node; + (void)reliable; + (void)acknum; +#else // do this before GetFreeAcknum because this function backups // the current packet doomcom->remotenode = (INT16)node; @@ -1031,6 +1090,8 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen } #endif +#endif // ndef NONET + return true; } @@ -1048,7 +1109,10 @@ boolean HGetPacket(void) { M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]); doomcom->datalength = reboundsize[rebound_tail]; - doomcom->remotenode = 0; + if (netbuffer->packettype == PT_NODETIMEOUT) + doomcom->remotenode = netbuffer->u.textcmd[0]; + else + doomcom->remotenode = 0; rebound_tail = (rebound_tail+1) % MAXREBOUND; #ifdef DEBUGFILE @@ -1061,6 +1125,8 @@ boolean HGetPacket(void) if (!netgame) return false; +#ifndef NONET + while(true) { //nodejustjoined = I_NetGet(); @@ -1120,6 +1186,7 @@ boolean HGetPacket(void) } break; } +#endif // ndef NONET return true; } @@ -1323,12 +1390,13 @@ void Command_Ping_f(void) int name_width = 0; int ms_width = 0; + int n; + INT32 i; + pingc = 0; - for (INT32 i = 1; i < MAXPLAYERS; ++i) + for (i = 1; i < MAXPLAYERS; ++i) if (playeringame[i]) { - int n; - n = strlen(player_names[i]); if (n > name_width) name_width = n; @@ -1348,7 +1416,7 @@ void Command_Ping_f(void) qsort(pingv, pingc, sizeof (struct pingcell), &pingcellcmp); - for (INT32 i = 0; i < pingc; ++i) + for (i = 0; i < pingc; ++i) { CONS_Printf("%02d : %-*s %*d ms\n", pingv[i].num, @@ -1364,13 +1432,15 @@ void Command_Ping_f(void) void D_CloseConnection(void) { + INT32 i; + if (netgame) { // wait the ackreturn with timout of 5 Sec Net_WaitAllAckReceived(5); // close all connection - for (INT32 i = 0; i < MAXNETNODES; i++) + for (i = 0; i < MAXNETNODES; i++) Net_CloseConnection(i|FORCECLOSE); InitAck(); diff --git a/src/netcode/d_net.h b/src/d_net.h similarity index 74% rename from src/netcode/d_net.h rename to src/d_net.h index 039f5b3b4..5baa593a0 100644 --- a/src/netcode/d_net.h +++ b/src/d_net.h @@ -18,8 +18,6 @@ #ifndef __D_NET__ #define __D_NET__ -#include "../doomtype.h" - // Max computers in a game // 127 is probably as high as this can go, because // SINT8 is used for nodes sometimes >:( @@ -39,24 +37,10 @@ boolean Net_GetNetStat(void); extern INT32 getbytes; extern INT64 sendbytes; // Realtime updated -typedef struct netnode_s -{ - boolean ingame; // set false as nodes leave game - tic_t freezetimeout; // Until when can this node freeze the server before getting a timeout? - - SINT8 player; - SINT8 player2; // say the numplayer for this node if any (splitscreen) - UINT8 numplayers; // used specialy for scplitscreen - - tic_t tic; // what tic the client have received - tic_t supposedtic; // nettics prevision for smaller packet - - boolean sendingsavegame; // Are we sending the savegame? - boolean resendingsavegame; // Are we resending the savegame? - tic_t savegameresendcooldown; // How long before we can resend again? -} netnode_t; - -extern netnode_t netnodes[MAXNETNODES]; +extern SINT8 nodetoplayer[MAXNETNODES]; +extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen) +extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen +extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game extern boolean serverrunning; @@ -68,6 +52,9 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength); boolean HGetPacket(void); void D_SetDoomcom(void); +#ifndef NONET +void D_SaveBan(void); +#endif boolean D_CheckNetGame(void); void D_CloseConnection(void); void Net_UnAcknowledgePacket(INT32 node); diff --git a/src/netcode/d_netcmd.c b/src/d_netcmd.c similarity index 99% rename from src/netcode/d_netcmd.c rename to src/d_netcmd.c index dd9f811e3..f721aa75a 100644 --- a/src/netcode/d_netcmd.c +++ b/src/d_netcmd.c @@ -12,47 +12,45 @@ /// commands are executed through the command buffer /// like console commands, other miscellaneous commands (at the end) -#include "../doomdef.h" +#include "doomdef.h" -#include "../console.h" -#include "../command.h" -#include "../i_time.h" -#include "../i_system.h" -#include "../g_game.h" -#include "../hu_stuff.h" -#include "../g_input.h" -#include "../i_gamepad.h" -#include "../m_menu.h" -#include "../r_local.h" -#include "../r_skins.h" -#include "../p_local.h" -#include "../p_setup.h" -#include "../s_sound.h" -#include "../i_sound.h" -#include "../m_misc.h" -#include "../am_map.h" -#include "../byteptr.h" +#include "console.h" +#include "command.h" +#include "i_time.h" +#include "i_system.h" +#include "g_game.h" +#include "hu_stuff.h" +#include "g_input.h" +#include "i_gamepad.h" +#include "m_menu.h" +#include "r_local.h" +#include "r_skins.h" +#include "p_local.h" +#include "p_setup.h" +#include "s_sound.h" +#include "i_sound.h" +#include "m_misc.h" +#include "am_map.h" +#include "byteptr.h" #include "d_netfil.h" -#include "../p_spec.h" -#include "../m_cheat.h" +#include "p_spec.h" +#include "m_cheat.h" #include "d_clisrv.h" -#include "server_connection.h" -#include "net_command.h" #include "d_net.h" -#include "../v_video.h" -#include "../d_main.h" -#include "../m_random.h" -#include "../f_finale.h" -#include "../filesrch.h" +#include "v_video.h" +#include "d_main.h" +#include "m_random.h" +#include "f_finale.h" +#include "filesrch.h" #include "mserv.h" -#include "../z_zone.h" -#include "../lua_script.h" -#include "../lua_hook.h" -#include "../m_cond.h" -#include "../m_anigif.h" -#include "../md5.h" -#include "../m_perfstats.h" -#include "../hardware/u_list.h" // TODO: this should be a standard utility class +#include "z_zone.h" +#include "lua_script.h" +#include "lua_hook.h" +#include "m_cond.h" +#include "m_anigif.h" +#include "md5.h" +#include "m_perfstats.h" +#include "hardware/u_list.h" // TODO: this should be a standard utility class #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR @@ -631,9 +629,12 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_maxsend); CV_RegisterVar(&cv_noticedownload); CV_RegisterVar(&cv_downloadspeed); +#ifndef NONET CV_RegisterVar(&cv_allownewplayer); + CV_RegisterVar(&cv_joinnextround); CV_RegisterVar(&cv_showjoinaddress); CV_RegisterVar(&cv_blamecfail); +#endif COM_AddCommand("ping", Command_Ping_f); CV_RegisterVar(&cv_nettimeout); @@ -1841,7 +1842,8 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese // reset players if there is a new one if (!IsPlayerAdmin(consoleplayer)) { - SV_SpawnServer(); + if (SV_SpawnServer()) + buf[0] &= ~(1<<1); if (!Playing()) // you failed to start a server somehow, so cancel the map change return; } diff --git a/src/netcode/d_netcmd.h b/src/d_netcmd.h similarity index 99% rename from src/netcode/d_netcmd.h rename to src/d_netcmd.h index 797a686a7..47f68a17e 100644 --- a/src/netcode/d_netcmd.h +++ b/src/d_netcmd.h @@ -15,7 +15,7 @@ #ifndef __D_NETCMD__ #define __D_NETCMD__ -#include "../command.h" +#include "command.h" // console vars extern consvar_t cv_playername; diff --git a/src/netcode/d_netfil.c b/src/d_netfil.c similarity index 93% rename from src/netcode/d_netfil.c rename to src/d_netfil.c index 10b7359ad..edbef30bb 100644 --- a/src/netcode/d_netfil.c +++ b/src/d_netfil.c @@ -31,25 +31,24 @@ #include #endif -#include "../doomdef.h" -#include "../doomstat.h" -#include "../d_main.h" -#include "../g_game.h" -#include "../i_time.h" +#include "doomdef.h" +#include "doomstat.h" +#include "d_main.h" +#include "g_game.h" +#include "i_time.h" #include "i_net.h" -#include "../i_system.h" -#include "../m_argv.h" +#include "i_system.h" +#include "m_argv.h" #include "d_net.h" -#include "../w_wad.h" +#include "w_wad.h" #include "d_netfil.h" -#include "net_command.h" -#include "../z_zone.h" -#include "../byteptr.h" -#include "../p_setup.h" -#include "../m_misc.h" -#include "../m_menu.h" -#include "../md5.h" -#include "../filesrch.h" +#include "z_zone.h" +#include "byteptr.h" +#include "p_setup.h" +#include "m_misc.h" +#include "m_menu.h" +#include "md5.h" +#include "filesrch.h" #include @@ -104,31 +103,26 @@ typedef struct } pauseddownload_t; 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 luafiletransfer_t *luafiletransfers = NULL; boolean waitingforluafiletransfer = false; boolean waitingforluafilecommand = false; char luafiledir[256 + 16] = "luafiles"; -// max file size to send to a player (in kilobytes) -static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}}; -consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); - -consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -// Speed of file downloading (in packets per tic) -static CV_PossibleValue_t downloadspeed_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; -consvar_t cv_downloadspeed = CVAR_INIT ("downloadspeed", "16", CV_SAVE|CV_NETVAR, downloadspeed_cons_t, NULL); static UINT16 GetWadNumFromFileNeededId(UINT8 id) { - for (UINT16 wadnum = mainwads; wadnum < numwadfiles; wadnum++) + UINT16 wadnum; + + for (wadnum = mainwads; wadnum < numwadfiles; wadnum++) { if (!wadfiles[wadnum]->important) continue; @@ -148,13 +142,14 @@ static UINT16 GetWadNumFromFileNeededId(UINT8 id) */ UINT8 *PutFileNeeded(UINT16 firstfile) { + size_t i; UINT8 count = 0; UINT8 *p_start = netbuffer->packettype == PT_MOREFILESNEEDED ? netbuffer->u.filesneededcfg.files : netbuffer->u.serverinfo.fileneeded; UINT8 *p = p_start; char wadfilename[MAX_WADPATH] = ""; UINT8 filestatus, folder; - for (size_t i = mainwads; i < numwadfiles; i++) //mainwads, otherwise we start on the first mainwad + for (i = mainwads; i < numwadfiles; i++) //mainwads, 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) @@ -229,6 +224,7 @@ void FreeFileNeeded(void) */ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 firstfile) { + INT32 i; UINT8 *p; UINT8 filestatus; @@ -237,7 +233,7 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi AllocFileNeeded(fileneedednum); - for (INT32 i = firstfile; i < fileneedednum; i++) + for (i = firstfile; i < fileneedednum; i++) { fileneeded[i].type = FILENEEDED_WAD; fileneeded[i].status = FS_NOTCHECKED; // We haven't even started looking for the file yet @@ -254,7 +250,9 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 fi void CL_PrepareDownloadSaveGame(const char *tmpsave) { +#ifndef NONET lastfilenum = -1; +#endif FreeFileNeeded(); AllocFileNeeded(1); @@ -277,9 +275,9 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave) */ boolean CL_CheckDownloadable(void) { - UINT8 dlstatus = 0; + UINT8 i,dlstatus = 0; - for (UINT8 i = 0; i < fileneedednum; i++) + for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN) { if (fileneeded[i].willsend == 1) @@ -300,7 +298,7 @@ boolean CL_CheckDownloadable(void) // not downloadable, put reason in console CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n")); - for (UINT8 i = 0; i < fileneedednum; i++) + for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN) { CONS_Printf(" * \"%s\" (%dK)", fileneeded[i].filename, fileneeded[i].totalsize >> 10); @@ -370,13 +368,14 @@ void CL_AbortDownloadResume(void) boolean CL_SendFileRequest(void) { char *p; + INT32 i; INT64 totalfreespaceneeded = 0, availablefreespace; #ifdef PARANOIA if (M_CheckParm("-nodownload")) I_Error("Attempted to download files in -nodownload mode"); - for (INT32 i = 0; i < fileneedednum; i++) + for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2)) { @@ -386,7 +385,7 @@ boolean CL_SendFileRequest(void) netbuffer->packettype = PT_REQUESTFILE; p = (char *)netbuffer->u.textcmd; - for (INT32 i = 0; i < fileneedednum; i++) + for (i = 0; i < fileneedednum; i++) if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)) { totalfreespaceneeded += fileneeded[i].totalsize; @@ -414,31 +413,26 @@ boolean CL_SendFileRequest(void) } // get request filepak and put it on the send queue -void PT_RequestFile(SINT8 node) +// returns false if a requested file was not found or cannot be sent +boolean PT_RequestFile(INT32 node) { UINT8 *p = netbuffer->u.textcmd; - - if (client || !cv_downloading.value) - { - Net_CloseConnection(node); // close connection if you are not the server or disabled downloading - return; - } + UINT8 id; while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow { - UINT8 id = READUINT8(p); + id = READUINT8(p); if (id == 0xFF) break; if (!AddFileToSendQueue(node, id)) { SV_AbortSendFiles(node); - Net_CloseConnection(node); // close connection if one of the requested files could not be sent - return; // don't read the rest of the files + return false; // don't read the rest of the files } } - return; // no problems with any files + return true; // no problems with any files } /** Checks if the files needed aren't already loaded or on the disk @@ -537,7 +531,9 @@ INT32 CL_CheckFiles(void) // Load it now boolean CL_LoadServerFiles(void) { - for (INT32 i = 0; i < fileneedednum; i++) + INT32 i; + + for (i = 0; i < fileneedednum; i++) { if (fileneeded[i].status == FS_OPEN) continue; // Already loaded @@ -633,10 +629,11 @@ void AddLuaFileTransfer(const char *filename, const char *mode) static void SV_PrepareSendLuaFileToNextNode(void) { + INT32 i; UINT8 success = 1; // Find a client to send the file to - for (INT32 i = 1; i < MAXNETNODES; i++) + for (i = 1; i < MAXNETNODES; i++) if (luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting { // Tell the client we're about to send them the file @@ -658,12 +655,13 @@ static void SV_PrepareSendLuaFileToNextNode(void) void SV_PrepareSendLuaFile(void) { char *binfilename; + INT32 i; luafiletransfers->ongoing = true; // Set status to "waiting" for everyone - for (INT32 i = 0; i < MAXNETNODES; i++) - luafiletransfers->nodestatus[i] = (netnodes[i].ingame ? LFTNS_WAITING : LFTNS_NONE); + for (i = 0; i < MAXNETNODES; i++) + luafiletransfers->nodestatus[i] = (nodeingame[i] ? LFTNS_WAITING : LFTNS_NONE); if (FIL_ReadFileOK(luafiletransfers->realfilename)) { @@ -1139,13 +1137,12 @@ void FileSendTicker(void) } } -void PT_FileAck(SINT8 node) +void PT_FileAck(void) { fileack_pak *packet = &netbuffer->u.fileack; + INT32 node = doomcom->remotenode; filetran_t *trans = &transfer[node]; - - if (client) - return; + INT32 i, j; // Wrong file id? Ignore it, it's probably a late packet if (!(trans->txlist && packet->fileid == trans->txlist->fileid)) @@ -1164,11 +1161,11 @@ void PT_FileAck(SINT8 node) trans->dontsenduntil = 0; } - for (INT32 i = 0; i < packet->numsegments; i++) + for (i = 0; i < packet->numsegments; i++) { fileacksegment_t *segment = &packet->segments[i]; - for (INT32 j = 0; j < 32; j++) + for (j = 0; j < 32; j++) if (LONG(segment->acks) & (1 << j)) { if (LONG(segment->start) * FILEFRAGMENTSIZE >= trans->txlist->size) @@ -1193,23 +1190,24 @@ void PT_FileAck(SINT8 node) } } -void PT_FileReceived(SINT8 node) +void PT_FileReceived(void) { - filetx_t *trans = transfer[node].txlist; + filetx_t *trans = transfer[doomcom->remotenode].txlist; - if (server && trans && netbuffer->u.filereceived == trans->fileid) - SV_EndFileSend(node); + if (trans && netbuffer->u.filereceived == trans->fileid) + SV_EndFileSend(doomcom->remotenode); } static void SendAckPacket(fileack_pak *packet, UINT8 fileid) { size_t packetsize; + INT32 i; packetsize = sizeof(*packet) + packet->numsegments * sizeof(*packet->segments); // Finalise the packet packet->fileid = fileid; - for (INT32 i = 0; i < packet->numsegments; i++) + for (i = 0; i < packet->numsegments; i++) { packet->segments[i].start = LONG(packet->segments[i].start); packet->segments[i].acks = LONG(packet->segments[i].acks); @@ -1249,7 +1247,9 @@ static void AddFragmentToAckPacket(fileack_pak *packet, UINT8 iteration, UINT32 void FileReceiveTicker(void) { - for (INT32 i = 0; i < fileneedednum; i++) + INT32 i; + + for (i = 0; i < fileneedednum; i++) { fileneeded_t *file = &fileneeded[i]; @@ -1263,7 +1263,8 @@ void FileReceiveTicker(void) if (file->ackresendposition != UINT32_MAX && file->status == FS_DOWNLOADING) { // Acknowledge ~70 MB/s, whichs means the client sends ~18 KB/s - for (INT32 j = 0; j < 2048; j++) + INT32 j; + for (j = 0; j < 2048; j++) { if (file->receivedfragments[file->ackresendposition]) AddFragmentToAckPacket(file->ackpacket, file->iteration, file->ackresendposition, i); @@ -1280,27 +1281,8 @@ void FileReceiveTicker(void) } } -void PT_FileFragment(SINT8 node, INT32 netconsole) +void PT_FileFragment(void) { - if (netnodes[node].ingame) - { - // Only accept PT_FILEFRAGMENT from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_FILEFRAGMENT", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - return; - } - if (server) - return; - } - else if (server || node != servernode) - { - Net_CloseConnection(node); - return; - } - INT32 filenum = netbuffer->u.filetxpak.fileid; fileneeded_t *file = &fileneeded[filenum]; UINT32 fragmentpos = LONG(netbuffer->u.filetxpak.position); @@ -1457,7 +1439,9 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s); } +#ifndef NONET lastfilenum = filenum; +#endif } /** \brief Checks if a node is downloading a file @@ -1485,14 +1469,15 @@ void SV_AbortSendFiles(INT32 node) void CloseNetFile(void) { + INT32 i; // Is sending? - for (INT32 i = 0; i < MAXNETNODES; i++) + for (i = 0; i < MAXNETNODES; i++) SV_AbortSendFiles(i); // Receiving a file? if (fileneeded) { - for (INT32 i = 0; i < fileneedednum; i++) + for (i = 0; i < fileneedednum; i++) if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file) { fclose(fileneeded[i].file); @@ -1525,7 +1510,9 @@ void CloseNetFile(void) void Command_Downloads_f(void) { - for (INT32 node = 0; node < MAXNETNODES; node++) + INT32 node; + + for (node = 0; node < MAXNETNODES; node++) if (transfer[node].txlist && transfer[node].txlist->ram == SF_FILE) // Node is downloading a file? { @@ -1559,11 +1546,14 @@ void Command_Downloads_f(void) void nameonly(char *s) { - for (size_t j = strlen(s); j != (size_t)-1; j--) + size_t j, len; + void *ns; + + for (j = strlen(s); j != (size_t)-1; j--) if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/')) { - void *ns = &(s[j+1]); - size_t len = strlen(ns); + ns = &(s[j+1]); + len = strlen(ns); #if 0 M_Memcpy(s, ns, len+1); #else @@ -1576,9 +1566,9 @@ void nameonly(char *s) // Returns the length in characters of the last element of a path. size_t nameonlylength(const char *s) { - size_t len = strlen(s); + size_t j, len = strlen(s); - for (size_t j = len; j != (size_t)-1; j--) + for (j = len; j != (size_t)-1; j--) if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/')) return len - j - 1; diff --git a/src/netcode/d_netfil.h b/src/d_netfil.h similarity index 94% rename from src/netcode/d_netfil.h rename to src/d_netfil.h index 850e24d49..f778a518f 100644 --- a/src/netcode/d_netfil.h +++ b/src/d_netfil.h @@ -15,7 +15,7 @@ #include "d_net.h" #include "d_clisrv.h" -#include "../w_wad.h" +#include "w_wad.h" typedef enum { @@ -70,13 +70,13 @@ extern INT32 fileneedednum; extern fileneeded_t *fileneeded; extern char downloaddir[512]; +#ifndef NONET extern INT32 lastfilenum; extern INT32 downloadcompletednum; extern UINT32 downloadcompletedsize; extern INT32 totalfilesrequestednum; extern UINT32 totalfilesrequestedsize; - -extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed; +#endif void AllocFileNeeded(INT32 size); void FreeFileNeeded(void); @@ -90,16 +90,16 @@ void AddRamToSendQueue(INT32 node, void *data, size_t size, freemethod_t freemet UINT8 fileid); void FileSendTicker(void); -void PT_FileAck(SINT8 node); -void PT_FileReceived(SINT8 node); +void PT_FileAck(void); +void PT_FileReceived(void); boolean SendingFile(INT32 node); void FileReceiveTicker(void); -void PT_FileFragment(SINT8 node, INT32 netconsole); +void PT_FileFragment(void); boolean CL_CheckDownloadable(void); boolean CL_SendFileRequest(void); -void PT_RequestFile(SINT8 node); +boolean PT_RequestFile(INT32 node); typedef enum { diff --git a/src/deh_soc.c b/src/deh_soc.c index 81b36479c..cbc7940f7 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -34,7 +34,7 @@ #include "r_sky.h" #include "fastcmp.h" #include "lua_script.h" // Reluctantly included for LUA_EvalMath -#include "netcode/d_clisrv.h" +#include "d_clisrv.h" #ifdef HWRENDER #include "hardware/hw_light.h" diff --git a/src/deh_soc.h b/src/deh_soc.h index cd55b665b..f972ec26e 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -35,7 +35,7 @@ #include "r_sky.h" #include "fastcmp.h" #include "lua_script.h" // Reluctantly included for LUA_EvalMath -#include "netcode/d_clisrv.h" +#include "d_clisrv.h" #ifdef HWRENDER #include "hardware/hw_light.h" diff --git a/src/deh_tables.c b/src/deh_tables.c index a119f792f..90c3047a8 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4831,7 +4831,7 @@ const char *const MENUTYPES_LIST[] = { "MP_SERVER", "MP_CONNECT", "MP_ROOM", - "MP_PLAYERSETUP", + "MP_PLAYERSETUP", // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET "MP_SERVER_OPTIONS", // Options diff --git a/src/doomdef.h b/src/doomdef.h index b70f0a47e..24b4fa980 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -56,6 +56,7 @@ #endif #ifdef _WINDOWS +#define NONET #if !defined (HWRENDER) && !defined (NOHW) #define HWRENDER #endif @@ -656,7 +657,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Maintain compatibility with older 2.2 demos #define OLD22DEMOCOMPAT -#ifdef HAVE_CURL +#if defined (HAVE_CURL) && ! defined (NONET) #define MASTERSERVER #else #undef UPDATE_ALERT diff --git a/src/doomstat.h b/src/doomstat.h index 344a3daa4..bce43416b 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -683,7 +683,7 @@ extern boolean singletics; // Netgame stuff // ============= -#include "netcode/d_clisrv.h" +#include "d_clisrv.h" extern consvar_t cv_timetic; // display high resolution timer extern consvar_t cv_powerupdisplay; // display powerups diff --git a/src/dummy/i_net.c b/src/dummy/i_net.c index 4c30dc767..f6e642022 100644 --- a/src/dummy/i_net.c +++ b/src/dummy/i_net.c @@ -1,4 +1,4 @@ -#include "../netcode/i_net.h" +#include "../i_net.h" boolean I_InitNetwork(void) { diff --git a/src/f_finale.c b/src/f_finale.c index 2122cf34d..307e00aaa 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -14,7 +14,7 @@ #include "doomdef.h" #include "doomstat.h" #include "d_main.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" #include "f_finale.h" #include "g_game.h" #include "hu_stuff.h" diff --git a/src/filesrch.c b/src/filesrch.c index d68a7d5ed..23b8e7f40 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -26,7 +26,7 @@ #include #include "filesrch.h" -#include "netcode/d_netfil.h" +#include "d_netfil.h" #include "m_misc.h" #include "z_zone.h" #include "m_menu.h" // Addons_option_Onchange diff --git a/src/filesrch.h b/src/filesrch.h index a934c48d6..59ef5269b 100644 --- a/src/filesrch.h +++ b/src/filesrch.h @@ -5,7 +5,7 @@ #define __FILESRCH_H__ #include "doomdef.h" -#include "netcode/d_netfil.h" +#include "d_netfil.h" #include "m_menu.h" // MAXSTRINGLENGTH #include "w_wad.h" diff --git a/src/g_demo.c b/src/g_demo.c index 6167b3a09..9099adc71 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -15,7 +15,7 @@ #include "console.h" #include "d_main.h" #include "d_player.h" -#include "netcode/d_clisrv.h" +#include "d_clisrv.h" #include "p_setup.h" #include "i_time.h" #include "i_system.h" diff --git a/src/g_game.c b/src/g_game.c index 2aa2d7862..dc12c2bab 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -15,8 +15,7 @@ #include "console.h" #include "d_main.h" #include "d_player.h" -#include "netcode/d_clisrv.h" -#include "netcode/net_command.h" +#include "d_clisrv.h" #include "f_finale.h" #include "p_setup.h" #include "p_saveg.h" diff --git a/src/g_input.c b/src/g_input.c index d425c6ba8..87f4d5173 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -18,7 +18,7 @@ #include "i_gamepad.h" #include "keys.h" #include "hu_stuff.h" // need HUFONT start & end -#include "netcode/d_net.h" +#include "d_net.h" #include "console.h" #define MAXMOUSESENSITIVITY 100 // sensitivity steps diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 24a39e2fb..3a6baab71 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -29,7 +29,7 @@ #include "../r_patch.h" #include "../r_picformats.h" #include "../r_bsp.h" -#include "../netcode/d_clisrv.h" +#include "../d_clisrv.h" #include "../w_wad.h" #include "../z_zone.h" #include "../r_splats.h" @@ -5234,7 +5234,7 @@ static void HWR_ProjectSprite(mobj_t *thing) { interpmobjstate_t casterinterp = { 0 }; fixed_t groundz; - fixed_t floordiff; + fixed_t floordiff; if (R_UsingFrameInterpolation() && !paused) { @@ -5244,7 +5244,7 @@ static void HWR_ProjectSprite(mobj_t *thing) { R_InterpolateMobjState(caster, FRACUNIT, &casterinterp); } - + groundz = R_GetShadowZ(thing, NULL); floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + casterinterp.z - groundz); diff --git a/src/netcode/http-mserv.c b/src/http-mserv.c similarity index 98% rename from src/netcode/http-mserv.c rename to src/http-mserv.c index 1ef180935..7b418c99a 100644 --- a/src/netcode/http-mserv.c +++ b/src/http-mserv.c @@ -18,15 +18,14 @@ Documentation available here. #include #endif -#include "../doomdef.h" +#include "doomdef.h" #include "d_clisrv.h" -#include "client_connection.h" -#include "../command.h" -#include "../m_argv.h" -#include "../m_menu.h" +#include "command.h" +#include "m_argv.h" +#include "m_menu.h" #include "mserv.h" #include "i_tcp.h"/* for current_port */ -#include "../i_threads.h" +#include "i_threads.h" /* reasonable default I guess?? */ #define DEFAULT_BUFFER_SIZE (4096) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 7ec33bd79..805aa694f 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -19,9 +19,7 @@ #include "m_cond.h" // emblems #include "m_misc.h" // word jumping -#include "netcode/d_clisrv.h" -#include "netcode/net_command.h" -#include "netcode/gamestate.h" +#include "d_clisrv.h" #include "g_game.h" #include "g_input.h" @@ -177,12 +175,14 @@ static huddrawlist_h luahuddrawlist_scores; static tic_t resynch_ticker = 0; +#ifndef NONET // just after static void Command_Say_f(void); static void Command_Sayto_f(void); static void Command_Sayteam_f(void); static void Command_CSay_f(void); static void Got_Saycmd(UINT8 **p, INT32 playernum); +#endif void HU_LoadGraphics(void) { @@ -327,11 +327,13 @@ void HU_LoadGraphics(void) // void HU_Init(void) { +#ifndef NONET COM_AddCommand("say", Command_Say_f); COM_AddCommand("sayto", Command_Sayto_f); COM_AddCommand("sayteam", Command_Sayteam_f); COM_AddCommand("csay", Command_CSay_f); RegisterNetXCmd(XD_SAY, Got_Saycmd); +#endif // set shift translation table shiftxform = english_shiftxform; @@ -361,6 +363,8 @@ void HU_Start(void) // EXECUTION //====================================================================== +#ifndef NONET + // EVERY CHANGE IN THIS SCRIPT IS LOL XD! BY VINCYTM static UINT32 chat_nummsg_log = 0; @@ -408,9 +412,11 @@ static void HU_removeChatText_Log(void) } chat_nummsg_log--; // lost 1 msg. } +#endif void HU_AddChatText(const char *text, boolean playsound) { +#ifndef NONET if (playsound && cv_consolechat.value != 2) // Don't play the sound if we're using hidden chat. S_StartSound(NULL, sfx_radio); // reguardless of our preferences, put all of this in the chat buffer in case we decide to change from oldchat mid-game. @@ -432,8 +438,14 @@ void HU_AddChatText(const char *text, boolean playsound) CONS_Printf("%s\n", text); else // if we aren't, still save the message to log.txt CON_LogMessage(va("%s\n", text)); +#else + (void)playsound; + CONS_Printf("%s\n", text); +#endif } +#ifndef NONET + /** Runs a say command, sending an ::XD_SAY message. * A say command consists of a signed 8-bit integer for the target, an * unsigned 8-bit flag variable, and then the message itself. @@ -853,6 +865,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) #endif } +#endif + // // void HU_Ticker(void) @@ -868,6 +882,7 @@ void HU_Ticker(void) else hu_showscores = false; +#ifndef NONET if (chat_on) { // count down the scroll timer. @@ -895,6 +910,7 @@ void HU_Ticker(void) HU_removeChatText_Mini(); } } +#endif if (cechotimer > 0) --cechotimer; @@ -902,6 +918,8 @@ void HU_Ticker(void) resynch_ticker++; } +#ifndef NONET + static boolean teamtalk = false; static boolean justscrolleddown; static boolean justscrolledup; @@ -1009,6 +1027,8 @@ static void HU_sendChatMessage(void) } } +#endif + void HU_clearChatChars(void) { memset(w_chat, '\0', sizeof(w_chat)); @@ -1023,13 +1043,16 @@ void HU_clearChatChars(void) // boolean HU_Responder(event_t *ev) { +#ifndef NONET INT32 c=0; +#endif if (ev->type != ev_keydown) return false; // only KeyDown events now... +#ifndef NONET c = (INT32)ev->key; if (!chat_on) @@ -1179,6 +1202,7 @@ boolean HU_Responder(event_t *ev) return true; } +#endif return false; } @@ -1188,6 +1212,8 @@ boolean HU_Responder(event_t *ev) // HEADS UP DRAWING //====================================================================== +#ifndef NONET + // Precompile a wordwrapped string to any given width. // This is a muuuch better method than V_WORDWRAP. // again stolen and modified a bit from video.c, don't mind me, will need to rearrange this one day. @@ -1767,6 +1793,7 @@ static void HU_DrawChat_Old(void) if (hu_tick < 4) V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, true); } +#endif // Draw crosshairs at the exact center of the view. // In splitscreen, crosshairs are stretched vertically to compensate for V_PERPLAYER squishing them. @@ -1906,6 +1933,7 @@ static void HU_DrawDemoInfo(void) // void HU_Drawer(void) { +#ifndef NONET // draw chat string plus cursor if (chat_on) { @@ -1922,6 +1950,7 @@ void HU_Drawer(void) if (!OLDCHAT && cv_consolechat.value < 2 && netgame) // Don't display minimized chat if you set the mode to Window (Hidden) HU_drawMiniChat(); // draw messages in a cool fashion. } +#endif if (cechotimer) HU_DrawCEcho(); diff --git a/src/netcode/i_addrinfo.c b/src/i_addrinfo.c similarity index 100% rename from src/netcode/i_addrinfo.c rename to src/i_addrinfo.c diff --git a/src/netcode/i_addrinfo.h b/src/i_addrinfo.h similarity index 100% rename from src/netcode/i_addrinfo.h rename to src/i_addrinfo.h diff --git a/src/netcode/i_net.h b/src/i_net.h similarity index 98% rename from src/netcode/i_net.h rename to src/i_net.h index 66126d050..62b7528d5 100644 --- a/src/netcode/i_net.h +++ b/src/i_net.h @@ -18,8 +18,8 @@ #pragma interface #endif -#include "../doomdef.h" -#include "../command.h" +#include "doomdef.h" +#include "command.h" /// \brief program net id #define DOOMCOM_ID (INT32)0x12345678l diff --git a/src/netcode/i_tcp.c b/src/i_tcp.c similarity index 88% rename from src/netcode/i_tcp.c rename to src/i_tcp.c index bd950c355..8838ba725 100644 --- a/src/netcode/i_tcp.c +++ b/src/i_tcp.c @@ -36,100 +36,109 @@ #include #endif -#include "../doomdef.h" +#include "doomdef.h" -#ifdef USE_WINSOCK1 - #include +#if defined (NOMD5) && !defined (NONET) + //#define NONET +#endif + +#ifdef NONET + #undef HAVE_MINIUPNPC #else - #ifndef USE_WINSOCK - #include - #ifdef __APPLE_CC__ - #ifndef _BSD_SOCKLEN_T_ - #define _BSD_SOCKLEN_T_ - #endif //_BSD_SOCKLEN_T_ - #endif //__APPLE_CC__ - #include - #include - #include - #include - #endif //normal BSD API + #ifdef USE_WINSOCK1 + #include + #else + #ifndef USE_WINSOCK + #include + #ifdef __APPLE_CC__ + #ifndef _BSD_SOCKLEN_T_ + #define _BSD_SOCKLEN_T_ + #endif //_BSD_SOCKLEN_T_ + #endif //__APPLE_CC__ + #include + #include + #include + #include + #endif //normal BSD API - #include - #include + #include + #include - #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) - #include - #endif // UNIXCOMMON -#endif + #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) + #include + #endif // UNIXCOMMON + #endif -#ifdef USE_WINSOCK - // some undefined under win32 - #undef errno - //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? - #define errno h_errno // some very strange things happen when not using h_error?!? - #ifdef EWOULDBLOCK - #undef EWOULDBLOCK - #endif - #define EWOULDBLOCK WSAEWOULDBLOCK - #ifdef EMSGSIZE - #undef EMSGSIZE - #endif - #define EMSGSIZE WSAEMSGSIZE - #ifdef ECONNREFUSED - #undef ECONNREFUSED - #endif - #define ECONNREFUSED WSAECONNREFUSED - #ifdef ETIMEDOUT - #undef ETIMEDOUT - #endif - #define ETIMEDOUT WSAETIMEDOUT - #ifndef IOC_VENDOR - #define IOC_VENDOR 0x18000000 - #endif - #ifndef _WSAIOW - #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) - #endif - #ifndef SIO_UDP_CONNRESET - #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) - #endif - #ifndef AI_ADDRCONFIG - #define AI_ADDRCONFIG 0x00000400 - #endif - #ifndef STATUS_INVALID_PARAMETER - #define STATUS_INVALID_PARAMETER 0xC000000D - #endif -#endif // USE_WINSOCK + #ifdef USE_WINSOCK + // some undefined under win32 + #undef errno + //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? + #define errno h_errno // some very strange things happen when not using h_error?!? + #ifdef EWOULDBLOCK + #undef EWOULDBLOCK + #endif + #define EWOULDBLOCK WSAEWOULDBLOCK + #ifdef EMSGSIZE + #undef EMSGSIZE + #endif + #define EMSGSIZE WSAEMSGSIZE + #ifdef ECONNREFUSED + #undef ECONNREFUSED + #endif + #define ECONNREFUSED WSAECONNREFUSED + #ifdef ETIMEDOUT + #undef ETIMEDOUT + #endif + #define ETIMEDOUT WSAETIMEDOUT + #ifndef IOC_VENDOR + #define IOC_VENDOR 0x18000000 + #endif + #ifndef _WSAIOW + #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) + #endif + #ifndef SIO_UDP_CONNRESET + #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) + #endif + #ifndef AI_ADDRCONFIG + #define AI_ADDRCONFIG 0x00000400 + #endif + #ifndef STATUS_INVALID_PARAMETER + #define STATUS_INVALID_PARAMETER 0xC000000D + #endif + #endif // USE_WINSOCK -typedef union -{ - struct sockaddr any; - struct sockaddr_in ip4; -#ifdef HAVE_IPV6 - struct sockaddr_in6 ip6; -#endif -} mysockaddr_t; - -#ifdef HAVE_MINIUPNPC - #ifdef STATIC_MINIUPNPC - #define STATICLIB + typedef union + { + struct sockaddr any; + struct sockaddr_in ip4; + #ifdef HAVE_IPV6 + struct sockaddr_in6 ip6; #endif - #include "miniupnpc/miniwget.h" - #include "miniupnpc/miniupnpc.h" - #include "miniupnpc/upnpcommands.h" - #undef STATICLIB - static UINT8 UPNP_support = TRUE; -#endif // HAVE_MINIUPNC + } mysockaddr_t; + + #ifdef HAVE_MINIUPNPC + #ifdef STATIC_MINIUPNPC + #define STATICLIB + #endif + #include "miniupnpc/miniwget.h" + #include "miniupnpc/miniupnpc.h" + #include "miniupnpc/upnpcommands.h" + #undef STATICLIB + static UINT8 UPNP_support = TRUE; + #endif // HAVE_MINIUPNC + +#endif // !NONET #define MAXBANS 100 -#include "../i_system.h" +#include "i_system.h" #include "i_net.h" #include "d_net.h" #include "d_netfil.h" #include "i_tcp.h" -#include "../m_argv.h" +#include "m_argv.h" -#include "../doomstat.h" +#include "doomstat.h" // win32 #ifdef USE_WINSOCK @@ -142,7 +151,7 @@ typedef union #define SELECTTEST #define DEFAULTPORT "5029" -#ifdef USE_WINSOCK +#if defined (USE_WINSOCK) && !defined (NONET) typedef SOCKET SOCKET_TYPE; #define ERRSOCKET (SOCKET_ERROR) #else @@ -154,20 +163,22 @@ typedef union #define ERRSOCKET (-1) #endif -// define socklen_t in DOS/Windows if it is not already defined -#ifdef USE_WINSOCK1 - typedef int socklen_t; +#ifndef NONET + // define socklen_t in DOS/Windows if it is not already defined + #ifdef USE_WINSOCK1 + typedef int socklen_t; + #endif + static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; + static size_t mysocketses = 0; + static int myfamily[MAXNETNODES+1] = {0}; + static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET}; + static mysockaddr_t clientaddress[MAXNETNODES+1]; + static mysockaddr_t broadcastaddress[MAXNETNODES+1]; + static size_t broadcastaddresses = 0; + static boolean nodeconnected[MAXNETNODES+1]; + static mysockaddr_t banned[MAXBANS]; + static UINT8 bannedmask[MAXBANS]; #endif -static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; -static size_t mysocketses = 0; -static int myfamily[MAXNETNODES+1] = {0}; -static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET}; -static mysockaddr_t clientaddress[MAXNETNODES+1]; -static mysockaddr_t broadcastaddress[MAXNETNODES+1]; -static size_t broadcastaddresses = 0; -static boolean nodeconnected[MAXNETNODES+1]; -static mysockaddr_t banned[MAXBANS]; -static UINT8 bannedmask[MAXBANS]; static size_t numbans = 0; static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? @@ -176,6 +187,7 @@ static boolean init_tcp_driver = false; static const char *serverport_name = DEFAULTPORT; static const char *clientport_name;/* any port */ +#ifndef NONET #ifdef USE_WINSOCK // stupid microsoft makes things complicated static char *get_WSAErrorStr(int e) @@ -362,33 +374,47 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk) #endif return s; } +#endif static const char *SOCK_GetNodeAddress(INT32 node) { if (node == 0) return "self"; +#ifdef NONET + return NULL; +#else if (!nodeconnected[node]) return NULL; return SOCK_AddrToStr(&clientaddress[node]); +#endif } static const char *SOCK_GetBanAddress(size_t ban) { if (ban >= numbans) return NULL; +#ifdef NONET + return NULL; +#else return SOCK_AddrToStr(&banned[ban]); +#endif } static const char *SOCK_GetBanMask(size_t ban) { +#ifdef NONET + (void)ban; +#else static char s[16]; //255.255.255.255 netmask? no, just CDIR for only if (ban >= numbans) return NULL; if (sprintf(s,"%d",bannedmask[ban]) > 0) return s; +#endif return NULL; } +#ifndef NONET static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) { UINT32 bitmask = INADDR_NONE; @@ -416,20 +442,24 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) */ static void cleanupnodes(void) { + SINT8 j; + if (!Playing()) return; // Why can't I start at zero? - for (SINT8 j = 1; j < MAXNETNODES; j++) - if (!(netnodes[j].ingame || SendingFile(j))) + for (j = 1; j < MAXNETNODES; j++) + if (!(nodeingame[j] || SendingFile(j))) nodeconnected[j] = false; } static SINT8 getfreenode(void) { + SINT8 j; + cleanupnodes(); - for (SINT8 j = 0; j < MAXNETNODES; j++) + for (j = 0; j < MAXNETNODES; j++) if (!nodeconnected[j]) { nodeconnected[j] = true; @@ -442,8 +472,8 @@ static SINT8 getfreenode(void) * downloading a needed wad, but it's better than not letting anyone join... */ /*I_Error("No more free nodes!!1!11!11!!1111\n"); - for (SINT8 j = 1; j < MAXNETNODES; j++) - if (!netnodes[j].ingame) + for (j = 1; j < MAXNETNODES; j++) + if (!nodeingame[j]) return j;*/ return -1; @@ -454,27 +484,28 @@ void Command_Numnodes(void) { INT32 connected = 0; INT32 ingame = 0; + INT32 i; - for (INT32 i = 1; i < MAXNETNODES; i++) + for (i = 1; i < MAXNETNODES; i++) { - if (!(nodeconnected[i] || netnodes[i].ingame)) + if (!(nodeconnected[i] || nodeingame[i])) continue; if (nodeconnected[i]) connected++; - if (netnodes[i].ingame) + if (nodeingame[i]) ingame++; CONS_Printf("%2d - ", i); - if (netnodes[i].player != -1) - CONS_Printf("player %.2d", netnodes[i].player); + if (nodetoplayer[i] != -1) + CONS_Printf("player %.2d", nodetoplayer[i]); else CONS_Printf(" "); if (nodeconnected[i]) CONS_Printf(" - connected"); else CONS_Printf(" - "); - if (netnodes[i].ingame) + if (nodeingame[i]) CONS_Printf(" - ingame"); else CONS_Printf(" - "); @@ -487,17 +518,19 @@ void Command_Numnodes(void) connected, ingame); } #endif +#endif +#ifndef NONET // Returns true if a packet was received from a new node, false in all other cases static boolean SOCK_Get(void) { - size_t i; + size_t i, n; int j; ssize_t c; mysockaddr_t fromaddress; socklen_t fromlen; - for (size_t n = 0; n < mysocketses; n++) + for (n = 0; n < mysocketses; n++) { fromlen = (socklen_t)sizeof(fromaddress); c = recvfrom(mysockets[n], (char *)&doomcom->data, MAXPACKETLENGTH, 0, @@ -550,17 +583,20 @@ static boolean SOCK_Get(void) doomcom->remotenode = -1; // no packet return false; } +#endif // check if we can send (do not go over the buffer) +#ifndef NONET static fd_set masterset; #ifdef SELECTTEST static boolean FD_CPY(fd_set *src, fd_set *dst, SOCKET_TYPE *fd, size_t len) { + size_t i; boolean testset = false; FD_ZERO(dst); - for (size_t i = 0; i < len;i++) + for (i = 0; i < len;i++) { if(fd[i] != (SOCKET_TYPE)ERRSOCKET && FD_ISSET(fd[i], src) && !FD_ISSET(fd[i], dst)) // no checking for dups @@ -600,7 +636,9 @@ static boolean SOCK_CanGet(void) return false; } #endif +#endif +#ifndef NONET static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr) { socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in); @@ -624,15 +662,16 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr static void SOCK_Send(void) { ssize_t c = ERRSOCKET; + size_t i, j; if (!nodeconnected[doomcom->remotenode]) return; if (doomcom->remotenode == BROADCASTADDR) { - for (size_t i = 0; i < mysocketses; i++) + for (i = 0; i < mysocketses; i++) { - for (size_t j = 0; j < broadcastaddresses; j++) + for (j = 0; j < broadcastaddresses; j++) { if (myfamily[i] == broadcastaddress[j].any.sa_family) SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]); @@ -642,7 +681,7 @@ static void SOCK_Send(void) } else if (nodesocket[doomcom->remotenode] == (SOCKET_TYPE)ERRSOCKET) { - for (size_t i = 0; i < mysocketses; i++) + for (i = 0; i < mysocketses; i++) { if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family) SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]); @@ -662,7 +701,9 @@ static void SOCK_Send(void) SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e)); } } +#endif +#ifndef NONET static void SOCK_FreeNodenum(INT32 numnode) { // can't disconnect from self :) @@ -677,10 +718,12 @@ static void SOCK_FreeNodenum(INT32 numnode) // put invalid address memset(&clientaddress[numnode], 0, sizeof (clientaddress[numnode])); } +#endif // // UDPsocket // +#ifndef NONET // allocate a socket static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen) @@ -1001,10 +1044,12 @@ static boolean UDP_Socket(void) return true; } +#endif boolean I_InitTcpDriver(void) { boolean tcp_was_up = init_tcp_driver; +#ifndef NONET if (!init_tcp_driver) { #ifdef USE_WINSOCK @@ -1059,7 +1104,7 @@ boolean I_InitTcpDriver(void) #endif init_tcp_driver = true; } - +#endif if (!tcp_was_up && init_tcp_driver) { I_AddExitFunc(I_ShutdownTcpDriver); @@ -1073,9 +1118,11 @@ boolean I_InitTcpDriver(void) return init_tcp_driver; } +#ifndef NONET static void SOCK_CloseSocket(void) { - for (size_t i=0; i < MAXNETNODES+1; i++) + size_t i; + for (i=0; i < MAXNETNODES+1; i++) { if (mysockets[i] != (SOCKET_TYPE)ERRSOCKET && FD_ISSET(mysockets[i], &masterset)) @@ -1086,9 +1133,11 @@ static void SOCK_CloseSocket(void) mysockets[i] = ERRSOCKET; } } +#endif void I_ShutdownTcpDriver(void) { +#ifndef NONET SOCK_CloseSocket(); CONS_Printf("I_ShutdownTcpDriver: "); @@ -1098,8 +1147,10 @@ void I_ShutdownTcpDriver(void) #endif CONS_Printf("shut down\n"); init_tcp_driver = false; +#endif } +#ifndef NONET static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) { SINT8 newnode = -1; @@ -1143,13 +1194,17 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) I_freeaddrinfo(ai); return newnode; } +#endif static boolean SOCK_OpenSocket(void) { +#ifndef NONET + size_t i; + memset(clientaddress, 0, sizeof (clientaddress)); nodeconnected[0] = true; // always connected to self - for (size_t i = 1; i < MAXNETNODES; i++) + for (i = 1; i < MAXNETNODES; i++) nodeconnected[i] = false; nodeconnected[BROADCASTADDR] = true; I_NetSend = SOCK_Send; @@ -1167,12 +1222,18 @@ static boolean SOCK_OpenSocket(void) // build the socket but close it first SOCK_CloseSocket(); return UDP_Socket(); +#else + return false; +#endif } static boolean SOCK_Ban(INT32 node) { if (node > MAXNETNODES) return false; +#ifdef NONET + return false; +#else if (numbans == MAXBANS) return false; @@ -1191,10 +1252,16 @@ static boolean SOCK_Ban(INT32 node) #endif numbans++; return true; +#endif } static boolean SOCK_SetBanAddress(const char *address, const char *mask) { +#ifdef NONET + (void)address; + (void)mask; + return false; +#else struct my_addrinfo *ai, *runp, hints; int gaie; @@ -1239,6 +1306,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) I_freeaddrinfo(ai); return true; +#endif } static void SOCK_ClearBans(void) diff --git a/src/netcode/i_tcp.h b/src/i_tcp.h similarity index 100% rename from src/netcode/i_tcp.h rename to src/i_tcp.h diff --git a/src/i_time.c b/src/i_time.c index 9303d7be7..c1cc9dfd4 100644 --- a/src/i_time.c +++ b/src/i_time.c @@ -17,7 +17,7 @@ #include "command.h" #include "doomtype.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" #include "m_fixed.h" #include "i_system.h" diff --git a/src/lua_baselib.c b/src/lua_baselib.c index ea519be55..c94e9e91e 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -27,11 +27,11 @@ #include "y_inter.h" #include "hu_stuff.h" // HU_AddChatText #include "console.h" -#include "netcode/d_netcmd.h" // IsPlayerAdmin +#include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff #include "m_misc.h" // M_MapNumber #include "b_bot.h" // B_UpdateBotleader -#include "netcode/d_clisrv.h" // CL_RemovePlayer +#include "d_clisrv.h" // CL_RemovePlayer #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "lua_script.h" diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index a1bdf380d..816051199 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -16,7 +16,6 @@ #include "g_game.h" #include "byteptr.h" #include "z_zone.h" -#include "netcode/net_command.h" #include "lua_script.h" #include "lua_libs.h" diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 2b1fef238..ef8426d7a 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -25,7 +25,7 @@ #include "lua_hud.h" // hud_running errors #include "m_perfstats.h" -#include "netcode/d_netcmd.h" // for cv_perfstats +#include "d_netcmd.h" // for cv_perfstats #include "i_system.h" // I_GetPreciseTime /* ========================================================================= diff --git a/src/lua_script.c b/src/lua_script.c index f1c800df5..8d8fb295c 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -28,7 +28,7 @@ #include "p_slopes.h" // for P_SlopeById and slopelist #include "p_polyobj.h" // polyobj_t, PolyObjects #ifdef LUA_ALLOW_BYTECODE -#include "netcode/d_netfil.h" // for LUA_DumpFile +#include "d_netfil.h" // for LUA_DumpFile #endif #include "lua_script.h" diff --git a/src/m_cheat.c b/src/m_cheat.c index 36b2d1907..f94377450 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -19,7 +19,7 @@ #include "r_local.h" #include "p_local.h" #include "p_setup.h" -#include "netcode/d_net.h" +#include "d_net.h" #include "m_cheat.h" #include "m_menu.h" diff --git a/src/m_menu.c b/src/m_menu.c index eecac55a7..1e7e1a761 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -20,7 +20,7 @@ #include "doomdef.h" #include "d_main.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" #include "console.h" #include "r_fps.h" #include "r_local.h" @@ -55,10 +55,8 @@ #include "hardware/hw_main.h" #endif -#include "netcode/d_net.h" -#include "netcode/mserv.h" -#include "netcode/server_connection.h" -#include "netcode/client_connection.h" +#include "d_net.h" +#include "mserv.h" #include "m_misc.h" #include "m_anigif.h" #include "byteptr.h" @@ -149,7 +147,9 @@ static struct INT32 index; } gamepadInfo[MAX_CONNECTED_GAMEPADS + 1]; +#ifndef NONET static UINT32 serverlistpage; +#endif static UINT8 numsaves = 0; static saveinfo_t* savegameinfo = NULL; // Extra info about the save games. @@ -189,8 +189,10 @@ static void M_GoBack(INT32 choice); static void M_StopMessage(INT32 choice); static boolean stopstopmessage = false; +#ifndef NONET static void M_HandleServerPage(INT32 choice); static void M_RoomMenu(INT32 choice); +#endif // Prototyping is fun, innit? // ========================================================================== @@ -292,6 +294,7 @@ static void M_SetupMultiPlayer2(INT32 choice); static void M_StartSplitServerMenu(INT32 choice); static void M_StartServer(INT32 choice); static void M_ServerOptions(INT32 choice); +#ifndef NONET static void M_StartServerMenu(INT32 choice); static void M_ConnectMenu(INT32 choice); static void M_ConnectMenuModChecks(INT32 choice); @@ -299,6 +302,7 @@ static void M_Refresh(INT32 choice); static void M_Connect(INT32 choice); static void M_ChooseRoom(INT32 choice); menu_t MP_MainDef; +#endif // Options // Split into multiple parts due to size @@ -377,9 +381,11 @@ static void M_DrawVideoMode(void); static void M_DrawColorMenu(void); static void M_DrawScreenshotMenu(void); static void M_DrawMonitorToggles(void); +#ifndef NONET static void M_DrawConnectMenu(void); static void M_DrawMPMainMenu(void); static void M_DrawRoomMenu(void); +#endif static void M_DrawGamepadList(void); static void M_DrawSetupMultiPlayerMenu(void); @@ -393,8 +399,10 @@ static void M_HandleImageDef(INT32 choice); static void M_HandleLoadSave(INT32 choice); static void M_HandleLevelStats(INT32 choice); static void M_HandlePlaystyleMenu(INT32 choice); +#ifndef NONET static boolean M_CancelConnect(void); static void M_HandleConnectIP(INT32 choice); +#endif static void M_HandleSetupMultiPlayer(INT32 choice); static void M_HandleVideoMode(INT32 choice); @@ -493,7 +501,11 @@ consvar_t cv_dummyloadless = CVAR_INIT ("dummyloadless", "In-game", CV_HIDEN, lo static menuitem_t MainMenu[] = { {IT_STRING|IT_CALL, NULL, "1 Player", M_SinglePlayerMenu, 76}, +#ifndef NONET {IT_STRING|IT_SUBMENU, NULL, "Multiplayer", &MP_MainDef, 84}, +#else + {IT_STRING|IT_CALL, NULL, "Multiplayer", M_StartSplitServerMenu, 84}, +#endif {IT_STRING|IT_CALL, NULL, "Extras", M_SecretsMenu, 92}, {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 100}, {IT_STRING|IT_CALL, NULL, "Options", M_Options, 108}, @@ -944,10 +956,16 @@ static menuitem_t SP_PlayerMenu[] = static menuitem_t MP_SplitServerMenu[] = { {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_MapChange, 100}, +#ifdef NONET // In order to keep player setup accessible. + {IT_STRING|IT_CALL, NULL, "Player 1 setup...", M_SetupMultiPlayer, 110}, + {IT_STRING|IT_CALL, NULL, "Player 2 setup...", M_SetupMultiPlayer2, 120}, +#endif {IT_STRING|IT_CALL, NULL, "More Options...", M_ServerOptions, 130}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 140}, }; +#ifndef NONET + static menuitem_t MP_MainMenu[] = { {IT_HEADER, NULL, "Join a game", NULL, 0}, @@ -1034,6 +1052,8 @@ menuitem_t MP_RoomMenu[] = {IT_DISABLED, NULL, "", M_ChooseRoom, 162}, }; +#endif + static menuitem_t MP_PlayerSetupMenu[] = { {IT_KEYHANDLER, NULL, "", M_HandleSetupMultiPlayer, 0}, // name @@ -1601,12 +1621,14 @@ enum static menuitem_t OP_ServerOptionsMenu[] = { {IT_HEADER, NULL, "General", NULL, 0}, +#ifndef NONET {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Server name", &cv_servername, 7}, {IT_STRING | IT_CVAR, NULL, "Max Players", &cv_maxplayers, 21}, {IT_STRING | IT_CVAR, NULL, "Allow Add-on Downloading", &cv_downloading, 26}, {IT_STRING | IT_CVAR, NULL, "Allow players to join", &cv_allownewplayer, 31}, {IT_STRING | IT_CVAR, NULL, "Minutes for reconnecting", &cv_rejointimeout, 36}, +#endif {IT_STRING | IT_CVAR, NULL, "Map progression", &cv_advancemap, 41}, {IT_STRING | IT_CVAR, NULL, "Intermission Timer", &cv_inttime, 46}, @@ -1645,6 +1667,7 @@ static menuitem_t OP_ServerOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Autobalance sizes", &cv_autobalance, 216}, {IT_STRING | IT_CVAR, NULL, "Scramble on Map Change", &cv_scrambleonchange, 221}, +#ifndef NONET {IT_HEADER, NULL, "Advanced", NULL, 230}, {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 236}, @@ -1652,6 +1675,7 @@ static menuitem_t OP_ServerOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 256}, {IT_STRING | IT_CVAR, NULL, "Show IP Address of Joiners", &cv_showjoinaddress, 261}, +#endif }; static menuitem_t OP_MonitorToggleMenu[] = @@ -1961,7 +1985,11 @@ menu_t MP_SplitServerDef = MTREE2(MN_MP_MAIN, MN_MP_SPLITSCREEN), "M_MULTI", sizeof (MP_SplitServerMenu)/sizeof (menuitem_t), +#ifndef NONET &MP_MainDef, +#else + &MainDef, +#endif MP_SplitServerMenu, M_DrawServerMenu, 27, 30 - 50, @@ -1969,6 +1997,8 @@ menu_t MP_SplitServerDef = NULL }; +#ifndef NONET + menu_t MP_MainDef = { MN_MP_MAIN, @@ -2020,10 +2050,15 @@ menu_t MP_RoomDef = 0, NULL }; +#endif menu_t MP_PlayerSetupDef = { +#ifdef NONET + MTREE2(MN_MP_MAIN, MN_MP_PLAYERSETUP), +#else MTREE3(MN_MP_MAIN, MN_MP_SPLITSCREEN, MN_MP_PLAYERSETUP), +#endif "M_SPLAYR", sizeof (MP_PlayerSetupMenu)/sizeof (menuitem_t), &MainDef, // doesn't matter @@ -3973,7 +4008,9 @@ void M_Init(void) OP_GamepadSetMenu[i].itemaction = M_AssignGamepad; } +#ifndef NONET CV_RegisterVar(&cv_serversort); +#endif } void M_InitCharacterTables(void) @@ -10986,6 +11023,7 @@ static void M_EndGame(INT32 choice) #define S_LINEY(n) currentMenu->y + SERVERHEADERHEIGHT + (n * SERVERLINEHEIGHT) +#ifndef NONET static UINT32 localservercount; static void M_HandleServerPage(INT32 choice) @@ -11257,9 +11295,11 @@ static int ServerListEntryComparator_modified(const void *entry1, const void *en // Default to strcmp. return strcmp(sa->info.servername, sb->info.servername); } +#endif void M_SortServerList(void) { +#ifndef NONET switch(cv_serversort.value) { case 0: // Ping. @@ -11281,8 +11321,10 @@ void M_SortServerList(void) qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename); break; } +#endif } +#ifndef NONET #ifdef UPDATE_ALERT static boolean M_CheckMODVersion(int id) { @@ -11481,6 +11523,7 @@ static void M_ChooseRoom(INT32 choice) if (currentMenu == &MP_ConnectDef) M_Refresh(0); } +#endif //NONET //=========================================================================== // Start Server Menu @@ -11528,6 +11571,7 @@ static void M_DrawServerMenu(void) { M_DrawGenericMenu(); +#ifndef NONET // Room name if (currentMenu == &MP_ServerDef) { @@ -11539,10 +11583,15 @@ static void M_DrawServerMenu(void) V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey, V_YELLOWMAP, room_list[menuRoomIndex].name); } +#endif if (cv_nextmap.value) { +#ifndef NONET #define imgheight MP_ServerMenu[mp_server_levelgt].alphaKey +#else +#define imgheight 100 +#endif patch_t *PictureOfLevel; lumpnum_t lumpnum; char headerstr[40]; @@ -11594,6 +11643,7 @@ static void M_ServerOptions(INT32 choice) { (void)choice; +#ifndef NONET if ((splitscreen && !netgame) || currentMenu == &MP_SplitServerDef) { OP_ServerOptionsMenu[ 1].status = IT_GRAYEDOUT; // Server name @@ -11614,6 +11664,7 @@ static void M_ServerOptions(INT32 choice) OP_ServerOptionsMenu[37].status = IT_STRING | IT_CVAR; OP_ServerOptionsMenu[38].status = IT_STRING | IT_CVAR; } +#endif /* Disable fading because of different menu head. */ if (currentMenu == &OP_MainDef)/* from Options menu */ @@ -11625,6 +11676,7 @@ static void M_ServerOptions(INT32 choice) M_SetupNextMenu(&OP_ServerOptionsDef); } +#ifndef NONET static void M_StartServerMenu(INT32 choice) { (void)choice; @@ -11881,6 +11933,7 @@ static void M_HandleConnectIP(INT32 choice) M_ClearMenus(true); } } +#endif //!NONET // ======================== // MULTIPLAYER PLAYER SETUP diff --git a/src/m_menu.h b/src/m_menu.h index 809186a17..8d023811d 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -20,7 +20,7 @@ #include "command.h" #include "f_finale.h" // for ttmode_enum #include "i_threads.h" -#include "netcode/mserv.h" +#include "mserv.h" #include "r_things.h" // for SKINNAMESIZE // Compatibility with old-style named NiGHTS replay files. @@ -74,7 +74,7 @@ typedef enum MN_MP_SERVER, MN_MP_CONNECT, MN_MP_ROOM, - MN_MP_PLAYERSETUP, + MN_MP_PLAYERSETUP, // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET MN_MP_SERVER_OPTIONS, // Options diff --git a/src/m_perfstats.c b/src/m_perfstats.c index cca4c62ca..9f65a7616 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -12,7 +12,7 @@ #include "m_perfstats.h" #include "v_video.h" #include "i_video.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" #include "r_main.h" #include "i_system.h" #include "z_zone.h" diff --git a/src/netcode/mserv.c b/src/mserv.c similarity index 97% rename from src/netcode/mserv.c rename to src/mserv.c index f603d78e5..bff562c95 100644 --- a/src/netcode/mserv.c +++ b/src/mserv.c @@ -15,14 +15,13 @@ #include #endif -#include "../doomstat.h" -#include "../doomdef.h" -#include "../command.h" -#include "../i_threads.h" +#include "doomstat.h" +#include "doomdef.h" +#include "command.h" +#include "i_threads.h" #include "mserv.h" -#include "client_connection.h" -#include "../m_menu.h" -#include "../z_zone.h" +#include "m_menu.h" +#include "z_zone.h" #ifdef MASTERSERVER @@ -46,7 +45,9 @@ static I_cond MSCond; # define Unlock_state() #endif/*HAVE_THREADS*/ +#ifndef NONET static void Command_Listserv_f(void); +#endif #endif/*MASTERSERVER*/ @@ -88,6 +89,7 @@ msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test */ void AddMServCommands(void) { +#ifndef NONET CV_RegisterVar(&cv_masterserver); CV_RegisterVar(&cv_masterserver_update_rate); CV_RegisterVar(&cv_masterserver_timeout); @@ -98,6 +100,7 @@ void AddMServCommands(void) COM_AddCommand("listserv", Command_Listserv_f); COM_AddCommand("masterserver_update", Update_parameters); // allows people to updates manually in case you were delisted by accident #endif +#endif } #ifdef MASTERSERVER @@ -186,6 +189,7 @@ void GetMODVersion_Console(void) } #endif +#ifndef NONET /** Gets a list of game servers. Called from console. */ static void Command_Listserv_f(void) @@ -196,6 +200,7 @@ static void Command_Listserv_f(void) HMS_list_servers(); } } +#endif static void Finish_registration (void) diff --git a/src/netcode/mserv.h b/src/mserv.h similarity index 99% rename from src/netcode/mserv.h rename to src/mserv.h index 7fdf3ed1b..23b26fbc5 100644 --- a/src/netcode/mserv.h +++ b/src/mserv.h @@ -14,7 +14,7 @@ #ifndef _MSERV_H_ #define _MSERV_H_ -#include "../i_threads.h" +#include "i_threads.h" // lowered from 32 due to menu changes #define NUM_LIST_ROOMS 16 diff --git a/src/netcode/Sourcefile b/src/netcode/Sourcefile deleted file mode 100644 index 7c0354714..000000000 --- a/src/netcode/Sourcefile +++ /dev/null @@ -1,13 +0,0 @@ -d_clisrv.c -server_connection.c -client_connection.c -tic_command.c -net_command.c -gamestate.c -commands.c -d_net.c -d_netcmd.c -d_netfil.c -http-mserv.c -i_tcp.c -mserv.c diff --git a/src/netcode/client_connection.c b/src/netcode/client_connection.c deleted file mode 100644 index d363d7d5a..000000000 --- a/src/netcode/client_connection.c +++ /dev/null @@ -1,1161 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 client_connection.h -/// \brief Client connection handling - -#include "client_connection.h" -#include "gamestate.h" -#include "d_clisrv.h" -#include "d_netfil.h" -#include "../d_main.h" -#include "../f_finale.h" -#include "../g_game.h" -#include "../i_gamepad.h" -#include "i_net.h" -#include "../i_system.h" -#include "../i_time.h" -#include "../i_video.h" -#include "../m_menu.h" -#include "../m_misc.h" -#include "../snake.h" -#include "../s_sound.h" -#include "../v_video.h" -#include "../y_inter.h" -#include "../z_zone.h" -#include "../doomtype.h" -#include "../doomstat.h" - -cl_mode_t cl_mode = CL_SEARCHING; -static UINT16 cl_lastcheckedfilecount = 0; // used for full file list -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; -UINT8 mynode; -static void *snake = NULL; - -static void CL_DrawConnectionStatusBox(void) -{ - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); - if (cl_mode != CL_CONFIRMCONNECT) - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); -} - -// -// CL_DrawConnectionStatus -// -// Keep the local client informed of our status. -// -static inline void CL_DrawConnectionStatus(void) -{ - INT32 ccstime = I_GetTime(); - - // Draw background fade - V_DrawFadeScreen(0xFF00, 16); // force default - - if (cl_mode != CL_DOWNLOADFILES && cl_mode != CL_LOADFILES) - { - INT32 animtime = ((ccstime / 4) & 15) + 16; - UINT8 palstart; - const char *cltext; - - // Draw the bottom box. - CL_DrawConnectionStatusBox(); - - if (cl_mode == CL_SEARCHING) - palstart = 32; // Red - else if (cl_mode == CL_CONFIRMCONNECT) - palstart = 48; // Orange - else - palstart = 96; // Green - - if (!(cl_mode == CL_DOWNLOADSAVEGAME && lastfilenum != -1)) - for (INT32 i = 0; i < 16; ++i) // 15 pal entries total. - V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-16, 16, 8, palstart + ((animtime - i) & 15)); - - switch (cl_mode) - { - case CL_DOWNLOADSAVEGAME: - if (fileneeded && lastfilenum != -1) - { - UINT32 currentsize = fileneeded[lastfilenum].currentsize; - UINT32 totalsize = fileneeded[lastfilenum].totalsize; - INT32 dldlength; - - cltext = M_GetText("Downloading game state..."); - Net_GetNetStat(); - - dldlength = (INT32)((currentsize/(double)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_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %4uK/%4uK",currentsize>>10,totalsize>>10)); - - V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va("%3.1fK/s ", ((double)getbps)/1024)); - } - else - cltext = M_GetText("Waiting to download game state..."); - break; - case CL_ASKFULLFILELIST: - 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: - if (serverisfull) - cltext = M_GetText("Server full, waiting for a slot..."); - else - cltext = M_GetText("Requesting to join..."); - break; - default: - cltext = M_GetText("Connecting to server..."); - break; - } - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, cltext); - } - else - { - if (cl_mode == CL_LOADFILES) - { - INT32 totalfileslength; - INT32 loadcompletednum = 0; - - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-16, V_YELLOWMAP, "Press ESC to abort"); - - // ima just count files here - if (fileneeded) - { - for (INT32 i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_OPEN) - loadcompletednum++; - } - - // Loading progress - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, "Loading server addons..."); - totalfileslength = (INT32)((loadcompletednum/(double)(fileneedednum)) * 256); - M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-16-8, 32, 1); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 256, 8, 111); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, totalfileslength, 8, 96); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16, V_20TRANS|V_MONOSPACE, - va(" %2u/%2u Files",loadcompletednum,fileneedednum)); - } - else if (lastfilenum != -1) - { - INT32 dldlength; - static char tempname[28]; - fileneeded_t *file; - char *filename; - - if (snake) - Snake_Draw(snake); - - // Draw the bottom box. - CL_DrawConnectionStatusBox(); - - if (fileneeded) - { - file = &fileneeded[lastfilenum]; - filename = file->filename; - } - else - return; - - 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); - - memset(tempname, 0, sizeof(tempname)); - // offset filename to just the name only part - filename += strlen(filename) - nameonlylength(filename); - - if (strlen(filename) > sizeof(tempname)-1) // too long to display fully - { - size_t endhalfpos = strlen(filename)-10; - // display as first 14 chars + ... + last 10 chars - // which should add up to 27 if our math(s) is correct - snprintf(tempname, sizeof(tempname), "%.14s...%.10s", filename, filename+endhalfpos); - } - else // we can copy the whole thing in safely - { - strncpy(tempname, filename, sizeof(tempname)-1); - } - - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, - va(M_GetText("Downloading \"%s\""), tempname)); - V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-16, 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, - va("%3.1fK/s ", ((double)getbps)/1024)); - } - else - { - if (snake) - Snake_Draw(snake); - - CL_DrawConnectionStatusBox(); - V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-16-24, V_YELLOWMAP, - M_GetText("Waiting to download files...")); - } - } -} - -static boolean CL_AskFileList(INT32 firstfile) -{ - netbuffer->packettype = PT_TELLFILESNEEDED; - netbuffer->u.filesneedednum = firstfile; - - return HSendPacket(servernode, false, 0, sizeof (INT32)); -} - -/** Sends a PT_CLIENTJOIN packet to the server - * - * \return True if the packet was successfully sent - * - */ -boolean CL_SendJoin(void) -{ - UINT8 localplayers = 1; - if (netgame) - CONS_Printf(M_GetText("Sending join request...\n")); - netbuffer->packettype = PT_CLIENTJOIN; - - netbuffer->u.clientcfg.modversion = MODVERSION; - strncpy(netbuffer->u.clientcfg.application, - SRB2APPLICATION, - sizeof netbuffer->u.clientcfg.application); - - if (splitscreen || botingame) - localplayers++; - netbuffer->u.clientcfg.localplayers = localplayers; - - CleanupPlayerName(consoleplayer, cv_playername.zstring); - if (splitscreen) - CleanupPlayerName(1, cv_playername2.zstring); // 1 is a HACK? oh no - - strncpy(netbuffer->u.clientcfg.names[0], cv_playername.zstring, MAXPLAYERNAME); - strncpy(netbuffer->u.clientcfg.names[1], cv_playername2.zstring, MAXPLAYERNAME); - - return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak)); -} - -static void SendAskInfo(INT32 node) -{ - const tic_t asktime = I_GetTime(); - netbuffer->packettype = PT_ASKINFO; - netbuffer->u.askinfo.version = VERSION; - netbuffer->u.askinfo.time = (tic_t)LONG(asktime); - - // Even if this never arrives due to the host being firewalled, we've - // now allowed traffic from the host to us in, so once the MS relays - // our address to the host, it'll be able to speak to us. - HSendPacket(node, false, 0, sizeof (askinfo_pak)); -} - -serverelem_t serverlist[MAXSERVERLIST]; -UINT32 serverlistcount = 0; - -#define FORCECLOSE 0x8000 - -static void SL_ClearServerList(INT32 connectedserver) -{ - for (UINT32 i = 0; i < serverlistcount; i++) - if (connectedserver != serverlist[i].node) - { - Net_CloseConnection(serverlist[i].node|FORCECLOSE); - serverlist[i].node = 0; - } - serverlistcount = 0; -} - -static UINT32 SL_SearchServer(INT32 node) -{ - for (UINT32 i = 0; i < serverlistcount; i++) - if (serverlist[i].node == node) - return i; - - return UINT32_MAX; -} - -static void SL_InsertServer(serverinfo_pak* info, SINT8 node) -{ - UINT32 i; - - // search if not already on it - i = SL_SearchServer(node); - if (i == UINT32_MAX) - { - // not found add it - if (serverlistcount >= MAXSERVERLIST) - return; // list full - - // check it later if connecting to this one - if (node != servernode) - { - if (info->_255 != 255) - return; // Old packet format - - if (info->packetversion != PACKETVERSION) - return; // Old new packet format - - if (info->version != VERSION) - return; // Not same version. - - if (info->subversion != SUBVERSION) - return; // Close, but no cigar. - - if (strcmp(info->application, SRB2APPLICATION)) - return; // That's a different mod - } - - i = serverlistcount++; - } - - serverlist[i].info = *info; - serverlist[i].node = node; - - // resort server list - M_SortServerList(); -} - -#if defined (MASTERSERVER) && defined (HAVE_THREADS) -struct Fetch_servers_ctx -{ - int room; - int id; -}; - -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) - { - I_lock_mutex(&ms_QueryId_mutex); - { - 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); - } - } - - free(ctx); -} -#endif // defined (MASTERSERVER) && defined (HAVE_THREADS) - -void CL_QueryServerList (msg_server_t *server_list) -{ - for (INT32 i = 0; server_list[i].header.buffer[0]; i++) - { - // Make sure MS version matches our own, to - // thwart nefarious servers who lie to the MS. - - // lol bruh, that version COMES from the servers - //if (strcmp(version, server_list[i].version) == 0) - { - INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port); - if (node == -1) - break; // no more node free - SendAskInfo(node); - // Force close the connection so that servers can't eat - // up nodes forever if we never get a reply back from them - // (usually when they've not forwarded their ports). - // - // Don't worry, we'll get in contact with the working - // servers again when they send SERVERINFO to us later! - // - // (Note: as a side effect this probably means every - // server in the list will probably be using the same node (e.g. node 1), - // not that it matters which nodes they use when - // the connections are closed afterwards anyway) - // -- Monster Iestyn 12/11/18 - Net_CloseConnection(node|FORCECLOSE); - } - } -} - -void CL_UpdateServerList(boolean internetsearch, INT32 room) -{ - (void)internetsearch; - (void)room; - - SL_ClearServerList(0); - - if (!netgame && I_NetOpenSocket) - { - if (I_NetOpenSocket()) - { - netgame = true; - multiplayer = true; - } - } - - // search for local servers - if (netgame) - SendAskInfo(BROADCASTADDR); - -#ifdef MASTERSERVER - if (internetsearch) - { -#ifdef HAVE_THREADS - struct Fetch_servers_ctx *ctx; - - 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 // MASTERSERVER -} - -static void M_ConfirmConnect(event_t *ev) -{ - if (ev->type == ev_keydown || ev->type == ev_gamepad_down) - { - if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A)) - { - if (totalfilesrequestednum > 0) - { - if (CL_SendFileRequest()) - { - cl_mode = CL_DOWNLOADFILES; - Snake_Allocate(&snake); - } - } - else - cl_mode = CL_LOADFILES; - - M_ClearMenus(true); - } - else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B)) - { - cl_mode = CL_ABORTED; - M_ClearMenus(true); - } - } -} - -static boolean CL_FinishedFileList(void) -{ - INT32 i; - char *downloadsize = NULL; - - //CONS_Printf(M_GetText("Checking files...\n")); - i = CL_CheckFiles(); - if (i == 4) // still checking ... - { - return true; - } - else if (i == 3) // too many files - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have too many WAD files loaded\n" - "to add ones the server is using.\n" - "Please restart SRB2 before connecting.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - else if (i == 2) // cannot join for some reason - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have the wrong addons loaded.\n\n" - "To play on this server, restart\n" - "the game and don't load any addons.\n" - "SRB2 will automatically add\n" - "everything you need when you join.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - else if (i == 1) - { - 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 ENTER to continue\nor ESC to cancel.\n\n" - ), M_ConfirmConnect, MM_EVENTHANDLER); - cl_mode = CL_CONFIRMCONNECT; - curfadevalue = 0; - } - else - cl_mode = CL_LOADFILES; - } - else - { - // must download something - // can we, though? - if (!CL_CheckDownloadable()) // nope! - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "An error occured when trying to\n" - "download missing addons.\n" - "(This is almost always a problem\n" - "with the server, not your game.)\n\n" - "See the console or log file\n" - "for additional details.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - - downloadcompletednum = 0; - downloadcompletedsize = 0; - totalfilesrequestednum = 0; - totalfilesrequestedsize = 0; - - if (fileneeded == NULL) - I_Error("CL_FinishedFileList: fileneeded == NULL"); - - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD) - { - totalfilesrequestednum++; - totalfilesrequestedsize += fileneeded[i].totalsize; - } - - if (totalfilesrequestedsize>>20 >= 100) - downloadsize = Z_StrDup(va("%uM",totalfilesrequestedsize>>20)); - else - downloadsize = Z_StrDup(va("%uK",totalfilesrequestedsize>>10)); - - if (serverisfull) - M_StartMessage(va(M_GetText( - "This server is full!\n" - "Download of %s additional content\nis required to join.\n" - "\n" - "You may download, load server addons,\nand wait for a slot.\n" - "\n" - "Press ENTER to continue\nor ESC to cancel.\n" - ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); - else - M_StartMessage(va(M_GetText( - "Download of %s additional content\nis required to join.\n" - "\n" - "Press ENTER to continue\nor ESC to cancel.\n" - ), downloadsize), M_ConfirmConnect, MM_EVENTHANDLER); - - Z_Free(downloadsize); - cl_mode = CL_CONFIRMCONNECT; - curfadevalue = 0; - } - return true; -} - -static const char * InvalidServerReason (serverinfo_pak *info) -{ -#define EOT "\nPress ESC\n" - - // Magic number for new packet format - if (info->_255 != 255) - { - return - "Outdated server (version unknown).\n" EOT; - } - - if (strncmp(info->application, SRB2APPLICATION, sizeof - info->application)) - { - return va( - "%s cannot connect\n" - "to %s servers.\n" EOT, - SRB2APPLICATION, - info->application); - } - - if ( - info->packetversion != PACKETVERSION || - info->version != VERSION || - info->subversion != SUBVERSION - ){ - return va( - "Incompatible %s versions.\n" - "(server version %d.%d.%d)\n" EOT, - SRB2APPLICATION, - info->version / 100, - info->version % 100, - info->subversion); - } - - switch (info->refusereason) - { - case REFUSE_BANNED: - return - "You have been banned\n" - "from the server.\n" EOT; - case REFUSE_JOINS_DISABLED: - return - "The server is not accepting\n" - "joins for the moment.\n" EOT; - case REFUSE_SLOTS_FULL: - return va( - "Maximum players reached: %d\n" EOT, - info->maxplayer); - default: - if (info->refusereason) - { - return - "You can't join.\n" - "I don't know why,\n" - "but you can't join.\n" EOT; - } - } - - return NULL; - -#undef EOT -} - -/** Called by CL_ServerConnectionTicker - * - * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit. - * \return False if the connection was aborted - * \sa CL_ServerConnectionTicker - * \sa CL_ConnectToServer - * - */ -static boolean CL_ServerConnectionSearchTicker(tic_t *asksent) -{ - INT32 i; - - // serverlist is updated by GetPackets - if (serverlistcount > 0) - { - // This can be a response to our broadcast request - if (servernode == -1 || servernode >= MAXNETNODES) - { - i = 0; - servernode = serverlist[i].node; - CONS_Printf(M_GetText("Found, ")); - } - else - { - i = SL_SearchServer(servernode); - if (i < 0) - return true; - } - - if (client) - { - serverinfo_pak *info = &serverlist[i].info; - - if (info->refusereason == REFUSE_SLOTS_FULL) - serverisfull = true; - else - { - const char *reason = InvalidServerReason(info); - - // Quit here rather than downloading files - // and being refused later. - if (reason) - { - char *message = Z_StrDup(reason); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(message, NULL, MM_NOTHING); - Z_Free(message); - return false; - } - } - - D_ParseFileneeded(info->fileneedednum, info->fileneeded, 0); - - if (info->flags & SV_LOTSOFADDONS) - { - cl_mode = CL_ASKFULLFILELIST; - cl_lastcheckedfilecount = 0; - return true; - } - - cl_mode = CL_CHECKFILES; - } - else - { - cl_mode = CL_ASKJOIN; // files need not be checked for the server. - *asksent = 0; - } - - return true; - } - - // Ask the info to the server (askinfo packet) - if (*asksent + NEWTICRATE < I_GetTime()) - { - SendAskInfo(servernode); - *asksent = I_GetTime(); - } - - return true; -} - -/** Called by CL_ConnectToServer - * - * \param tmpsave The name of the gamestate file??? - * \param oldtic Used for knowing when to poll events and redraw - * \param asksent ??? - * \return False if the connection was aborted - * \sa CL_ServerConnectionSearchTicker - * \sa CL_ConnectToServer - * - */ -static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic_t *asksent) -{ - boolean waitmore; - - switch (cl_mode) - { - case CL_SEARCHING: - if (!CL_ServerConnectionSearchTicker(asksent)) - return false; - break; - - case CL_ASKFULLFILELIST: - if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved - cl_mode = CL_CHECKFILES; - else if (fileneedednum != cl_lastcheckedfilecount || I_GetTime() >= *asksent) - { - if (CL_AskFileList(fileneedednum)) - { - cl_lastcheckedfilecount = fileneedednum; - *asksent = I_GetTime() + NEWTICRATE; - } - } - break; - case CL_CHECKFILES: - if (!CL_FinishedFileList()) - return false; - break; - case CL_DOWNLOADFILES: - waitmore = false; - for (INT32 i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_DOWNLOADING - || fileneeded[i].status == FS_REQUESTED) - { - waitmore = true; - break; - } - if (waitmore) - break; // exit the case - - Snake_Free(&snake); - - cl_mode = CL_LOADFILES; - break; - case CL_LOADFILES: - if (CL_LoadServerFiles()) - { - FreeFileNeeded(); - *asksent = 0; // This ensures the first join request is right away - firstconnectattempttime = I_GetTime(); - cl_mode = CL_ASKJOIN; - } - break; - case CL_ASKJOIN: - 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 - CL_PrepareDownloadSaveGame(tmpsave); - - 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; - case CL_DOWNLOADSAVEGAME: - // At this state, the first (and only) needed file is the gamestate - if (fileneeded[0].status == FS_FOUND) - { - // Gamestate is now handled within CL_LoadReceivedSavegame() - CL_LoadReceivedSavegame(false); - cl_mode = CL_CONNECTED; - } // don't break case continue to CL_CONNECTED - else - break; - - case CL_CONNECTED: - case CL_CONFIRMCONNECT: //logic is handled by M_ConfirmConnect - default: - break; - - // Connection closed by cancel, timeout or refusal. - case CL_ABORTED: - cl_mode = CL_SEARCHING; - return false; - } - - GetPackets(); - Net_AckTicker(); - - // Call it only once by tic - if (*oldtic != I_GetTime()) - { - I_OsPolling(); - - if (cl_mode == CL_CONFIRMCONNECT) - D_ProcessEvents(); //needed for menu system to receive inputs - else - { - // my hand has been forced and I am dearly sorry for this awful hack :vomit: - for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) - { - G_MapEventsToControls(&events[eventtail]); - } - } - - if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || 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); - - Snake_Free(&snake); - - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - memset(gamekeydown, 0, NUMKEYS); - return false; - } - else if (cl_mode == CL_DOWNLOADFILES && snake) - Snake_Update(snake); - - if (client && (cl_mode == CL_DOWNLOADFILES || cl_mode == CL_DOWNLOADSAVEGAME)) - FileReceiveTicker(); - - *oldtic = I_GetTime(); - - if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) - { - if (!snake) - { - F_MenuPresTicker(true); // title sky - F_TitleScreenTicker(true); - 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(); - S_UpdateSounds(); - S_UpdateClosedCaptions(); - } - } - else - { - I_Sleep(cv_sleep.value); - I_UpdateTime(cv_timescale.value); - } - - return true; -} - -#define TMPSAVENAME "$$$.sav" - -void CL_ConnectToServer(void) -{ - INT32 pnumnodes, nodewaited = doomcom->numnodes, i; - tic_t oldtic; - tic_t asksent; - char tmpsave[256]; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - lastfilenum = -1; - - cl_mode = CL_SEARCHING; - - // Don't get a corrupt savegame error because tmpsave already exists - if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) - I_Error("Can't delete %s\n", tmpsave); - - if (netgame) - { - if (servernode < 0 || servernode >= MAXNETNODES) - CONS_Printf(M_GetText("Searching for a server...\n")); - else - CONS_Printf(M_GetText("Contacting the server...\n")); - } - - if (gamestate == GS_INTERMISSION) - Y_EndIntermission(); // clean up intermission graphics etc - - DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); - G_SetGamestate(GS_WAITINGPLAYERS); - wipegamestate = GS_WAITINGPLAYERS; - - ClearAdminPlayers(); - pnumnodes = 1; - oldtic = I_GetTime() - 1; - - asksent = (tic_t) - TICRATE; - firstconnectattempttime = I_GetTime(); - - i = SL_SearchServer(servernode); - - if (i != -1) - { - char *gametypestr = serverlist[i].info.gametypename; - CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); - gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0'; - CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); - CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, - serverlist[i].info.version%100, serverlist[i].info.subversion); - } - SL_ClearServerList(servernode); - - do - { - // If the connection was aborted for some reason, leave - if (!CL_ServerConnectionTicker(tmpsave, &oldtic, &asksent)) - return; - - if (server) - { - pnumnodes = 0; - for (i = 0; i < MAXNETNODES; i++) - if (netnodes[i].ingame) - pnumnodes++; - } - } - while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes)))); - - DEBFILE(va("Synchronisation Finished\n")); - - displayplayer = consoleplayer; -} - -/** Called when a PT_SERVERINFO packet is received - * - * \param node The packet sender - * \note What happens if the packet comes from a client or something like that? - * - */ -void PT_ServerInfo(SINT8 node) -{ - // compute ping in ms - const tic_t ticnow = I_GetTime(); - const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time); - const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE; - netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff); - netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; - netbuffer->u.serverinfo.application - [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; - netbuffer->u.serverinfo.gametypename - [sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0'; - - SL_InsertServer(&netbuffer->u.serverinfo, node); -} - -// Helper function for packets that should only be sent by the server -// If it is NOT from the server, bail out and close the connection! -static boolean ServerOnly(SINT8 node) -{ - if (node == servernode) - return false; - - Net_CloseConnection(node); - return true; -} - -void PT_MoreFilesNeeded(SINT8 node) -{ - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - return; - } - if (ServerOnly(node)) - return; - if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) - { - D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); - if (!netbuffer->u.filesneededcfg.more) - cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list - } -} - -// Negative response of client join request -void PT_ServerRefuse(SINT8 node) -{ - if (server && serverrunning) - { // But wait I thought I'm the server? - Net_CloseConnection(node); - return; - } - if (ServerOnly(node)) - return; - if (cl_mode == CL_WAITJOINRESPONSE) - { - // Save the reason so it can be displayed after quitting the netgame - char *reason = strdup(netbuffer->u.serverrefuse.reason); - if (!reason) - I_Error("Out of memory!\n"); - - 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); - return; - } - - 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. - cl_mode = CL_ABORTED; - } -} - -// Positive response of client join request -void PT_ServerCFG(SINT8 node) -{ - if (server && serverrunning && node != servernode) - { // but wait I thought I'm the server? - Net_CloseConnection(node); - return; - } - if (ServerOnly(node)) - return; - /// \note how would this happen? and is it doing the right thing if it does? - if (cl_mode != CL_WAITJOINRESPONSE) - return; - - if (client) - { - maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic); - G_SetGametype(netbuffer->u.servercfg.gametype); - modifiedgame = netbuffer->u.servercfg.modifiedgame; - memcpy(server_context, netbuffer->u.servercfg.server_context, 8); - } - - netnodes[(UINT8)servernode].ingame = true; - serverplayer = netbuffer->u.servercfg.serverplayer; - doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum); - mynode = netbuffer->u.servercfg.clientnode; - if (serverplayer >= 0) - playernode[(UINT8)serverplayer] = servernode; - - if (netgame) - CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n")); - DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode)); - - /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? - /// Shouldn't they be downloaded even at intermission time? - /// Also, according to PT_ClientJoin, the server will send the savegame even during intermission... - if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || - netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) - cl_mode = CL_DOWNLOADSAVEGAME; - else - cl_mode = CL_CONNECTED; -} diff --git a/src/netcode/client_connection.h b/src/netcode/client_connection.h deleted file mode 100644 index 74cff61ff..000000000 --- a/src/netcode/client_connection.h +++ /dev/null @@ -1,61 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 client_connection.h -/// \brief Client connection handling - -#ifndef __D_CLIENT_CONNECTION__ -#define __D_CLIENT_CONNECTION__ - -#include "../doomtype.h" -#include "d_clisrv.h" - -#define MAXSERVERLIST (MAXNETNODES-1) - -typedef struct -{ - SINT8 node; - serverinfo_pak info; -} serverelem_t; - -typedef enum -{ - CL_SEARCHING, - CL_CHECKFILES, - CL_DOWNLOADFILES, - CL_ASKJOIN, - CL_LOADFILES, - CL_WAITJOINRESPONSE, - CL_DOWNLOADSAVEGAME, - CL_CONNECTED, - CL_ABORTED, - CL_ASKFULLFILELIST, - CL_CONFIRMCONNECT -} cl_mode_t; - -extern serverelem_t serverlist[MAXSERVERLIST]; -extern UINT32 serverlistcount; - -extern cl_mode_t cl_mode; -extern boolean serverisfull; //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 -extern tic_t firstconnectattempttime; -extern UINT8 mynode; // my address pointofview server - -void CL_QueryServerList(msg_server_t *list); -void CL_UpdateServerList(boolean internetsearch, INT32 room); - -void CL_ConnectToServer(void); -boolean CL_SendJoin(void); - -void PT_ServerInfo(SINT8 node); -void PT_MoreFilesNeeded(SINT8 node); -void PT_ServerRefuse(SINT8 node); -void PT_ServerCFG(SINT8 node); - -#endif diff --git a/src/netcode/commands.c b/src/netcode/commands.c deleted file mode 100644 index 4d9a48b6b..000000000 --- a/src/netcode/commands.c +++ /dev/null @@ -1,484 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 commands.c -/// \brief Various netgame commands, such as kick and ban - -#include "commands.h" -#include "d_clisrv.h" -#include "client_connection.h" -#include "net_command.h" -#include "d_netcmd.h" -#include "d_net.h" -#include "i_net.h" -#include "protocol.h" -#include "../byteptr.h" -#include "../d_main.h" -#include "../g_game.h" -#include "../w_wad.h" -#include "../z_zone.h" -#include "../doomstat.h" -#include "../doomdef.h" -#include "../r_local.h" -#include -#include -#include -#include - -typedef struct banreason_s -{ - char *reason; - struct banreason_s *prev; //-1 - struct banreason_s *next; //+1 -} banreason_t; - -static banreason_t *reasontail = NULL; //last entry, use prev -static banreason_t *reasonhead = NULL; //1st entry, use next - -void Ban_Add(const char *reason) -{ - banreason_t *reasonlist = malloc(sizeof(*reasonlist)); - - if (!reasonlist) - return; - if (!reason) - reason = "NA"; - - reasonlist->next = NULL; - reasonlist->reason = Z_StrDup(reason); - if ((reasonlist->prev = reasontail) == NULL) - reasonhead = reasonlist; - else - reasontail->next = reasonlist; - reasontail = reasonlist; -} - -static void Ban_Clear(void) -{ - banreason_t *temp; - - I_ClearBans(); - - reasontail = NULL; - - while (reasonhead) - { - temp = reasonhead->next; - Z_Free(reasonhead->reason); - free(reasonhead); - reasonhead = temp; - } -} - -void Ban_Load_File(boolean warning) -{ - FILE *f; - const char *address, *mask; - char buffer[MAX_WADPATH]; - - if (!I_ClearBans) - return; - - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); - - if (!f) - { - if (warning) - CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n")); - return; - } - - Ban_Clear(); - - for (size_t i=0; fgets(buffer, (int)sizeof(buffer), f); i++) - { - address = strtok(buffer, " \t\r\n"); - mask = strtok(NULL, " \t\r\n"); - - I_SetBanAddress(address, mask); - - Ban_Add(strtok(NULL, "\r\n")); - } - - fclose(f); -} - -void D_SaveBan(void) -{ - FILE *f; - banreason_t *reasonlist = reasonhead; - const char *address, *mask; - const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); - - if (!reasonhead) - { - remove(path); - return; - } - - f = fopen(path, "w"); - - if (!f) - { - CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n")); - return; - } - - for (size_t i = 0;(address = I_GetBanAddress(i)) != NULL;i++) - { - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - fprintf(f, "%s 0", address); - else - fprintf(f, "%s %s", address, mask); - - if (reasonlist && reasonlist->reason) - fprintf(f, " %s\n", reasonlist->reason); - else - fprintf(f, " %s\n", "NA"); - - if (reasonlist) reasonlist = reasonlist->next; - } - - fclose(f); -} - -void Command_ShowBan(void) //Print out ban list -{ - size_t i; - const char *address, *mask; - banreason_t *reasonlist = reasonhead; - - if (I_GetBanAddress) - CONS_Printf(M_GetText("Ban List:\n")); - else - return; - - for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++) - { - if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL) - CONS_Printf("%s: %s ", sizeu1(i+1), address); - else - CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask); - - if (reasonlist && reasonlist->reason) - CONS_Printf("(%s)\n", reasonlist->reason); - else - CONS_Printf("\n"); - - if (reasonlist) reasonlist = reasonlist->next; - } - - if (i == 0 && !address) - CONS_Printf(M_GetText("(empty)\n")); -} - -void Command_ClearBans(void) -{ - if (!I_ClearBans) - return; - - Ban_Clear(); - D_SaveBan(); -} - -void Command_Ban(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("Ban : ban and kick a player\n")); - return; - } - - if (!netgame) // Don't kick Tails in splitscreen! - { - CONS_Printf(M_GetText("This only works in a netgame.\n")); - return; - } - - if (server || IsPlayerAdmin(consoleplayer)) - { - UINT8 buf[3 + MAX_REASONLENGTH]; - UINT8 *p = buf; - const SINT8 pn = nametonum(COM_Argv(1)); - const INT32 node = playernode[(INT32)pn]; - - if (pn == -1 || pn == 0) - return; - - WRITEUINT8(p, pn); - - if (server && I_Ban && !I_Ban(node)) // only the server is allowed to do this right now - { - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); - WRITEUINT8(p, KICK_MSG_GO_AWAY); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - if (server) // only the server is allowed to do this right now - { - Ban_Add(COM_Argv(2)); - D_SaveBan(); // save the ban list - } - - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_BANNED); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t j = COM_Argc(); - char message[MAX_REASONLENGTH]; - - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (size_t i = 3; i < j; i++) - { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); - } - - WRITEUINT8(p, KICK_MSG_CUSTOM_BAN); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } - } - } - else - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - -} - -void Command_BanIP(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("banip : ban an ip address\n")); - return; - } - - if (server) // Only the server can use this, otherwise does nothing. - { - const char *address = (COM_Argv(1)); - const char *reason; - - if (COM_Argc() == 2) - reason = NULL; - else - reason = COM_Argv(2); - - - if (I_SetBanAddress && I_SetBanAddress(address, NULL)) - { - if (reason) - CONS_Printf("Banned IP address %s for: %s\n", address, reason); - else - CONS_Printf("Banned IP address %s\n", address); - - Ban_Add(reason); - D_SaveBan(); - } - else - { - return; - } - } -} - -void Command_ReloadBan(void) //recheck ban.txt -{ - Ban_Load_File(true); -} - -void Command_Kick(void) -{ - if (COM_Argc() < 2) - { - CONS_Printf(M_GetText("kick : kick a player\n")); - return; - } - - if (!netgame) // Don't kick Tails in splitscreen! - { - CONS_Printf(M_GetText("This only works in a netgame.\n")); - return; - } - - if (server || IsPlayerAdmin(consoleplayer)) - { - UINT8 buf[3 + MAX_REASONLENGTH]; - UINT8 *p = buf; - const SINT8 pn = nametonum(COM_Argv(1)); - - if (pn == -1 || pn == 0) - return; - - // Special case if we are trying to kick a player who is downloading the game state: - // trigger a timeout instead of kicking them, because a kick would only - // take effect after they have finished downloading - if (server && playernode[pn] != UINT8_MAX && netnodes[playernode[pn]].sendingsavegame) - { - Net_ConnectionTimeout(playernode[pn]); - return; - } - - WRITESINT8(p, pn); - - if (COM_Argc() == 2) - { - WRITEUINT8(p, KICK_MSG_GO_AWAY); - SendNetXCmd(XD_KICK, &buf, 2); - } - else - { - size_t j = COM_Argc(); - char message[MAX_REASONLENGTH]; - - //Steal from the motd code so you don't have to put the reason in quotes. - strlcpy(message, COM_Argv(2), sizeof message); - for (size_t i = 3; i < j; i++) - { - strlcat(message, " ", sizeof message); - strlcat(message, COM_Argv(i), sizeof message); - } - - WRITEUINT8(p, KICK_MSG_CUSTOM_KICK); - WRITESTRINGN(p, message, MAX_REASONLENGTH); - SendNetXCmd(XD_KICK, &buf, p - buf); - } - } - else - CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); -} - -void Command_connect(void) -{ - if (COM_Argc() < 2 || *COM_Argv(1) == 0) - { - CONS_Printf(M_GetText( - "Connect (port): connect to a server\n" - "Connect ANY: connect to the first lan server found\n" - //"Connect SELF: connect to your own server.\n" - )); - return; - } - - if (Playing() || titledemo) - { - CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); - return; - } - - server = false; -/* - if (!stricmp(COM_Argv(1), "self")) - { - servernode = 0; - server = true; - /// \bug should be but... - //SV_SpawnServer(); - } - else -*/ - { - // used in menu to connect to a server in the list - if (netgame && !stricmp(COM_Argv(1), "node")) - { - servernode = (SINT8)atoi(COM_Argv(2)); - } - else if (netgame) - { - CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); - return; - } - else if (I_NetOpenSocket) - { - I_NetOpenSocket(); - netgame = true; - multiplayer = true; - - if (!stricmp(COM_Argv(1), "any")) - servernode = BROADCASTADDR; - else if (I_NetMakeNodewPort) - { - if (COM_Argc() >= 3) // address AND port - servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2)); - else // address only, or address:port - servernode = I_NetMakeNode(COM_Argv(1)); - } - else - { - CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); - D_CloseConnection(); - return; - } - } - else - CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n")); - } - - splitscreen = false; - SplitScreen_OnChange(); - botingame = false; - botskin = 0; - CL_ConnectToServer(); -} - -void Command_GetPlayerNum(void) -{ - for (INT32 i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - { - if (serverplayer == i) - CONS_Printf(M_GetText("num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); - else - CONS_Printf(M_GetText("\x82num:%2d node:%2d %s\n"), i, playernode[i], player_names[i]); - } -} - -/** Lists all players and their player numbers. - * - * \sa Command_GetPlayerNum - */ -void Command_Nodes(void) -{ - size_t maxlen = 0; - const char *address; - - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - const size_t plen = strlen(player_names[i]); - if (playeringame[i] && plen > maxlen) - maxlen = plen; - } - - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]); - - if (playernode[i] != UINT8_MAX) - { - CONS_Printf(" - node %.2d", playernode[i]); - if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL) - CONS_Printf(" - %s", address); - } - - if (IsPlayerAdmin(i)) - CONS_Printf(M_GetText(" (verified admin)")); - - if (players[i].spectator) - CONS_Printf(M_GetText(" (spectator)")); - - CONS_Printf("\n"); - } - } -} diff --git a/src/netcode/commands.h b/src/netcode/commands.h deleted file mode 100644 index 5ff4d1cae..000000000 --- a/src/netcode/commands.h +++ /dev/null @@ -1,33 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 commands.h -/// \brief Various netgame commands, such as kick and ban - -#ifndef __COMMANDS__ -#define __COMMANDS__ - -#include "../doomdef.h" - -#define MAX_REASONLENGTH 30 - -void Ban_Add(const char *reason); -void D_SaveBan(void); -void Ban_Load_File(boolean warning); -void Command_ShowBan(void); -void Command_ClearBans(void); -void Command_Ban(void); -void Command_BanIP(void); -void Command_ReloadBan(void); -void Command_Kick(void); -void Command_connect(void); -void Command_GetPlayerNum(void); -void Command_Nodes(void); - -#endif diff --git a/src/netcode/d_clisrv.c b/src/netcode/d_clisrv.c deleted file mode 100644 index 18eae580c..000000000 --- a/src/netcode/d_clisrv.c +++ /dev/null @@ -1,1564 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 d_clisrv.c -/// \brief SRB2 Network game communication and protocol, all OS independent parts. - -#include -#ifdef __GNUC__ -#include //for unlink -#endif - -#include "../i_time.h" -#include "i_net.h" -#include "../i_system.h" -#include "../i_video.h" -#include "d_net.h" -#include "../d_main.h" -#include "../g_game.h" -#include "../st_stuff.h" -#include "../hu_stuff.h" -#include "../keys.h" -#include "../g_input.h" -#include "../i_gamepad.h" -#include "../m_menu.h" -#include "../console.h" -#include "d_netfil.h" -#include "../byteptr.h" -#include "../p_saveg.h" -#include "../z_zone.h" -#include "../p_local.h" -#include "../p_haptic.h" -#include "../m_misc.h" -#include "../am_map.h" -#include "../m_random.h" -#include "mserv.h" -#include "../y_inter.h" -#include "../r_local.h" -#include "../m_argv.h" -#include "../p_setup.h" -#include "../lzf.h" -#include "../lua_script.h" -#include "../lua_hook.h" -#include "../lua_libs.h" -#include "../md5.h" -#include "../m_perfstats.h" -#include "server_connection.h" -#include "client_connection.h" -#include "tic_command.h" -#include "net_command.h" -#include "gamestate.h" -#include "commands.h" -#include "protocol.h" - -// -// NETWORKING -// -// gametic is the tic about to (or currently being) run -// Server: -// maketic is the tic that hasn't had control made for it yet -// nettics is the tic for each node -// firstticstosend is the lowest value of nettics -// Client: -// neededtic is the tic needed by the client to run the game -// firstticstosend is used to optimize a condition -// Normally maketic >= gametic > 0 - -boolean server = true; // true or false but !server == client -boolean serverrunning = false; -INT32 serverplayer = 0; -char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support) - -netnode_t netnodes[MAXNETNODES]; - -// Server specific vars -UINT8 playernode[MAXPLAYERS]; - -UINT16 pingmeasurecount = 1; -UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone. -UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. -static INT32 pingtimeout[MAXPLAYERS]; -tic_t servermaxping = 800; // server's max ping. Defaults to 800 - -tic_t maketic; - -INT16 consistancy[BACKUPTICS]; - -// true when a player is connecting or disconnecting so that the gameplay has stopped in its tracks -boolean hu_stopped = false; - -UINT8 adminpassmd5[16]; -boolean adminpasswordset = false; - -tic_t neededtic; -SINT8 servernode = 0; // the number of the server node - -boolean acceptnewnode = true; - -UINT16 software_MAXPACKETLENGTH; - -static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; -consvar_t cv_netticbuffer = CVAR_INIT ("netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL); - -static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; -consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); - -consvar_t cv_blamecfail = CVAR_INIT ("blamecfail", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; -consvar_t cv_playbackspeed = CVAR_INIT ("playbackspeed", "1", 0, playbackspeed_cons_t, NULL); - -void ResetNode(INT32 node) -{ - memset(&netnodes[node], 0, sizeof(*netnodes)); - netnodes[node].player = -1; - netnodes[node].player2 = -1; -} - -void CL_Reset(void) -{ - if (metalrecording) - G_StopMetalRecording(false); - if (metalplayback) - G_StopMetalDemo(); - if (demorecording) - G_CheckDemoStatus(); - - // reset client/server code - DEBFILE(va("\n-=-=-=-=-=-=-= Client reset =-=-=-=-=-=-=-\n\n")); - - if (servernode > 0 && servernode < MAXNETNODES) - { - netnodes[(UINT8)servernode].ingame = false; - Net_CloseConnection(servernode); - } - D_CloseConnection(); // netgame = false - multiplayer = false; - servernode = 0; - server = true; - doomcom->numnodes = 1; - doomcom->numslots = 1; - SV_StopServer(); - SV_ResetServer(); - - // make sure we don't leave any fileneeded gunk over from a failed join - FreeFileNeeded(); - fileneedednum = 0; - - totalfilesrequestednum = 0; - totalfilesrequestedsize = 0; - firstconnectattempttime = 0; - serverisfull = false; - connectiontimeout = (tic_t)cv_nettimeout.value; //reset this temporary hack - - // D_StartTitle should get done now, but the calling function will handle it -} - -// -// CL_ClearPlayer -// -// Clears the player data so that a future client can use this slot -// -void CL_ClearPlayer(INT32 playernum) -{ - if (players[playernum].mo) - P_RemoveMobj(players[playernum].mo); - memset(&players[playernum], 0, sizeof (player_t)); - memset(playeraddress[playernum], 0, sizeof(*playeraddress)); -} - -// Xcmd XD_ADDPLAYER -static void Got_AddPlayer(UINT8 **p, INT32 playernum) -{ - INT16 node, newplayernum; - boolean splitscreenplayer; - boolean rejoined; - player_t *newplayer; - - if (playernum != serverplayer && !IsPlayerAdmin(playernum)) - { - // protect against hacked/buggy client - CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]); - if (server) - SendKick(playernum, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - return; - } - - node = READUINT8(*p); - newplayernum = READUINT8(*p); - splitscreenplayer = newplayernum & 0x80; - newplayernum &= ~0x80; - - rejoined = playeringame[newplayernum]; - - if (!rejoined) - { - // Clear player before joining, lest some things get set incorrectly - // HACK: don't do this for splitscreen, it relies on preset values - if (!splitscreen && !botingame) - CL_ClearPlayer(newplayernum); - playeringame[newplayernum] = true; - G_AddPlayer(newplayernum); - if (newplayernum+1 > doomcom->numslots) - doomcom->numslots = (INT16)(newplayernum+1); - - if (server && I_GetNodeAddress) - { - const char *address = I_GetNodeAddress(node); - char *port = NULL; - if (address) // MI: fix msvcrt.dll!_mbscat crash? - { - strcpy(playeraddress[newplayernum], address); - port = strchr(playeraddress[newplayernum], ':'); - if (port) - *port = '\0'; - } - } - } - - newplayer = &players[newplayernum]; - - newplayer->jointime = 0; - newplayer->quittime = 0; - - READSTRINGN(*p, player_names[newplayernum], MAXPLAYERNAME); - - // the server is creating my player - if (node == mynode) - { - playernode[newplayernum] = 0; // for information only - if (!splitscreenplayer) - { - consoleplayer = newplayernum; - displayplayer = newplayernum; - secondarydisplayplayer = newplayernum; - DEBFILE("spawning me\n"); - ticcmd_oldangleturn[0] = newplayer->oldrelangleturn; - } - else - { - secondarydisplayplayer = newplayernum; - DEBFILE("spawning my brother\n"); - if (botingame) - newplayer->bot = 1; - ticcmd_oldangleturn[1] = newplayer->oldrelangleturn; - } - P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16)); - D_SendPlayerConfig(); - addedtogame = true; - - if (rejoined) - { - if (newplayer->mo) - { - newplayer->viewheight = 41*newplayer->height/48; - - if (newplayer->mo->eflags & MFE_VERTICALFLIP) - newplayer->viewz = newplayer->mo->z + newplayer->mo->height - newplayer->viewheight; - else - newplayer->viewz = newplayer->mo->z + newplayer->viewheight; - } - - // wake up the status bar - ST_Start(); - // wake up the heads up text - HU_Start(); - - if (camera.chase && !splitscreenplayer) - P_ResetCamera(newplayer, &camera); - if (camera2.chase && splitscreenplayer) - P_ResetCamera(newplayer, &camera2); - } - } - - if (netgame) - { - char joinmsg[256]; - - if (rejoined) - strcpy(joinmsg, M_GetText("\x82*%s has rejoined the game (player %d)")); - else - strcpy(joinmsg, M_GetText("\x82*%s has joined the game (player %d)")); - strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum)); - - // Merge join notification + IP to avoid clogging console/chat - if (server && cv_showjoinaddress.value && I_GetNodeAddress) - { - const char *address = I_GetNodeAddress(node); - if (address) - strcat(joinmsg, va(" (%s)", address)); - } - - HU_AddChatText(joinmsg, false); - } - - if (server && multiplayer && motd[0] != '\0') - COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); - - if (!rejoined) - LUA_HookInt(newplayernum, HOOK(PlayerJoin)); -} - -static void UnlinkPlayerFromNode(INT32 playernum) -{ - INT32 node = playernode[playernum]; - - if (node == UINT8_MAX) - return; - - playernode[playernum] = UINT8_MAX; - - netnodes[node].numplayers--; - if (netnodes[node].numplayers <= 0) - { - netnodes[node].ingame = false; - Net_CloseConnection(node); - ResetNode(node); - } -} - -static void PT_ClientQuit(SINT8 node, INT32 netconsole) -{ - if (client) - return; - - if (netnodes[node].ingame && netconsole != -1 && playeringame[netconsole]) - SendKicksForNode(node, KICK_MSG_PLAYER_QUIT | KICK_MSG_KEEP_BODY); - - Net_CloseConnection(node); - netnodes[node].ingame = false; - netnodes[node].player = -1; - netnodes[node].player2 = -1; -} - -static void Got_KickCmd(UINT8 **p, INT32 playernum) -{ - INT32 pnum, msg; - char buf[3 + MAX_REASONLENGTH]; - char *reason = buf; - kickreason_t kickreason = KR_KICK; - boolean keepbody; - - pnum = READUINT8(*p); - msg = READUINT8(*p); - keepbody = (msg & KICK_MSG_KEEP_BODY) != 0; - msg &= ~KICK_MSG_KEEP_BODY; - - if (pnum == serverplayer && IsPlayerAdmin(playernum)) - { - CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n")); - - if (server) - COM_BufAddText("quit\n"); - - return; - } - - // Is playernum authorized to make this kick? - if (playernum != serverplayer && !IsPlayerAdmin(playernum) - && !(playernode[playernum] != UINT8_MAX && netnodes[playernode[playernum]].numplayers == 2 - && netnodes[playernode[playernum]].player2 == pnum)) - { - // We received a kick command from someone who isn't the - // server or admin, and who isn't in splitscreen removing - // player 2. Thus, it must be someone with a modified - // binary, trying to kick someone but without having - // authorization. - - // We deal with this by changing the kick reason to - // "consistency failure" and kicking the offending user - // instead. - - // Note: Splitscreen in netgames is broken because of - // this. Only the server has any idea of which players - // are using splitscreen on the same computer, so - // clients cannot always determine if a kick is - // legitimate. - - CONS_Alert(CONS_WARNING, M_GetText("Illegal kick command received from %s for player %d\n"), player_names[playernum], pnum); - - // In debug, print a longer message with more details. - // TODO Callum: Should we translate this? -/* - CONS_Debug(DBG_NETPLAY, - "So, you must be asking, why is this an illegal kick?\n" - "Well, let's take a look at the facts, shall we?\n" - "\n" - "playernum (this is the guy who did it), he's %d.\n" - "pnum (the guy he's trying to kick) is %d.\n" - "playernum's node is %d.\n" - "That node has %d players.\n" - "Player 2 on that node is %d.\n" - "pnum's node is %d.\n" - "That node has %d players.\n" - "Player 2 on that node is %d.\n" - "\n" - "If you think this is a bug, please report it, including all of the details above.\n", - playernum, pnum, - playernode[playernum], netnodes[playernode[playernum]].numplayers, - netnodes[playernode[playernum]].player2, - playernode[pnum], netnodes[playernode[pnum]].numplayers, - netnodes[playernode[pnum]].player2); -*/ - pnum = playernum; - msg = KICK_MSG_CON_FAIL; - keepbody = true; - } - - //CONS_Printf("\x82%s ", player_names[pnum]); - - // If a verified admin banned someone, the server needs to know about it. - // If the playernum isn't zero (the server) then the server needs to record the ban. - if (server && playernum && (msg == KICK_MSG_BANNED || msg == KICK_MSG_CUSTOM_BAN)) - { - if (I_Ban && !I_Ban(playernode[(INT32)pnum])) - CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n")); - else - Ban_Add(reason); - } - - switch (msg) - { - case KICK_MSG_GO_AWAY: - if (!players[pnum].quittime) - HU_AddChatText(va("\x82*%s has been kicked (No reason given)", player_names[pnum]), false); - kickreason = KR_KICK; - break; - case KICK_MSG_PING_HIGH: - HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false); - kickreason = KR_PINGLIMIT; - break; - case KICK_MSG_CON_FAIL: - HU_AddChatText(va("\x82*%s left the game (Synch failure)", player_names[pnum]), false); - kickreason = KR_SYNCH; - - if (M_CheckParm("-consisdump")) // Helps debugging some problems - { - CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum); - - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - CONS_Printf("-------------------------------------\n"); - CONS_Printf("Player %d: %s\n", i, player_names[i]); - CONS_Printf("Skin: %d\n", players[i].skin); - CONS_Printf("Color: %d\n", players[i].skincolor); - CONS_Printf("Speed: %d\n",players[i].speed>>FRACBITS); - if (players[i].mo) - { - if (!players[i].mo->skin) - CONS_Printf("Mobj skin: NULL!\n"); - else - CONS_Printf("Mobj skin: %s\n", ((skin_t *)players[i].mo->skin)->name); - CONS_Printf("Position: %d, %d, %d\n", players[i].mo->x, players[i].mo->y, players[i].mo->z); - if (!players[i].mo->state) - CONS_Printf("State: S_NULL\n"); - else - CONS_Printf("State: %d\n", (statenum_t)(players[i].mo->state-states)); - } - else - CONS_Printf("Mobj: NULL\n"); - CONS_Printf("-------------------------------------\n"); - } - } - break; - case KICK_MSG_TIMEOUT: - HU_AddChatText(va("\x82*%s left the game (Connection timeout)", player_names[pnum]), false); - kickreason = KR_TIMEOUT; - break; - case KICK_MSG_PLAYER_QUIT: - if (netgame && !players[pnum].quittime) // not splitscreen/bots or soulless body - HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false); - kickreason = KR_LEAVE; - break; - case KICK_MSG_BANNED: - HU_AddChatText(va("\x82*%s has been banned (No reason given)", player_names[pnum]), false); - kickreason = KR_BAN; - break; - case KICK_MSG_CUSTOM_KICK: - READSTRINGN(*p, reason, MAX_REASONLENGTH+1); - HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false); - kickreason = KR_KICK; - break; - case KICK_MSG_CUSTOM_BAN: - READSTRINGN(*p, reason, MAX_REASONLENGTH+1); - HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false); - kickreason = KR_BAN; - break; - } - - if (pnum == consoleplayer) - { - LUA_HookBool(false, HOOK(GameQuit)); -#ifdef DUMPCONSISTENCY - if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); -#endif - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - if (msg == KICK_MSG_CON_FAIL) - M_StartMessage(M_GetText("Server closed connection\n(synch failure)\nPress ESC\n"), NULL, MM_NOTHING); - else if (msg == KICK_MSG_PING_HIGH) - M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); - else if (msg == KICK_MSG_BANNED) - M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); - else if (msg == KICK_MSG_CUSTOM_KICK) - M_StartMessage(va(M_GetText("You have been kicked\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); - else if (msg == KICK_MSG_CUSTOM_BAN) - M_StartMessage(va(M_GetText("You have been banned\n(%s)\nPress ESC\n"), reason), NULL, MM_NOTHING); - else - M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); - } - else if (keepbody) - { - if (server) - UnlinkPlayerFromNode(pnum); - players[pnum].quittime = 1; - } - else - CL_RemovePlayer(pnum, kickreason); -} - -// If in a special stage, redistribute the player's -// spheres across the remaining players. -// I feel like this shouldn't even be in this file at all, but well. -static void RedistributeSpecialStageSpheres(INT32 playernum) -{ - if (!G_IsSpecialStage(gamemap) || D_NumPlayers() <= 1) - return; - - INT32 count = D_NumPlayers() - 1; - INT32 spheres = players[playernum].spheres; - INT32 rings = players[playernum].rings; - - while (spheres || rings) - { - INT32 sincrement = max(spheres / count, 1); - INT32 rincrement = max(rings / count, 1); - - INT32 n; - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || i == playernum) - continue; - - n = min(spheres, sincrement); - P_GivePlayerSpheres(&players[i], n); - spheres -= n; - - n = min(rings, rincrement); - P_GivePlayerRings(&players[i], n); - rings -= n; - } - } -} - -// -// CL_RemovePlayer -// -// Removes a player from the current game -// -void CL_RemovePlayer(INT32 playernum, kickreason_t reason) -{ - // Sanity check: exceptional cases (i.e. c-fails) can cause multiple - // kick commands to be issued for the same player. - if (!playeringame[playernum]) - return; - - if (server) - UnlinkPlayerFromNode(playernum); - - if (gametyperules & GTR_TEAMFLAGS) - P_PlayerFlagBurst(&players[playernum], false); // Don't take the flag with you! - - RedistributeSpecialStageSpheres(playernum); - - LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting - - // don't look through someone's view who isn't there - if (playernum == displayplayer) - { - // Call ViewpointSwitch hooks here. - // The viewpoint was forcibly changed. - LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); - displayplayer = consoleplayer; - } - - // Reset player data - CL_ClearPlayer(playernum); - - // remove avatar of player - playeringame[playernum] = false; - while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1) - doomcom->numslots--; - - // 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 - } - - LUA_InvalidatePlayer(&players[playernum]); - - if (G_TagGametype()) //Check if you still have a game. Location flexible. =P - P_CheckSurvivors(); - else if (gametyperules & GTR_RACE) - P_CheckRacers(); -} - -// -// D_QuitNetGame -// Called before quitting to leave a net game -// without hanging the other players -// -void D_QuitNetGame(void) -{ - mousegrabbedbylua = true; - I_UpdateMouseGrab(); - - if (!netgame || !netbuffer) - return; - - DEBFILE("===========================================================================\n" - " Quitting Game, closing connection\n" - "===========================================================================\n"); - - // abort send/receive of files - CloseNetFile(); - RemoveAllLuaFileTransfers(); - waitingforluafiletransfer = false; - waitingforluafilecommand = false; - - if (server) - { - netbuffer->packettype = PT_SERVERSHUTDOWN; - for (INT32 i = 0; i < MAXNETNODES; i++) - if (netnodes[i].ingame) - HSendPacket(i, true, 0, 0); -#ifdef MASTERSERVER - if (serverrunning && ms_RoomId > 0) - UnregisterServer(); -#endif - } - else if (servernode > 0 && servernode < MAXNETNODES && netnodes[(UINT8)servernode].ingame) - { - netbuffer->packettype = PT_CLIENTQUIT; - HSendPacket(servernode, true, 0, 0); - } - - D_CloseConnection(); - ClearAdminPlayers(); - - DEBFILE("===========================================================================\n" - " Log finish\n" - "===========================================================================\n"); -#ifdef DEBUGFILE - if (debugfile) - { - fclose(debugfile); - debugfile = NULL; - } -#endif -} - -void CL_HandleTimeout(void) -{ - LUA_HookBool(false, HOOK(GameQuit)); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING); -} - -void CL_AddSplitscreenPlayer(void) -{ - if (cl_mode == CL_CONNECTED) - CL_SendJoin(); -} - -void CL_RemoveSplitscreenPlayer(void) -{ - if (cl_mode != CL_CONNECTED) - return; - - SendKick(secondarydisplayplayer, KICK_MSG_PLAYER_QUIT); -} - -void SV_ResetServer(void) -{ - // +1 because this command will be executed in com_executebuffer in - // tryruntic so gametic will be incremented, anyway maketic > gametic - // is not an issue - - maketic = gametic + 1; - neededtic = maketic; - tictoclear = maketic; - - joindelay = 0; - - for (INT32 i = 0; i < MAXNETNODES; i++) - ResetNode(i); - - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - LUA_InvalidatePlayer(&players[i]); - playeringame[i] = false; - playernode[i] = UINT8_MAX; - memset(playeraddress[i], 0, sizeof(*playeraddress)); - sprintf(player_names[i], "Player %d", i + 1); - adminplayers[i] = -1; // Populate the entire adminplayers array with -1. - } - - memset(player_name_changes, 0, sizeof player_name_changes); - - mynode = 0; - cl_packetmissed = false; - cl_redownloadinggamestate = false; - - if (dedicated) - { - netnodes[0].ingame = true; - serverplayer = 0; - } - else - serverplayer = consoleplayer; - - if (server) - servernode = 0; - - doomcom->numslots = 0; - - // clear server_context - memset(server_context, '-', 8); - - CV_RevertNetVars(); - - DEBFILE("\n-=-=-=-=-=-=-= Server Reset =-=-=-=-=-=-=-\n\n"); -} - -static inline void SV_GenContext(void) -{ - // generate server_context, as exactly 8 bytes of randomly mixed A-Z and a-z - // (hopefully M_Random is initialized!! if not this will be awfully silly!) - for (UINT8 i = 0; i < 8; i++) - { - const char a = M_RandomKey(26*2); - if (a < 26) // uppercase - server_context[i] = 'A'+a; - else // lowercase - server_context[i] = 'a'+(a-26); - } -} - -void SV_SpawnServer(void) -{ - if (demoplayback) - G_StopDemo(); // reset engine parameter - if (metalplayback) - G_StopMetalDemo(); - - if (!serverrunning) - { - CONS_Printf(M_GetText("Starting Server....\n")); - serverrunning = true; - SV_ResetServer(); - SV_GenContext(); - if (netgame && I_NetOpenSocket) - { - I_NetOpenSocket(); -#ifdef MASTERSERVER - if (ms_RoomId > 0) - RegisterServer(); -#endif - } - - // non dedicated server just connect to itself - if (!dedicated) - CL_ConnectToServer(); - else doomcom->numslots = 1; - } -} - -// called at singleplayer start and stopdemo -void SV_StartSinglePlayerServer(void) -{ - server = true; - netgame = false; - multiplayer = false; - G_SetGametype(GT_COOP); - - // no more tic the game with this settings! - SV_StopServer(); - - if (splitscreen) - multiplayer = true; -} - -void SV_StopServer(void) -{ - if (gamestate == GS_INTERMISSION) - Y_EndIntermission(); - gamestate = wipegamestate = GS_NULL; - - localtextcmd[0] = 0; - localtextcmd2[0] = 0; - - for (tic_t i = firstticstosend; i < firstticstosend + BACKUPTICS; i++) - D_Clearticcmd(i); - - consoleplayer = 0; - cl_mode = CL_SEARCHING; - maketic = gametic+1; - neededtic = maketic; - serverrunning = false; -} - -/** Called when a PT_SERVERSHUTDOWN packet is received - * - * \param node The packet sender (should be the server) - * - */ -static void PT_ServerShutdown(SINT8 node) -{ - if (node != servernode || server || cl_mode == CL_SEARCHING) - return; - - (void)node; - LUA_HookBool(false, HOOK(GameQuit)); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); -} - -static void PT_Login(SINT8 node, INT32 netconsole) -{ - (void)node; - - if (client) - return; - -#ifndef NOMD5 - UINT8 finalmd5[16];/* Well, it's the cool thing to do? */ - - if (doomcom->datalength < 16)/* ignore partial sends */ - return; - - if (!adminpasswordset) - { - CONS_Printf(M_GetText("Password from %s failed (no password set).\n"), player_names[netconsole]); - return; - } - - // Do the final pass to compare with the sent md5 - D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", netconsole), &finalmd5); - - if (!memcmp(netbuffer->u.md5sum, finalmd5, 16)) - { - CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[netconsole]); - COM_BufInsertText(va("promote %d\n", netconsole)); // do this immediately - } - else - CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[netconsole]); -#else - (void)netconsole; -#endif -} - -static void PT_AskLuaFile(SINT8 node) -{ - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_ASKED) - AddLuaFileToSendQueue(node, luafiletransfers->realfilename); -} - -static void PT_HasLuaFile(SINT8 node) -{ - if (server && luafiletransfers && luafiletransfers->nodestatus[node] == LFTNS_SENDING) - SV_HandleLuaFileSent(node); -} - -static void PT_SendingLuaFile(SINT8 node) -{ - (void)node; - - if (client) - CL_PrepareDownloadLuaFile(); -} - -/* -Ping Update except better: -We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out. -If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave. -*/ - -static inline void PingUpdate(void) -{ - boolean laggers[MAXPLAYERS]; - UINT8 numlaggers = 0; - memset(laggers, 0, sizeof(boolean) * MAXPLAYERS); - - netbuffer->packettype = PT_PING; - - //check for ping limit breakage. - if (cv_maxping.value) - { - for (INT32 i = 1; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].quittime - && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value)) - { - if (players[i].jointime > 30 * TICRATE) - laggers[i] = true; - numlaggers++; - } - else - pingtimeout[i] = 0; - } - - //kick lagging players... unless everyone but the server's ping sucks. - //in that case, it is probably the server's fault. - if (numlaggers < D_NumPlayers() - 1) - { - for (INT32 i = 1; i < MAXPLAYERS; i++) - { - if (playeringame[i] && laggers[i]) - { - pingtimeout[i]++; - // ok your net has been bad for too long, you deserve to die. - if (pingtimeout[i] > cv_pingtimeout.value) - { - pingtimeout[i] = 0; - SendKick(i, KICK_MSG_PING_HIGH | KICK_MSG_KEEP_BODY); - } - } - /* - you aren't lagging, - but you aren't free yet. - In case you'll keep spiking, - we just make the timer go back down. (Very unstable net must still get kicked). - */ - else - pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1); - } - } - } - - //make the ping packet and clear server data for next one - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount; - //server takes a snapshot of the real ping for display. - //otherwise, pings fluctuate a lot and would be odd to look at. - playerpingtable[i] = realpingtable[i] / pingmeasurecount; - realpingtable[i] = 0; //Reset each as we go. - } - - // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked. - netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value; - - //send out our ping packets - for (INT32 i = 0; i < MAXNETNODES; i++) - if (netnodes[i].ingame) - HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); - - pingmeasurecount = 1; //Reset count -} - -static void PT_Ping(SINT8 node, INT32 netconsole) -{ - // Only accept PT_PING from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - return; - } - - //Update client ping table from the server. - if (client) - { - for (INT32 i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; - - servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; - } -} - -/** Handles a packet received from a node that isn't in game - * - * \param node The packet sender - * \todo Choose a better name, as the packet can also come from the server apparently? - * \sa HandlePacketFromPlayer - * \sa GetPackets - * - */ -static void HandlePacketFromAwayNode(SINT8 node) -{ - if (node != servernode) - DEBFILE(va("Received packet from unknown host %d\n", node)); - - switch (netbuffer->packettype) - { - case PT_ASKINFOVIAMS : PT_AskInfoViaMS (node ); break; - case PT_SERVERINFO : PT_ServerInfo (node ); break; - case PT_TELLFILESNEEDED: PT_TellFilesNeeded(node ); break; - case PT_MOREFILESNEEDED: PT_MoreFilesNeeded(node ); break; - case PT_ASKINFO : PT_AskInfo (node ); break; - case PT_SERVERREFUSE : PT_ServerRefuse (node ); break; - case PT_SERVERCFG : PT_ServerCFG (node ); break; - case PT_FILEFRAGMENT : PT_FileFragment (node, -1); break; - case PT_FILEACK : PT_FileAck (node ); break; - case PT_FILERECEIVED : PT_FileReceived (node ); break; - case PT_REQUESTFILE : PT_RequestFile (node ); break; - case PT_CLIENTQUIT : PT_ClientQuit (node, -1); break; - case PT_SERVERTICS : PT_ServerTics (node, -1); break; - case PT_CLIENTJOIN : PT_ClientJoin (node ); break; - case PT_SERVERSHUTDOWN : PT_ServerShutdown (node ); break; - case PT_CLIENTCMD : break; // This is not an "unknown packet" - case PT_PLAYERINFO : break; // This is not an "unknown packet" - - default: - DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); - Net_CloseConnection(node); - } -} - -/** Handles a packet received from a node that is in game - * - * \param node The packet sender - * \todo Choose a better name - * \sa HandlePacketFromAwayNode - * \sa GetPackets - * - */ -static void HandlePacketFromPlayer(SINT8 node) -{ - INT32 netconsole; - - if (dedicated && node == 0) - netconsole = 0; - else - netconsole = netnodes[node].player; -#ifdef PARANOIA - if (netconsole >= MAXPLAYERS) - I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); -#endif - - switch (netbuffer->packettype) - { - // SERVER RECEIVE - case PT_CLIENTCMD: - case PT_CLIENT2CMD: - case PT_CLIENTMIS: - case PT_CLIENT2MIS: - case PT_NODEKEEPALIVE: - case PT_NODEKEEPALIVEMIS: - PT_ClientCmd(node, netconsole); - break; - case PT_TEXTCMD : PT_TextCmd (node, netconsole); break; - case PT_TEXTCMD2 : PT_TextCmd (node, netconsole); break; - case PT_LOGIN : PT_Login (node, netconsole); break; - case PT_CLIENTQUIT : PT_ClientQuit (node, netconsole); break; - case PT_CANRECEIVEGAMESTATE: PT_CanReceiveGamestate(node ); break; - case PT_ASKLUAFILE : PT_AskLuaFile (node ); break; - case PT_HASLUAFILE : PT_HasLuaFile (node ); break; - case PT_RECEIVEDGAMESTATE : PT_ReceivedGamestate (node ); break; - case PT_SERVERINFO : PT_ServerInfo (node ); break; - - // CLIENT RECEIVE - case PT_SERVERTICS : PT_ServerTics (node, netconsole); break; - case PT_PING : PT_Ping (node, netconsole); break; - case PT_FILEFRAGMENT : PT_FileFragment (node, netconsole); break; - case PT_FILEACK : PT_FileAck (node ); break; - case PT_FILERECEIVED : PT_FileReceived (node ); break; - case PT_WILLRESENDGAMESTATE: PT_WillResendGamestate(node ); break; - case PT_SENDINGLUAFILE : PT_SendingLuaFile (node ); break; - case PT_SERVERSHUTDOWN : PT_ServerShutdown (node ); break; - case PT_SERVERCFG : break; - case PT_CLIENTJOIN : break; - - default: - DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n", - netbuffer->packettype, node)); - } -} - -/** Handles all received packets, if any - * - * \todo Add details to this description (lol) - * - */ -void GetPackets(void) -{ - while (HGetPacket()) - { - SINT8 node = doomcom->remotenode; - - // Packet received from someone already playing - if (netnodes[node].ingame) - HandlePacketFromPlayer(node); - // Packet received from someone not playing - else - HandlePacketFromAwayNode(node); - } -} - -boolean TryRunTics(tic_t realtics) -{ - // the machine has lagged but it is not so bad - if (realtics > TICRATE/7) - { - if (server) - realtics = 1; - else - realtics = TICRATE/7; - } - - if (singletics) - realtics = 1; - - if (realtics >= 1) - { - COM_BufTicker(); - if (mapchangepending) - D_MapChange(-1, 0, ultimatemode, false, 2, false, fromlevelselect); // finish the map change - } - - NetUpdate(); - - if (demoplayback) - { - neededtic = gametic + realtics; - // start a game after a demo - maketic += realtics; - firstticstosend = maketic; - tictoclear = firstticstosend; - } - - GetPackets(); - -#ifdef DEBUGFILE - if (debugfile && (realtics || neededtic > gametic)) - { - fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n", - realtics, neededtic, gametic, debugload); - debugload = 100000; - } -#endif - - if (neededtic > gametic) - { - if (realtics) - hu_stopped = false; - - if (advancedemo) - { - if (timedemo_quit) - COM_ImmedExecute("quit"); - else - D_StartTitle(); - } - else - // run the count * tics - while (neededtic > gametic) - { - boolean update_stats = !(paused || P_AutoPause()); - - DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); - - if (update_stats) - PS_START_TIMING(ps_tictime); - - G_Ticker((gametic % NEWTICRATERATIO) == 0); - ExtraDataTicker(); - gametic++; - consistancy[gametic%BACKUPTICS] = Consistancy(); - - if (update_stats) - { - PS_STOP_TIMING(ps_tictime); - PS_UpdateTickStats(); - } - - // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. - if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) - break; - } - - return true; - } - else - { - if (realtics) - hu_stopped = true; - - return false; - } -} - -void NetUpdate(void) -{ - static tic_t gametime = 0; - static tic_t resptime = 0; - tic_t nowtime; - INT32 realtics; - - nowtime = I_GetTime(); - realtics = nowtime - gametime; - - if (realtics <= 0) // nothing new to update - return; - if (realtics > 5) - { - if (server) - realtics = 1; - else - realtics = 5; - } - - gametime = nowtime; - - if (server) - { - if (netgame && !(gametime % 35)) // update once per second. - PingUpdate(); - // update node latency values so we can take an average later. - for (INT32 i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && playernode[i] != UINT8_MAX) - realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i])); - pingmeasurecount++; - } - - if (client) - maketic = neededtic; - - Local_Maketic(realtics); - - if (server) - CL_SendClientCmd(); // send it - - GetPackets(); // get packet from client or from server - - // The client sends the command after receiving from the server - // The server sends it before because this is better in single player - -#ifdef MASTERSERVER - MasterClient_Ticker(); // Acking the Master Server -#endif - - if (client) - { - // If the client just finished redownloading the game state, load it - if (cl_redownloadinggamestate && fileneeded[0].status == FS_FOUND) - CL_ReloadReceivedSavegame(); - - CL_SendClientCmd(); // Send tic cmd - hu_redownloadinggamestate = cl_redownloadinggamestate; - } - else - { - if (!demoplayback) - { - hu_redownloadinggamestate = false; - - firstticstosend = gametic; - for (INT32 i = 0; i < MAXNETNODES; i++) - if (netnodes[i].ingame && netnodes[i].tic < firstticstosend) - { - firstticstosend = netnodes[i].tic; - - if (maketic + 1 >= netnodes[i].tic + BACKUPTICS) - Net_ConnectionTimeout(i); - } - - // Don't erase tics not acknowledged - INT32 counts = realtics; - if (maketic + counts >= firstticstosend + BACKUPTICS) - counts = firstticstosend+BACKUPTICS-maketic-1; - - for (INT32 i = 0; i < counts; i++) - SV_Maketic(); // Create missed tics and increment maketic - - for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged - D_Clearticcmd(tictoclear); // Clear the maketic the new tic - - SV_SendTics(); - - neededtic = maketic; // The server is a client too - } - } - - Net_AckTicker(); - - // Handle timeouts to prevent definitive freezes from happenning - if (server) - { - for (INT32 i = 1; i < MAXNETNODES; i++) - if (netnodes[i].ingame && netnodes[i].freezetimeout < I_GetTime()) - Net_ConnectionTimeout(i); - - // In case the cvar value was lowered - if (joindelay) - joindelay = min(joindelay - 1, 3 * (tic_t)cv_joindelay.value * TICRATE); - } - - nowtime /= NEWTICRATERATIO; - if (nowtime > resptime) - { - resptime = nowtime; -#ifdef HAVE_THREADS - I_lock_mutex(&m_menu_mutex); -#endif - M_Ticker(); -#ifdef HAVE_THREADS - I_unlock_mutex(m_menu_mutex); -#endif - CON_Ticker(); - } - - FileSendTicker(); -} - -// called one time at init -void D_ClientServerInit(void) -{ - DEBFILE(va("- - -== SRB2 v%d.%.2d.%d "VERSIONSTRING" debugfile ==- - -\n", - VERSION/100, VERSION%100, SUBVERSION)); - - COM_AddCommand("getplayernum", Command_GetPlayerNum); - COM_AddCommand("kick", Command_Kick); - COM_AddCommand("ban", Command_Ban); - COM_AddCommand("banip", Command_BanIP); - COM_AddCommand("clearbans", Command_ClearBans); - COM_AddCommand("showbanlist", Command_ShowBan); - COM_AddCommand("reloadbans", Command_ReloadBan); - COM_AddCommand("connect", Command_connect); - COM_AddCommand("nodes", Command_Nodes); - COM_AddCommand("resendgamestate", Command_ResendGamestate); -#ifdef PACKETDROP - COM_AddCommand("drop", Command_Drop); - COM_AddCommand("droprate", Command_Droprate); -#endif -#ifdef _DEBUG - COM_AddCommand("numnodes", Command_Numnodes); -#endif - - RegisterNetXCmd(XD_KICK, Got_KickCmd); - RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); -#ifdef DUMPCONSISTENCY - CV_RegisterVar(&cv_dumpconsistency); -#endif - Ban_Load_File(false); - - gametic = 0; - localgametic = 0; - - // do not send anything before the real begin - SV_StopServer(); - SV_ResetServer(); - if (dedicated) - SV_SpawnServer(); -} - -SINT8 nametonum(const char *name) -{ - INT32 playernum; - - if (!strcmp(name, "0")) - return 0; - - playernum = (SINT8)atoi(name); - - if (playernum < 0 || playernum >= MAXPLAYERS) - return -1; - - if (playernum) - { - if (playeringame[playernum]) - return (SINT8)playernum; - else - return -1; - } - - for (INT32 i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] && !stricmp(player_names[i], name)) - return (SINT8)i; - - CONS_Printf(M_GetText("There is no player named \"%s\"\n"), name); - - return -1; -} - -// Is there a game running? -boolean Playing(void) -{ - return (server && serverrunning) || (client && cl_mode == CL_CONNECTED); -} - -/** Returns the number of players playing. - * \return Number of players. Can be zero if we're running a ::dedicated - * server. - * \author Graue - */ -INT32 D_NumPlayers(void) -{ - INT32 num = 0; - for (INT32 ix = 0; ix < MAXPLAYERS; ix++) - if (playeringame[ix]) - num++; - return num; -} - -// -// Consistancy -// -// Note: It is called consistAncy on purpose. -// -INT16 Consistancy(void) -{ - UINT32 ret = 0; -#ifdef MOBJCONSISTANCY - thinker_t *th; - mobj_t *mo; -#endif - - DEBFILE(va("TIC %u ", gametic)); - - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - ret ^= 0xCCCC; - else if (!players[i].mo); - else - { - ret += players[i].mo->x; - ret -= players[i].mo->y; - ret += players[i].powers[pw_shield]; - ret *= i+1; - } - } - // I give up - // Coop desynching enemies is painful - if (!G_PlatformGametype()) - ret += P_GetRandSeed(); - -#ifdef MOBJCONSISTANCY - if (gamestate == GS_LEVEL) - { - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) - { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo = (mobj_t *)th; - - if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) - { - ret -= mo->type; - ret += mo->x; - ret -= mo->y; - ret += mo->z; - ret -= mo->momx; - ret += mo->momy; - ret -= mo->momz; - ret += mo->angle; - ret -= mo->flags; - ret += mo->flags2; - ret -= mo->eflags; - if (mo->target) - { - ret += mo->target->type; - ret -= mo->target->x; - ret += mo->target->y; - ret -= mo->target->z; - ret += mo->target->momx; - ret -= mo->target->momy; - ret += mo->target->momz; - ret -= mo->target->angle; - ret += mo->target->flags; - ret -= mo->target->flags2; - ret += mo->target->eflags; - ret -= mo->target->state - states; - ret += mo->target->tics; - ret -= mo->target->sprite; - ret += mo->target->frame; - } - else - ret ^= 0x3333; - if (mo->tracer && mo->tracer->type != MT_OVERLAY) - { - ret += mo->tracer->type; - ret -= mo->tracer->x; - ret += mo->tracer->y; - ret -= mo->tracer->z; - ret += mo->tracer->momx; - ret -= mo->tracer->momy; - ret += mo->tracer->momz; - ret -= mo->tracer->angle; - ret += mo->tracer->flags; - ret -= mo->tracer->flags2; - ret += mo->tracer->eflags; - ret -= mo->tracer->state - states; - ret += mo->tracer->tics; - ret -= mo->tracer->sprite; - ret += mo->tracer->frame; - } - else - ret ^= 0xAAAA; - ret -= mo->state - states; - ret += mo->tics; - ret -= mo->sprite; - ret += mo->frame; - } - } - } -#endif - - DEBFILE(va("Consistancy = %u\n", (ret & 0xFFFF))); - - return (INT16)(ret & 0xFFFF); -} - -tic_t GetLag(INT32 node) -{ - return gametic - netnodes[node].tic; -} - -void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest) -{ -#ifdef NOMD5 - (void)buffer; - (void)len; - (void)salt; - memset(dest, 0, 16); -#else - char tmpbuf[256]; - const size_t sl = strlen(salt); - - if (len > 256-sl) - len = 256-sl; - - memcpy(tmpbuf, buffer, len); - memmove(&tmpbuf[len], salt, sl); - //strcpy(&tmpbuf[len], salt); - len += strlen(salt); - if (len < 256) - memset(&tmpbuf[len],0,256-len); - - // Yes, we intentionally md5 the ENTIRE buffer regardless of size... - md5_buffer(tmpbuf, 256, dest); -#endif -} diff --git a/src/netcode/d_clisrv.h b/src/netcode/d_clisrv.h deleted file mode 100644 index 0abd638ce..000000000 --- a/src/netcode/d_clisrv.h +++ /dev/null @@ -1,131 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 d_clisrv.h -/// \brief high level networking stuff - -#ifndef __D_CLISRV__ -#define __D_CLISRV__ - -#include "protocol.h" -#include "../d_ticcmd.h" -#include "d_net.h" -#include "d_netcmd.h" -#include "d_net.h" -#include "../tables.h" -#include "../d_player.h" -#include "mserv.h" - -#define CLIENTBACKUPTICS 32 - -#ifdef PACKETDROP -void Command_Drop(void); -void Command_Droprate(void); -#endif -#ifdef _DEBUG -void Command_Numnodes(void); -#endif - -extern INT32 mapchangepending; - -// Points inside doomcom -extern doomdata_t *netbuffer; - -#define BASEPACKETSIZE offsetof(doomdata_t, u) -#define BASESERVERTICSSIZE offsetof(doomdata_t, u.serverpak.cmds[0]) - -typedef enum -{ - KR_KICK = 1, //Kicked by server - KR_PINGLIMIT = 2, //Broke Ping Limit - KR_SYNCH = 3, //Synch Failure - KR_TIMEOUT = 4, //Connection Timeout - KR_BAN = 5, //Banned by server - KR_LEAVE = 6, //Quit the game - -} 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) -extern boolean dedicated; // For dedicated server -extern UINT16 software_MAXPACKETLENGTH; -extern boolean acceptnewnode; -extern SINT8 servernode; -extern tic_t maketic; -extern tic_t neededtic; -extern INT16 consistancy[BACKUPTICS]; - -void Command_Ping_f(void); -extern tic_t connectiontimeout; -extern UINT16 pingmeasurecount; -extern UINT32 realpingtable[MAXPLAYERS]; -extern UINT32 playerpingtable[MAXPLAYERS]; -extern tic_t servermaxping; - -extern consvar_t cv_netticbuffer, cv_resynchattempts, cv_blamecfail, cv_playbackspeed; - -// Used in d_net, the only dependence -void D_ClientServerInit(void); - -// Create any new ticcmds and broadcast to other players. -void NetUpdate(void); - -void GetPackets(void); -void ResetNode(INT32 node); -INT16 Consistancy(void); - -void SV_StartSinglePlayerServer(void); -void SV_SpawnServer(void); -void SV_StopServer(void); -void SV_ResetServer(void); -void CL_AddSplitscreenPlayer(void); -void CL_RemoveSplitscreenPlayer(void); -void CL_Reset(void); -void CL_ClearPlayer(INT32 playernum); -void CL_RemovePlayer(INT32 playernum, kickreason_t reason); -void CL_HandleTimeout(void); -// Is there a game running -boolean Playing(void); - -// Broadcasts special packets to other players -// to notify of game exit -void D_QuitNetGame(void); - -//? How many ticks to run? -boolean TryRunTics(tic_t realtic); - -// extra data for lmps -// these functions scare me. they contain magic. -/*boolean AddLmpExtradata(UINT8 **demo_p, INT32 playernum); -void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum);*/ - -// translate a playername in a player number return -1 if not found and -// print a error message in the console -SINT8 nametonum(const char *name); - -extern char motd[254], server_context[8]; -extern UINT8 playernode[MAXPLAYERS]; - -INT32 D_NumPlayers(void); - -tic_t GetLag(INT32 node); - -void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt, void *dest); - -extern UINT8 adminpassmd5[16]; -extern boolean adminpasswordset; - -extern boolean hu_stopped; - -#endif diff --git a/src/netcode/gamestate.c b/src/netcode/gamestate.c deleted file mode 100644 index c1ceb95b5..000000000 --- a/src/netcode/gamestate.c +++ /dev/null @@ -1,336 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 gamestate.c -/// \brief Gamestate (re)sending - -#include "d_clisrv.h" -#include "d_netfil.h" -#include "gamestate.h" -#include "i_net.h" -#include "protocol.h" -#include "server_connection.h" -#include "../am_map.h" -#include "../byteptr.h" -#include "../console.h" -#include "../d_main.h" -#include "../doomstat.h" -#include "../doomtype.h" -#include "../f_finale.h" -#include "../g_demo.h" -#include "../g_game.h" -#include "../i_time.h" -#include "../lua_script.h" -#include "../lzf.h" -#include "../m_misc.h" -#include "../p_haptic.h" -#include "../p_local.h" -#include "../p_saveg.h" -#include "../r_main.h" -#include "../tables.h" -#include "../z_zone.h" - -#define SAVEGAMESIZE (768*1024) - -UINT8 hu_redownloadinggamestate = 0; -boolean cl_redownloadinggamestate = false; - -boolean SV_ResendingSavegameToAnyone(void) -{ - for (INT32 i = 0; i < MAXNETNODES; i++) - if (netnodes[i].resendingsavegame) - return true; - return false; -} - -void SV_SendSaveGame(INT32 node, boolean resending) -{ - size_t length, compressedlen; - UINT8 *savebuffer; - UINT8 *compressedsave; - UINT8 *buffertosend; - - // first save it in a malloced buffer - savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); - if (!savebuffer) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - // Leave room for the uncompressed length. - save_p = savebuffer + sizeof(UINT32); - - P_SaveNetGame(resending); - - length = save_p - savebuffer; - if (length > SAVEGAMESIZE) - { - free(savebuffer); - save_p = NULL; - I_Error("Savegame buffer overrun"); - } - - // Allocate space for compressed save: one byte fewer than for the - // uncompressed data to ensure that the compression is worthwhile. - compressedsave = malloc(length - 1); - if (!compressedsave) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - // Attempt to compress it. - if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) - { - // Compressing succeeded; send compressed data - - free(savebuffer); - - // State that we're compressed. - buffertosend = compressedsave; - WRITEUINT32(compressedsave, length - sizeof(UINT32)); - length = compressedlen + sizeof(UINT32); - } - else - { - // Compression failed to make it smaller; send original - - free(compressedsave); - - // State that we're not compressed - buffertosend = savebuffer; - WRITEUINT32(savebuffer, 0); - } - - AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); - save_p = NULL; - - // Remember when we started sending the savegame so we can handle timeouts - netnodes[node].sendingsavegame = true; - netnodes[node].freezetimeout = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte -} - -#ifdef DUMPCONSISTENCY -#define TMPSAVENAME "badmath.sav" -static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -void SV_SavedGame(void) -{ - size_t length; - UINT8 *savebuffer; - char tmpsave[256]; - - if (!cv_dumpconsistency.value) - return; - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - // first save it in a malloced buffer - save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); - if (!save_p) - { - CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); - return; - } - - P_SaveNetGame(false); - - length = save_p - savebuffer; - if (length > SAVEGAMESIZE) - { - free(savebuffer); - save_p = NULL; - I_Error("Savegame buffer overrun"); - } - - // then save it! - if (!FIL_WriteFile(tmpsave, savebuffer, length)) - CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); - - free(savebuffer); - save_p = NULL; -} - -#undef TMPSAVENAME -#endif -#define TMPSAVENAME "$$$.sav" - - -void CL_LoadReceivedSavegame(boolean reloading) -{ - UINT8 *savebuffer = NULL; - size_t length, decompressedlen; - char tmpsave[256]; - - FreeFileNeeded(); - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - length = FIL_ReadFile(tmpsave, &savebuffer); - - CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); - if (!length) - { - I_Error("Can't read savegame sent"); - return; - } - - save_p = savebuffer; - - // Decompress saved game if necessary. - decompressedlen = READUINT32(save_p); - if(decompressedlen > 0) - { - UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); - lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); - Z_Free(savebuffer); - save_p = savebuffer = decompressedbuffer; - } - - paused = false; - demoplayback = false; - titlemapinaction = TITLEMAP_OFF; - titledemo = false; - automapactive = false; - - P_StopRumble(NULL); - - // load a base level - if (P_LoadNetGame(reloading)) - { - const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; - CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); - if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) - { - CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); - if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - CONS_Printf(M_GetText(" Zone")); - if (actnum > 0) - CONS_Printf(" %2d", actnum); - } - CONS_Printf("\"\n"); - } - - // done - Z_Free(savebuffer); - save_p = NULL; - if (unlink(tmpsave) == -1) - CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); - consistancy[gametic%BACKUPTICS] = Consistancy(); - CON_ToggleOff(); - - // Tell the server we have received and reloaded the gamestate - // so they know they can resume the game - netbuffer->packettype = PT_RECEIVEDGAMESTATE; - HSendPacket(servernode, true, 0, 0); -} - -void CL_ReloadReceivedSavegame(void) -{ - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - LUA_InvalidatePlayer(&players[i]); - sprintf(player_names[i], "Player %d", i + 1); - } - - CL_LoadReceivedSavegame(true); - - neededtic = max(neededtic, gametic); - maketic = neededtic; - - ticcmd_oldangleturn[0] = players[consoleplayer].oldrelangleturn; - P_ForceLocalAngle(&players[consoleplayer], (angle_t)(players[consoleplayer].angleturn << 16)); - if (splitscreen) - { - ticcmd_oldangleturn[1] = players[secondarydisplayplayer].oldrelangleturn; - P_ForceLocalAngle(&players[secondarydisplayplayer], (angle_t)(players[secondarydisplayplayer].angleturn << 16)); - } - - camera.subsector = R_PointInSubsector(camera.x, camera.y); - camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); - - cl_redownloadinggamestate = false; - - CONS_Printf(M_GetText("Game state reloaded\n")); -} - -void Command_ResendGamestate(void) -{ - SINT8 playernum; - - if (COM_Argc() == 1) - { - CONS_Printf(M_GetText("resendgamestate : resend the game state to a player\n")); - return; - } - else if (client) - { - CONS_Printf(M_GetText("Only the server can use this.\n")); - return; - } - - playernum = nametonum(COM_Argv(1)); - if (playernum == -1 || playernum == 0) - return; - - // Send a PT_WILLRESENDGAMESTATE packet to the client so they know what's going on - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - if (!HSendPacket(playernode[playernum], true, 0, 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("A problem occured, please try again.\n")); - return; - } -} - -void PT_CanReceiveGamestate(SINT8 node) -{ - if (client || netnodes[node].sendingsavegame) - return; - - CONS_Printf(M_GetText("Resending game state to %s...\n"), player_names[netnodes[node].player]); - - SV_SendSaveGame(node, true); // Resend a complete game state - netnodes[node].resendingsavegame = true; -} - -void PT_ReceivedGamestate(SINT8 node) -{ - netnodes[node].sendingsavegame = false; - netnodes[node].resendingsavegame = false; - netnodes[node].savegameresendcooldown = I_GetTime() + 5 * TICRATE; -} - -void PT_WillResendGamestate(SINT8 node) -{ - (void)node; - - char tmpsave[256]; - - if (server || cl_redownloadinggamestate) - return; - - // Send back a PT_CANRECEIVEGAMESTATE packet to the server - // so they know they can start sending the game state - netbuffer->packettype = PT_CANRECEIVEGAMESTATE; - if (!HSendPacket(servernode, true, 0, 0)) - return; - - CONS_Printf(M_GetText("Reloading game state...\n")); - - sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); - - // Don't get a corrupt savegame error because tmpsave already exists - if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1) - I_Error("Can't delete %s\n", tmpsave); - - CL_PrepareDownloadSaveGame(tmpsave); - - cl_redownloadinggamestate = true; -} diff --git a/src/netcode/gamestate.h b/src/netcode/gamestate.h deleted file mode 100644 index 9d2779772..000000000 --- a/src/netcode/gamestate.h +++ /dev/null @@ -1,31 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 gamestate.h -/// \brief Gamestate (re)sending - -#ifndef __GAMESTATE__ -#define __GAMESTATE__ - -#include "../doomtype.h" - -extern UINT8 hu_redownloadinggamestate; -extern boolean cl_redownloadinggamestate; - -boolean SV_ResendingSavegameToAnyone(void); -void SV_SendSaveGame(INT32 node, boolean resending); -void SV_SavedGame(void); -void CL_LoadReceivedSavegame(boolean reloading); -void CL_ReloadReceivedSavegame(void); -void Command_ResendGamestate(void); -void PT_CanReceiveGamestate(SINT8 node); -void PT_ReceivedGamestate(SINT8 node); -void PT_WillResendGamestate(SINT8 node); - -#endif diff --git a/src/netcode/net_command.c b/src/netcode/net_command.c deleted file mode 100644 index efc8bd0ef..000000000 --- a/src/netcode/net_command.c +++ /dev/null @@ -1,386 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 net_command.c -/// \brief Net command handling - -#include "net_command.h" -#include "tic_command.h" -#include "gamestate.h" -#include "server_connection.h" -#include "d_clisrv.h" -#include "i_net.h" -#include "../byteptr.h" -#include "../g_game.h" -#include "../z_zone.h" -#include "../doomtype.h" - -textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; -UINT8 localtextcmd[MAXTEXTCMD]; -UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen -static void (*listnetxcmd[MAXNETXCMD])(UINT8 **p, INT32 playernum); - -void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) -{ -#ifdef PARANOIA - if (id >= MAXNETXCMD) - I_Error("Command id %d too big", id); - if (listnetxcmd[id] != 0) - I_Error("Command id %d already used", id); -#endif - listnetxcmd[id] = cmd_f; -} - -void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam) -{ - if (localtextcmd[0]+2+nparam > MAXTEXTCMD) - { - // for future reference: if (cv_debug) != debug disabled. - CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam)); - return; - } - localtextcmd[0]++; - localtextcmd[localtextcmd[0]] = (UINT8)id; - if (param && nparam) - { - M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam); - localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam); - } -} - -// splitscreen player -void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam) -{ - if (localtextcmd2[0]+2+nparam > MAXTEXTCMD) - { - I_Error("No more place in the buffer for netcmd %d\n",id); - return; - } - localtextcmd2[0]++; - localtextcmd2[localtextcmd2[0]] = (UINT8)id; - if (param && nparam) - { - M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam); - localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam); - } -} - -UINT8 GetFreeXCmdSize(void) -{ - // -1 for the size and another -1 for the ID. - return (UINT8)(localtextcmd[0] - 2); -} - -// Frees all textcmd memory for the specified tic -void D_FreeTextcmd(tic_t tic) -{ - textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdtic_t *textcmdtic = *tctprev; - - while (textcmdtic && textcmdtic->tic != tic) - { - tctprev = &textcmdtic->next; - textcmdtic = textcmdtic->next; - } - - if (textcmdtic) - { - // Remove this tic from the list. - *tctprev = textcmdtic->next; - - // Free all players. - for (INT32 i = 0; i < TEXTCMD_HASH_SIZE; i++) - { - textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i]; - - while (textcmdplayer) - { - textcmdplayer_t *tcpnext = textcmdplayer->next; - Z_Free(textcmdplayer); - textcmdplayer = tcpnext; - } - } - - // Free this tic's own memory. - Z_Free(textcmdtic); - } -} - -// Gets the buffer for the specified ticcmd, or NULL if there isn't one -UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum) -{ - textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next; - - // Do we have an entry for the tic? If so, look for player. - if (textcmdtic) - { - textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; - while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next; - - if (textcmdplayer) return textcmdplayer->cmd; - } - - return NULL; -} - -// Gets the buffer for the specified ticcmd, creating one if necessary -UINT8* D_GetTextcmd(tic_t tic, INT32 playernum) -{ - textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)]; - textcmdplayer_t *textcmdplayer, **tcpprev; - - // Look for the tic. - while (textcmdtic && textcmdtic->tic != tic) - { - tctprev = &textcmdtic->next; - textcmdtic = textcmdtic->next; - } - - // If we don't have an entry for the tic, make it. - if (!textcmdtic) - { - textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL); - textcmdtic->tic = tic; - } - - tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)]; - textcmdplayer = *tcpprev; - - // Look for the player. - while (textcmdplayer && textcmdplayer->playernum != playernum) - { - tcpprev = &textcmdplayer->next; - textcmdplayer = textcmdplayer->next; - } - - // If we don't have an entry for the player, make it. - if (!textcmdplayer) - { - textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL); - textcmdplayer->playernum = playernum; - } - - return textcmdplayer->cmd; -} - -void ExtraDataTicker(void) -{ - for (INT32 i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] || i == 0) - { - UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i); - - if (bufferstart) - { - UINT8 *curpos = bufferstart; - UINT8 *bufferend = &curpos[curpos[0]+1]; - - curpos++; - while (curpos < bufferend) - { - if (*curpos < MAXNETXCMD && listnetxcmd[*curpos]) - { - const UINT8 id = *curpos; - curpos++; - DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); - (listnetxcmd[id])(&curpos, i); - DEBFILE("done\n"); - } - else - { - if (server) - { - SendKick(i, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic)); - } - CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]); - break; - } - } - } - } - - // If you are a client, you can safely forget the net commands for this tic - // If you are the server, you need to remember them until every client has been acknowledged, - // because if you need to resend a PT_SERVERTICS packet, you will need to put the commands in it - if (client) - D_FreeTextcmd(gametic); -} - -// used at txtcmds received to check packetsize bound -size_t TotalTextCmdPerTic(tic_t tic) -{ - size_t total = 1; // num of textcmds in the tic (ntextcmd byte) - - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - UINT8 *textcmd = D_GetExistingTextcmd(tic, i); - if ((!i || playeringame[i]) && textcmd) - total += 2 + textcmd[0]; // "+2" for size and playernum - } - - return total; -} - -void PT_TextCmd(SINT8 node, INT32 netconsole) -{ - if (client) - return; - - // splitscreen special - if (netbuffer->packettype == PT_TEXTCMD2) - netconsole = netnodes[node].player2; - - if (netconsole < 0 || netconsole >= MAXPLAYERS) - Net_UnAcknowledgePacket(node); - else - { - size_t j; - tic_t tic = maketic; - UINT8 *textcmd; - - // ignore if the textcmd has a reported size of zero - // this shouldn't be sent at all - if (!netbuffer->u.textcmd[0]) - { - DEBFILE(va("GetPacket: Textcmd with size 0 detected! (node %u, player %d)\n", - node, netconsole)); - Net_UnAcknowledgePacket(node); - return; - } - - // ignore if the textcmd size var is actually larger than it should be - // BASEPACKETSIZE + 1 (for size) + textcmd[0] should == datalength - if (netbuffer->u.textcmd[0] > (size_t)doomcom->datalength-BASEPACKETSIZE-1) - { - DEBFILE(va("GetPacket: Bad Textcmd packet size! (expected %d, actual %s, node %u, player %d)\n", - netbuffer->u.textcmd[0], sizeu1((size_t)doomcom->datalength-BASEPACKETSIZE-1), - node, netconsole)); - Net_UnAcknowledgePacket(node); - return; - } - - // check if tic that we are making isn't too large else we cannot send it :( - // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time - j = software_MAXPACKETLENGTH - - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE - + (doomcom->numslots+1)*sizeof(ticcmd_t)); - - // search a tic that have enougth space in the ticcmd - while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), - (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD) - && tic < firstticstosend + BACKUPTICS) - tic++; - - if (tic >= firstticstosend + BACKUPTICS) - { - DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, " - "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)), - maketic, firstticstosend, node, netconsole)); - Net_UnAcknowledgePacket(node); - return; - } - - // Make sure we have a buffer - if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole); - - DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n", - tic, textcmd[0]+1, netconsole, firstticstosend, maketic)); - - M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]); - textcmd[0] += (UINT8)netbuffer->u.textcmd[0]; - } -} - -void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf) -{ - UINT8 *numcmds; - - numcmds = (*buf)++; - *numcmds = 0; - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - UINT8 *cmd = D_GetExistingTextcmd(tic, i); - INT32 size = cmd ? cmd[0] : 0; - - if ((!i || playeringame[i]) && size) - { - (*numcmds)++; - WRITEUINT8(*buf, i); - M_Memcpy(*buf, cmd, size + 1); - *buf += size + 1; - } - } -} - -void CL_CopyNetCommandsFromServerPacket(tic_t tic) -{ - servertics_pak *packet = &netbuffer->u.serverpak; - UINT8 *cmds = (UINT8*)&packet->cmds[packet->numslots * packet->numtics]; - UINT8 numcmds; - - numcmds = *cmds++; - - for (UINT32 i = 0; i < numcmds; i++) - { - INT32 playernum = *cmds++; // playernum - size_t size = cmds[0]+1; - - if (tic >= gametic) // Don't copy old net commands - M_Memcpy(D_GetTextcmd(tic, playernum), cmds, size); - cmds += size; - } -} - -void CL_SendNetCommands(void) -{ - // Send extra data if needed - if (localtextcmd[0]) - { - netbuffer->packettype = PT_TEXTCMD; - M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail... - localtextcmd[0] = 0; - } - - // Send extra data if needed for player 2 (splitscreen) - if (localtextcmd2[0]) - { - netbuffer->packettype = PT_TEXTCMD2; - M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1); - // All extra data have been sent - if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail... - localtextcmd2[0] = 0; - } -} - -void SendKick(UINT8 playernum, UINT8 msg) -{ - UINT8 buf[2]; - - if (!(server && cv_rejointimeout.value)) - msg &= ~KICK_MSG_KEEP_BODY; - - buf[0] = playernum; - buf[1] = msg; - SendNetXCmd(XD_KICK, &buf, 2); -} - -void SendKicksForNode(SINT8 node, UINT8 msg) -{ - if (!netnodes[node].ingame) - return; - - for (INT32 playernum = netnodes[node].player; playernum != -1; playernum = netnodes[node].player2) - if (playernum != -1 && playeringame[playernum]) - SendKick(playernum, msg); -} diff --git a/src/netcode/net_command.h b/src/netcode/net_command.h deleted file mode 100644 index cc26aeb0e..000000000 --- a/src/netcode/net_command.h +++ /dev/null @@ -1,66 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 net_command.h -/// \brief Net command handling - -#ifndef __D_NET_COMMAND__ -#define __D_NET_COMMAND__ - -#include "d_clisrv.h" -#include "../doomtype.h" - -// Must be a power of two -#define TEXTCMD_HASH_SIZE 4 - -typedef struct textcmdplayer_s -{ - INT32 playernum; - UINT8 cmd[MAXTEXTCMD]; - struct textcmdplayer_s *next; -} textcmdplayer_t; - -typedef struct textcmdtic_s -{ - tic_t tic; - textcmdplayer_t *playercmds[TEXTCMD_HASH_SIZE]; - struct textcmdtic_s *next; -} textcmdtic_t; - -extern textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE]; - -extern UINT8 localtextcmd[MAXTEXTCMD]; -extern UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen - -void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); -void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); -void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player - -UINT8 GetFreeXCmdSize(void); -void D_FreeTextcmd(tic_t tic); - -// Gets the buffer for the specified ticcmd, or NULL if there isn't one -UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum); - -// Gets the buffer for the specified ticcmd, creating one if necessary -UINT8* D_GetTextcmd(tic_t tic, INT32 playernum); - -void ExtraDataTicker(void); - -// used at txtcmds received to check packetsize bound -size_t TotalTextCmdPerTic(tic_t tic); - -void PT_TextCmd(SINT8 node, INT32 netconsole); -void SV_WriteNetCommandsForTic(tic_t tic, UINT8 **buf); -void CL_CopyNetCommandsFromServerPacket(tic_t tic); -void CL_SendNetCommands(void); -void SendKick(UINT8 playernum, UINT8 msg); -void SendKicksForNode(SINT8 node, UINT8 msg); - -#endif diff --git a/src/netcode/server_connection.c b/src/netcode/server_connection.c deleted file mode 100644 index f8ec3c7bd..000000000 --- a/src/netcode/server_connection.c +++ /dev/null @@ -1,507 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 server_connection.c -/// \brief Server-side part of connection handling - -#include "server_connection.h" -#include "i_net.h" -#include "d_clisrv.h" -#include "d_netfil.h" -#include "mserv.h" -#include "net_command.h" -#include "gamestate.h" -#include "../byteptr.h" -#include "../g_game.h" -#include "../g_state.h" -#include "../p_setup.h" -#include "../p_tick.h" -#include "../command.h" -#include "../doomstat.h" - -// Minimum timeout for sending the savegame -// The actual timeout will be longer depending on the savegame length -tic_t jointimeout = (10*TICRATE); - -// Incremented by cv_joindelay when a client joins, decremented each tic. -// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled. -tic_t joindelay = 0; - -// Minimum timeout for sending the savegame -// The actual timeout will be longer depending on the savegame length -char playeraddress[MAXPLAYERS][64]; - -consvar_t cv_showjoinaddress = CVAR_INIT ("showjoinaddress", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -consvar_t cv_allownewplayer = CVAR_INIT ("allowjoin", "On", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); - -static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}}; -consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxplayers_cons_t, NULL); - -static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); - -static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); - -static INT32 FindRejoinerNum(SINT8 node) -{ - char strippednodeaddress[64]; - const char *nodeaddress; - char *port; - - // Make sure there is no dead dress before proceeding to the stripping - if (!I_GetNodeAddress) - return -1; - nodeaddress = I_GetNodeAddress(node); - if (!nodeaddress) - return -1; - - // Strip the address of its port - strcpy(strippednodeaddress, nodeaddress); - port = strchr(strippednodeaddress, ':'); - if (port) - *port = '\0'; - - // Check if any player matches the stripped address - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && playeraddress[i][0] && playernode[i] == UINT8_MAX - && !strcmp(playeraddress[i], strippednodeaddress)) - return i; - } - - return -1; -} - -static UINT8 -GetRefuseReason (INT32 node) -{ - if (!node || FindRejoinerNum(node) != -1) - return 0; - else if (bannednode && bannednode[node]) - return REFUSE_BANNED; - else if (!cv_allownewplayer.value) - return REFUSE_JOINS_DISABLED; - else if (D_NumPlayers() >= cv_maxplayers.value) - return REFUSE_SLOTS_FULL; - else - return 0; -} - -static void SV_SendServerInfo(INT32 node, tic_t servertime) -{ - UINT8 *p; - - netbuffer->packettype = PT_SERVERINFO; - netbuffer->u.serverinfo._255 = 255; - netbuffer->u.serverinfo.packetversion = PACKETVERSION; - netbuffer->u.serverinfo.version = VERSION; - netbuffer->u.serverinfo.subversion = SUBVERSION; - strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION, - sizeof netbuffer->u.serverinfo.application); - // return back the time value so client can compute their ping - netbuffer->u.serverinfo.time = (tic_t)LONG(servertime); - netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime); - - netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); - netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; - - netbuffer->u.serverinfo.refusereason = GetRefuseReason(node); - - strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype], - sizeof netbuffer->u.serverinfo.gametypename); - netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; - netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); - netbuffer->u.serverinfo.flags = (dedicated ? SV_DEDICATED : 0); - strncpy(netbuffer->u.serverinfo.servername, cv_servername.string, - MAXSERVERNAME); - strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7); - - M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); - - memset(netbuffer->u.serverinfo.maptitle, 0, sizeof netbuffer->u.serverinfo.maptitle); - - if (mapheaderinfo[gamemap-1] && *mapheaderinfo[gamemap-1]->lvlttl) - { - char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle; - while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0') - { - if (!(*read & 0x80)) - { - *writ = toupper(*read); - writ++; - } - read++; - } - *writ = '\0'; - //strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); - } - else - strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32); - - if (mapheaderinfo[gamemap-1] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - netbuffer->u.serverinfo.iszone = 1; - else - netbuffer->u.serverinfo.iszone = 0; - - if (mapheaderinfo[gamemap-1]) - netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum; - - p = PutFileNeeded(0); - - HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); -} - -static void SV_SendPlayerInfo(INT32 node) -{ - netbuffer->packettype = PT_PLAYERINFO; - - for (UINT8 i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - { - netbuffer->u.playerinfo[i].num = 255; // This slot is empty. - continue; - } - - netbuffer->u.playerinfo[i].num = i; - strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); - netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; - - //fetch IP address - //No, don't do that, you fuckface. - memset(netbuffer->u.playerinfo[i].address, 0, 4); - - if (G_GametypeHasTeams()) - { - if (!players[i].ctfteam) - netbuffer->u.playerinfo[i].team = 255; - else - netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam; - } - else - { - if (players[i].spectator) - netbuffer->u.playerinfo[i].team = 255; - else - netbuffer->u.playerinfo[i].team = 0; - } - - netbuffer->u.playerinfo[i].score = LONG(players[i].score); - netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); - netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin -#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself - % 3 -#endif - ); - - // Extra data - netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; - - if (players[i].pflags & PF_TAGIT) - netbuffer->u.playerinfo[i].data |= 0x20; - - if (players[i].gotflag) - netbuffer->u.playerinfo[i].data |= 0x40; - - if (players[i].powers[pw_super]) - netbuffer->u.playerinfo[i].data |= 0x80; - } - - HSendPacket(node, false, 0, sizeof(plrinfo_pak) * MAXPLAYERS); -} - -/** Sends a PT_SERVERCFG packet - * - * \param node The destination - * \return True if the packet was successfully sent - * - */ -static boolean SV_SendServerConfig(INT32 node) -{ - boolean waspacketsent; - - netbuffer->packettype = PT_SERVERCFG; - - netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; - netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); - netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); - netbuffer->u.servercfg.clientnode = (UINT8)node; - netbuffer->u.servercfg.gamestate = (UINT8)gamestate; - netbuffer->u.servercfg.gametype = (UINT8)gametype; - netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame; - - memcpy(netbuffer->u.servercfg.server_context, server_context, 8); - - { - const size_t len = sizeof (serverconfig_pak); - -#ifdef DEBUGFILE - if (debugfile) - { - fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n", - sizeu1(len), node); - } -#endif - - waspacketsent = HSendPacket(node, true, 0, len); - } - -#ifdef DEBUGFILE - if (debugfile) - { - if (waspacketsent) - { - fprintf(debugfile, "ServerConfig Packet was sent\n"); - } - else - { - fprintf(debugfile, "ServerConfig Packet could not be sent right now\n"); - } - } -#endif - - return waspacketsent; -} - -// Adds a node to the game (player will follow at map change or at savegame....) -static inline void SV_AddNode(INT32 node) -{ - netnodes[node].tic = gametic; - netnodes[node].supposedtic = gametic; - // little hack because the server connects to itself and puts - // nodeingame when connected not here - if (node) - netnodes[node].ingame = true; -} - -static void SV_AddPlayer(SINT8 node, const char *name) -{ - INT32 n; - UINT8 buf[2 + MAXPLAYERNAME]; - UINT8 *p; - INT32 newplayernum; - - newplayernum = FindRejoinerNum(node); - if (newplayernum == -1) - { - // search for a free playernum - // we can't use playeringame since it is not updated here - for (newplayernum = dedicated ? 1 : 0; newplayernum < MAXPLAYERS; newplayernum++) - { - if (playeringame[newplayernum]) - continue; - for (n = 0; n < MAXNETNODES; n++) - if (netnodes[n].player == newplayernum || netnodes[n].player2 == newplayernum) - break; - if (n == MAXNETNODES) - break; - } - } - - // should never happen since we check the playernum - // before accepting the join - I_Assert(newplayernum < MAXPLAYERS); - - playernode[newplayernum] = (UINT8)node; - - p = buf + 2; - buf[0] = (UINT8)node; - buf[1] = newplayernum; - if (netnodes[node].numplayers < 1) - { - netnodes[node].player = newplayernum; - } - else - { - netnodes[node].player2 = newplayernum; - buf[1] |= 0x80; - } - WRITESTRINGN(p, name, MAXPLAYERNAME); - netnodes[node].numplayers++; - - SendNetXCmd(XD_ADDPLAYER, &buf, p - buf); - - DEBFILE(va("Server added player %d node %d\n", newplayernum, node)); -} - -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); - Net_CloseConnection(node); -} - -static const char * -GetRefuseMessage (SINT8 node, INT32 rejoinernum) -{ - clientconfig_pak *cc = &netbuffer->u.clientcfg; - - boolean rejoining = (rejoinernum != -1); - - if (!node)/* server connecting to itself */ - return NULL; - - if ( - cc->modversion != MODVERSION || - strncmp(cc->application, SRB2APPLICATION, - sizeof cc->application) - ){ - return/* this is probably client's fault */ - "Incompatible."; - } - else if (bannednode && bannednode[node]) - { - return - "You have been banned\n" - "from the server."; - } - else if (cc->localplayers != 1) - { - return - "Wrong player count."; - } - - if (!rejoining) - { - if (!cv_allownewplayer.value) - { - return - "The server is not accepting\n" - "joins for the moment."; - } - else if (D_NumPlayers() >= cv_maxplayers.value) - { - return va( - "Maximum players reached: %d", - cv_maxplayers.value); - } - } - - if (luafiletransfers) - { - return - "The serveris broadcasting a file\n" - "requested by a Lua script.\n" - "Please wait a bit and then\n" - "try rejoining."; - } - - if (netgame) - { - const tic_t th = 2 * cv_joindelay.value * TICRATE; - - if (joindelay > th) - { - return va( - "Too many people are connecting.\n" - "Please wait %d seconds and then\n" - "try rejoining.", - (joindelay - th) / TICRATE); - } - } - - return NULL; -} - -/** Called when a PT_CLIENTJOIN packet is received - * - * \param node The packet sender - * - */ -void PT_ClientJoin(SINT8 node) -{ - char names[MAXSPLITSCREENPLAYERS][MAXPLAYERNAME + 1]; - INT32 numplayers = netbuffer->u.clientcfg.localplayers; - INT32 rejoinernum; - - // Ignore duplicate packets - if (client || netnodes[node].ingame) - return; - - rejoinernum = FindRejoinerNum(node); - - const char *refuse = GetRefuseMessage(node, rejoinernum); - if (refuse) - { - SV_SendRefuse(node, refuse); - return; - } - - for (INT32 i = 0; i < numplayers; i++) - { - strlcpy(names[i], netbuffer->u.clientcfg.names[i], MAXPLAYERNAME + 1); - if (!EnsurePlayerNameIsGood(names[i], rejoinernum)) - { - SV_SendRefuse(node, "Bad player name"); - return; - } - } - - SV_AddNode(node); - - if (!SV_SendServerConfig(node)) - { - /// \note Shouldn't SV_SendRefuse be called before ResetNode? - ResetNode(node); - SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); - /// \todo fix this !!! - return; - } - DEBFILE("new node joined\n"); - - if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) - { - SV_SendSaveGame(node, false); // send a complete game state - DEBFILE("send savegame\n"); - } - - // Splitscreen can allow 2 players in one node - for (INT32 i = 0; i < numplayers; i++) - SV_AddPlayer(node, names[i]); - - joindelay += cv_joindelay.value * TICRATE; -} - -void PT_AskInfoViaMS(SINT8 node) -{ - Net_CloseConnection(node); -} - -void PT_TellFilesNeeded(SINT8 node) -{ - if (server && serverrunning) - { - UINT8 *p; - INT32 firstfile = netbuffer->u.filesneedednum; - - netbuffer->packettype = PT_MOREFILESNEEDED; - netbuffer->u.filesneededcfg.first = firstfile; - netbuffer->u.filesneededcfg.more = 0; - - p = PutFileNeeded(firstfile); - - HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); - } - else // Shouldn't get this if you aren't the server...? - Net_CloseConnection(node); -} - -void PT_AskInfo(SINT8 node) -{ - if (server && serverrunning) - { - SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time)); - SV_SendPlayerInfo(node); // Send extra info - } - Net_CloseConnection(node); -} diff --git a/src/netcode/server_connection.h b/src/netcode/server_connection.h deleted file mode 100644 index 7481d0eb5..000000000 --- a/src/netcode/server_connection.h +++ /dev/null @@ -1,30 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 server_connection.h -/// \brief Server-side part of connection handling - -#ifndef __D_SERVER_CONNECTION__ -#define __D_SERVER_CONNECTION__ - -#include "../command.h" -#include "../doomdef.h" -#include "../doomtype.h" - -void PT_ClientJoin(SINT8 node); -void PT_AskInfoViaMS(SINT8 node); -void PT_TellFilesNeeded(SINT8 node); -void PT_AskInfo(SINT8 node); - -extern tic_t jointimeout; -extern tic_t joindelay; -extern char playeraddress[MAXPLAYERS][64]; -extern consvar_t cv_showjoinaddress, cv_allownewplayer, cv_maxplayers, cv_joindelay, cv_rejointimeout; - -#endif diff --git a/src/netcode/tic_command.c b/src/netcode/tic_command.c deleted file mode 100644 index 620a10f7a..000000000 --- a/src/netcode/tic_command.c +++ /dev/null @@ -1,454 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 tic_command.c -/// \brief Tic command handling - -#include "tic_command.h" -#include "d_clisrv.h" -#include "net_command.h" -#include "client_connection.h" -#include "gamestate.h" -#include "i_net.h" -#include "../d_main.h" -#include "../g_game.h" -#include "../i_system.h" -#include "../i_time.h" -#include "../byteptr.h" -#include "../doomstat.h" -#include "../doomtype.h" - -tic_t firstticstosend; // Smallest netnode.tic -tic_t tictoclear = 0; // Optimize D_ClearTiccmd -ticcmd_t localcmds; -ticcmd_t localcmds2; -boolean cl_packetmissed; -ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; - -static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) -{ - const size_t d = n / sizeof(ticcmd_t); - const size_t r = n % sizeof(ticcmd_t); - UINT8 *ret = dest; - - if (r) - M_Memcpy(dest, src, n); - else if (d) - G_MoveTiccmd(dest, src, d); - return ret+n; -} - -static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) -{ - const size_t d = n / sizeof(ticcmd_t); - const size_t r = n % sizeof(ticcmd_t); - UINT8 *ret = src; - - if (r) - M_Memcpy(dest, src, n); - else if (d) - G_MoveTiccmd(dest, src, d); - return ret+n; -} - -/** Guesses the full value of a tic from its lowest byte, for a specific node - * - * \param low The lowest byte of the tic value - * \param node The node to deduce the tic for - * \return The full tic value - * - */ -tic_t ExpandTics(INT32 low, INT32 node) -{ - INT32 delta; - - delta = low - (netnodes[node].tic & UINT8_MAX); - - if (delta >= -64 && delta <= 64) - return (netnodes[node].tic & ~UINT8_MAX) + low; - else if (delta > 64) - return (netnodes[node].tic & ~UINT8_MAX) - 256 + low; - else //if (delta < -64) - return (netnodes[node].tic & ~UINT8_MAX) + 256 + low; -} - -void D_Clearticcmd(tic_t tic) -{ - D_FreeTextcmd(tic); - - for (INT32 i = 0; i < MAXPLAYERS; i++) - netcmds[tic%BACKUPTICS][i].angleturn = 0; - - DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); -} - -void D_ResetTiccmds(void) -{ - memset(&localcmds, 0, sizeof(ticcmd_t)); - memset(&localcmds2, 0, sizeof(ticcmd_t)); - - // Reset the net command list - for (INT32 i = 0; i < TEXTCMD_HASH_SIZE; i++) - while (textcmds[i]) - D_Clearticcmd(textcmds[i]->tic); -} - -// Check ticcmd for "speed hacks" -static void CheckTiccmdHacks(INT32 playernum) -{ - ticcmd_t *cmd = &netcmds[maketic%BACKUPTICS][playernum]; - if (cmd->forwardmove > MAXPLMOVE || cmd->forwardmove < -MAXPLMOVE - || cmd->sidemove > MAXPLMOVE || cmd->sidemove < -MAXPLMOVE) - { - CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), playernum); - SendKick(playernum, KICK_MSG_CON_FAIL); - } -} - -// Check player consistancy during the level -static void CheckConsistancy(SINT8 nodenum, tic_t tic) -{ - netnode_t *node = &netnodes[nodenum]; - INT16 neededconsistancy = consistancy[tic%BACKUPTICS]; - INT16 clientconsistancy = SHORT(netbuffer->u.clientpak.consistancy); - - if (tic > gametic || tic + BACKUPTICS - 1 <= gametic || gamestate != GS_LEVEL - || neededconsistancy == clientconsistancy || SV_ResendingSavegameToAnyone() - || node->resendingsavegame || node->savegameresendcooldown > I_GetTime()) - return; - - if (cv_resynchattempts.value) - { - // Tell the client we are about to resend them the gamestate - netbuffer->packettype = PT_WILLRESENDGAMESTATE; - HSendPacket(nodenum, true, 0, 0); - - node->resendingsavegame = true; - - if (cv_blamecfail.value) - CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"), - node->player+1, player_names[node->player], - neededconsistancy, clientconsistancy); - - DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n", - node->player, tic, neededconsistancy, clientconsistancy)); - } - else - { - SendKick(node->player, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - - DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n", - node->player, tic, neededconsistancy, clientconsistancy)); - } -} - -void PT_ClientCmd(SINT8 nodenum, INT32 netconsole) -{ - netnode_t *node = &netnodes[nodenum]; - tic_t realend, realstart; - - if (client) - return; - - // To save bytes, only the low byte of tic numbers are sent - // Use ExpandTics to figure out what the rest of the bytes are - realstart = ExpandTics(netbuffer->u.clientpak.client_tic, nodenum); - realend = ExpandTics(netbuffer->u.clientpak.resendfrom, nodenum); - - if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS - || netbuffer->packettype == PT_NODEKEEPALIVEMIS - || node->supposedtic < realend) - { - node->supposedtic = realend; - } - // Discard out of order packet - if (node->tic > realend) - { - DEBFILE(va("out of order ticcmd discarded nettics = %u\n", node->tic)); - return; - } - - // Update the nettics - node->tic = realend; - - // Don't do anything for packets of type NODEKEEPALIVE? - if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE - || netbuffer->packettype == PT_NODEKEEPALIVEMIS) - return; - - // As long as clients send valid ticcmds, the server can keep running, so reset the timeout - /// \todo Use a separate cvar for that kind of timeout? - node->freezetimeout = I_GetTime() + connectiontimeout; - - // Copy ticcmd - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1); - - // Splitscreen cmd - if ((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS) - && node->player2 >= 0) - G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)node->player2], - &netbuffer->u.client2pak.cmd2, 1); - - CheckTiccmdHacks(netconsole); - CheckConsistancy(nodenum, realstart); -} - -void PT_ServerTics(SINT8 node, INT32 netconsole) -{ - tic_t realend, realstart; - - if (!netnodes[node].ingame) - { - // Do not remove my own server (we have just get a out of order packet) - if (node != servernode) - { - DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype)); - Net_CloseConnection(node); - } - return; - } - - // Only accept PT_SERVERTICS from the server. - if (node != servernode) - { - CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node); - if (server) - SendKick(netconsole, KICK_MSG_CON_FAIL | KICK_MSG_KEEP_BODY); - return; - } - - realstart = netbuffer->u.serverpak.starttic; - realend = realstart + netbuffer->u.serverpak.numtics; - - realend = min(realend, gametic + CLIENTBACKUPTICS); - cl_packetmissed = realstart > neededtic; - - if (realstart <= neededtic && realend > neededtic) - { - UINT8 *pak = (UINT8 *)&netbuffer->u.serverpak.cmds; - - for (tic_t i = realstart; i < realend; i++) - { - // clear first - D_Clearticcmd(i); - - // copy the tics - pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, - netbuffer->u.serverpak.numslots*sizeof (ticcmd_t)); - - CL_CopyNetCommandsFromServerPacket(i); - } - - neededtic = realend; - } - else - { - DEBFILE(va("frame not in bound: %u\n", neededtic)); - } -} - -// send the client packet to the server -void CL_SendClientCmd(void) -{ - size_t packetsize = 0; - - netbuffer->packettype = PT_CLIENTCMD; - - if (cl_packetmissed) - netbuffer->packettype++; - netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX); - netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX); - - if (gamestate == GS_WAITINGPLAYERS) - { - // Send PT_NODEKEEPALIVE packet - netbuffer->packettype += 4; - packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16); - HSendPacket(servernode, false, 0, packetsize); - } - else if (gamestate != GS_NULL && (addedtogame || dedicated)) - { - G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1); - netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]); - - // Send a special packet with 2 cmd for splitscreen - if (splitscreen || botingame) - { - netbuffer->packettype += 2; - G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1); - packetsize = sizeof (client2cmd_pak); - } - else - packetsize = sizeof (clientcmd_pak); - - HSendPacket(servernode, false, 0, packetsize); - } - - if (cl_mode == CL_CONNECTED || dedicated) - CL_SendNetCommands(); -} - -// PT_SERVERTICS packets can grow too large for a single UDP packet, -// So this checks how many tics worth of data can be sent in one packet. -// The rest can be sent later, usually the next tic. -static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t lasttic) -{ - size_t size = BASESERVERTICSSIZE; - - for (tic_t tic = firsttic; tic < lasttic; tic++) - { - size += sizeof (ticcmd_t) * doomcom->numslots; - size += TotalTextCmdPerTic(tic); - - if (size > software_MAXPACKETLENGTH) - { - DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n", - sizeu1(size), tic, firsttic, lasttic)); - lasttic = tic; - - // Too bad: too many players have sent extra data - // and there is too much data for a single tic. - // To avoid that, keep the data for the next tic (see PT_TEXTCMD). - if (lasttic == firsttic) - { - if (size > MAXPACKETLENGTH) - I_Error("Too many players: can't send %s data for %d players to node %d\n" - "Well sorry nobody is perfect....\n", - sizeu1(size), doomcom->numslots, nodenum); - else - { - lasttic++; // send it anyway! - DEBFILE("sending it anyway\n"); - } - } - break; - } - } - - return lasttic - firsttic; -} - -// Sends the server packet -// Sends tic/net commands from firstticstosend to maketic-1 -void SV_SendTics(void) -{ - tic_t realfirsttic, lasttictosend; - - // Send to all clients except yourself - // For each node, create a packet with x tics and send it - // x is computed using node.supposedtic, max packet size and maketic - for (INT32 n = 1; n < MAXNETNODES; n++) - if (netnodes[n].ingame) - { - netnode_t *node = netnodes[n]; - - // assert node->supposedtic>=node->tic - realfirsttic = node->supposedtic; - lasttictosend = min(maketic, node->tic + CLIENTBACKUPTICS); - - if (realfirsttic >= lasttictosend) - { - // Well, we have sent all the tics, so we will use extra bandwidth - // to resend packets that are supposed lost. - // This is necessary since lost packet detection - // works when we receive a packet with firsttic > neededtic (PT_SERVERTICS) - DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n", - n, maketic, node->supposedtic, node->tic)); - - realfirsttic = node->tic; - - if (realfirsttic >= lasttictosend || (I_GetTime() + n)&3) - // All tics are Ok - continue; - - DEBFILE(va("Sent %d anyway\n", realfirsttic)); - } - realfirsttic = max(realfirsttic, firstticstosend); - - lasttictosend = realfirsttic + SV_CalculateNumTicsForPacket(n, realfirsttic, lasttictosend); - - // Prepare the packet header - netbuffer->packettype = PT_SERVERTICS; - netbuffer->u.serverpak.starttic = realfirsttic; - netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); - netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); - - // Fill and send the packet - UINT8 *bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; - for (tic_t i = realfirsttic; i < lasttictosend; i++) - bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); - for (tic_t i = realfirsttic; i < lasttictosend; i++) - SV_WriteNetCommandsForTic(i, &bufpos); - size_t packsize = bufpos - (UINT8 *)&(netbuffer->u); - HSendPacket(n, false, 0, packsize); - - // When tics are too large, only one tic is sent so don't go backwards! - if (lasttictosend-doomcom->extratics > realfirsttic) - node->supposedtic = lasttictosend-doomcom->extratics; - else - node->supposedtic = lasttictosend; - node->supposedtic = max(node->supposedtic, node->tic); - } - - // node 0 is me! - netnodes[0].supposedtic = maketic; -} - -void Local_Maketic(INT32 realtics) -{ - I_OsPolling(); // I_Getevent - D_ProcessEvents(); // menu responder, cons responder, - // game responder calls HU_Responder, AM_Responder, - // and G_MapEventsToControls - if (!dedicated) - rendergametic = gametic; - // translate inputs (keyboard/mouse/gamepad) into game controls - G_BuildTiccmd(&localcmds, realtics, 1); - if (splitscreen || botingame) - G_BuildTiccmd(&localcmds2, realtics, 2); - - localcmds.angleturn |= TICCMD_RECEIVED; - localcmds2.angleturn |= TICCMD_RECEIVED; -} - -// create missed tic -void SV_Maketic(void) -{ - for (INT32 i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - // We didn't receive this tic - if ((netcmds[maketic % BACKUPTICS][i].angleturn & TICCMD_RECEIVED) == 0) - { - ticcmd_t * ticcmd = &netcmds[(maketic ) % BACKUPTICS][i]; - ticcmd_t *prevticcmd = &netcmds[(maketic - 1) % BACKUPTICS][i]; - - if (players[i].quittime) - { - // Copy the angle/aiming from the previous tic - // and empty the other inputs - memset(ticcmd, 0, sizeof(netcmds[0][0])); - ticcmd->angleturn = prevticcmd->angleturn | TICCMD_RECEIVED; - ticcmd->aiming = prevticcmd->aiming; - } - else - { - DEBFILE(va("MISS tic%4d for player %d\n", maketic, i)); - // Copy the input from the previous tic - *ticcmd = *prevticcmd; - ticcmd->angleturn &= ~TICCMD_RECEIVED; - } - } - } - - // All tics have been processed, make the next - maketic++; -} diff --git a/src/netcode/tic_command.h b/src/netcode/tic_command.h deleted file mode 100644 index 289750fb3..000000000 --- a/src/netcode/tic_command.h +++ /dev/null @@ -1,44 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 tic_command.h -/// \brief Tic command handling - -#ifndef __D_TIC_COMMAND__ -#define __D_TIC_COMMAND__ - -#include "d_clisrv.h" -#include "../d_ticcmd.h" -#include "../doomdef.h" -#include "../doomtype.h" - -extern tic_t firstticstosend; // min of the nettics -extern tic_t tictoclear; // optimize d_clearticcmd - -extern ticcmd_t localcmds; -extern ticcmd_t localcmds2; -extern boolean cl_packetmissed; - -extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; - -tic_t ExpandTics(INT32 low, INT32 node); -void D_Clearticcmd(tic_t tic); -void D_ResetTiccmds(void); - -void PT_ClientCmd(SINT8 nodenum, INT32 netconsole); -void PT_ServerTics(SINT8 node, INT32 netconsole); - -// send the client packet to the server -void CL_SendClientCmd(void); - -void SV_SendTics(void); -void Local_Maketic(INT32 realtics); -void SV_Maketic(void); - -#endif diff --git a/src/p_ceilng.c b/src/p_ceilng.c index 949eeddf2..66f2dd58e 100644 --- a/src/p_ceilng.c +++ b/src/p_ceilng.c @@ -17,7 +17,7 @@ #include "r_main.h" #include "s_sound.h" #include "z_zone.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" // ========================================================================== // CEILINGS diff --git a/src/p_haptic.c b/src/p_haptic.c index 4d22fbd77..dbfa58737 100644 --- a/src/p_haptic.c +++ b/src/p_haptic.c @@ -11,7 +11,7 @@ #include "p_haptic.h" #include "g_game.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" #include "i_gamepad.h" #include "doomstat.h" diff --git a/src/p_inter.c b/src/p_inter.c index f33494d1f..f3c13e315 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -30,7 +30,6 @@ #include "m_misc.h" #include "v_video.h" // video flags for CEchos #include "f_finale.h" -#include "netcode/net_command.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" diff --git a/src/p_lights.c b/src/p_lights.c index a1f4de853..4c783f884 100644 --- a/src/p_lights.c +++ b/src/p_lights.c @@ -17,7 +17,7 @@ #include "r_state.h" #include "z_zone.h" #include "m_random.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" /** Removes any active lighting effects in a sector. * diff --git a/src/p_mobj.c b/src/p_mobj.c index daf8e0262..c14ffb251 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -36,7 +36,6 @@ #include "p_slopes.h" #include "f_finale.h" #include "m_cond.h" -#include "netcode/net_command.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); diff --git a/src/p_setup.c b/src/p_setup.c index 916fb7524..d7b532282 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -87,8 +87,6 @@ #include "taglist.h" -#include "netcode/net_command.h" - // // Map MD5, calculated on level load. // Sent to clients in PT_SERVERINFO. diff --git a/src/p_tick.c b/src/p_tick.c index e92dfe8e4..fe6a4d33f 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -26,8 +26,6 @@ #include "r_main.h" #include "r_fps.h" #include "i_video.h" // rendermode -#include "netcode/net_command.h" -#include "netcode/server_connection.h" // Object place #include "m_cheat.h" diff --git a/src/p_user.c b/src/p_user.c index de3bd0af2..4ca4e6c8a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -18,8 +18,7 @@ #include "doomdef.h" #include "i_system.h" #include "d_event.h" -#include "netcode/d_net.h" -#include "netcode/net_command.h" +#include "d_net.h" #include "g_game.h" #include "p_local.h" #include "r_fps.h" diff --git a/src/r_segs.c b/src/r_segs.c index 704f33334..43a7f945f 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -20,7 +20,7 @@ #include "w_wad.h" #include "z_zone.h" -#include "netcode/d_netcmd.h" +#include "d_netcmd.h" #include "m_misc.h" #include "p_local.h" // Camera... #include "p_slopes.h" diff --git a/src/r_things.c b/src/r_things.c index 5e444d0b9..461761977 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -34,7 +34,7 @@ #include "p_tick.h" #include "p_local.h" #include "p_slopes.h" -#include "netcode/d_netfil.h" // blargh. for nameonly(). +#include "d_netfil.h" // blargh. for nameonly(). #include "m_cheat.h" // objectplace #ifdef HWRENDER #include "hardware/hw_md2.h" diff --git a/src/screen.c b/src/screen.c index ad9c91462..d4785f941 100644 --- a/src/screen.c +++ b/src/screen.c @@ -27,7 +27,7 @@ #include "hu_stuff.h" #include "z_zone.h" #include "d_main.h" -#include "netcode/d_clisrv.h" +#include "d_clisrv.h" #include "f_finale.h" #include "y_inter.h" // usebuffer #include "i_sound.h" // closed captions diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index f27504f76..ecde251fb 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -15,7 +15,7 @@ #include "../i_system.h" #include "../doomdef.h" #include "../d_main.h" -#include "../netcode/d_netcmd.h" +#include "../d_netcmd.h" #include "../g_game.h" #include "../m_argv.h" #include "../m_menu.h" diff --git a/src/sdl/i_net.c b/src/sdl/i_net.c index 515a85568..ee4a34c13 100644 --- a/src/sdl/i_net.c +++ b/src/sdl/i_net.c @@ -21,16 +21,16 @@ #include "../i_system.h" #include "../d_event.h" -#include "../netcode/d_net.h" +#include "../d_net.h" #include "../m_argv.h" #include "../doomstat.h" -#include "../netcode/i_net.h" +#include "../i_net.h" #include "../z_zone.h" -#include "../netcode/i_tcp.h" +#include "../i_tcp.h" #ifdef HAVE_SDL diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 47402e549..ee082fd5d 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -188,8 +188,7 @@ static char returnWadPath[256]; #include "../i_gamepad.h" #include "../i_threads.h" #include "../screen.h" //vid.WndParent -#include "../netcode/d_net.h" -#include "../netcode/commands.h" +#include "../d_net.h" #include "../g_game.h" #include "../filesrch.h" #include "endtxt.h" @@ -208,7 +207,7 @@ static char returnWadPath[256]; #if !defined(NOMUMBLE) && defined(HAVE_MUMBLE) // Mumble context string -#include "../netcode/d_clisrv.h" +#include "../d_clisrv.h" #include "../byteptr.h" #endif @@ -1593,7 +1592,9 @@ void I_Quit(void) SDLforceUngrabMouse(); quiting = SDL_FALSE; M_SaveConfig(NULL); //save game config, cvars.. +#ifndef NONET D_SaveBan(); // save the ban list +#endif G_SaveGameData(); // Tails 12-08-2002 //added:16-02-98: when recording a demo, should exit using 'q' key, // but sometimes we forget and use 'F10'.. so save here too. @@ -1708,7 +1709,9 @@ void I_Error(const char *error, ...) // --- M_SaveConfig(NULL); // save game config, cvars.. +#ifndef NONET D_SaveBan(); // save the ban list +#endif G_SaveGameData(); // Tails 12-08-2002 // Shutdown. Here might be other errors. diff --git a/src/sdl/i_ttf.c b/src/sdl/i_ttf.c index 1f838e9b4..f2cd497ee 100644 --- a/src/sdl/i_ttf.c +++ b/src/sdl/i_ttf.c @@ -21,7 +21,7 @@ #include "SDL_ttf.h" #include "../doomdef.h" #include "../doomstat.h" -#include "../netcode/d_netfil.h" +#include "../d_netfil.h" #include "../filesrch.h" #include "i_ttf.h" diff --git a/src/snake.c b/src/snake.c deleted file mode 100644 index 21e79401d..000000000 --- a/src/snake.c +++ /dev/null @@ -1,535 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 2023-2023 by Louis-Antoine de Moulins de Rochefort. -// -// 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 snake.c -/// \brief Snake minigame for the download screen. - -#include "snake.h" -#include "g_input.h" -#include "m_random.h" -#include "s_sound.h" -#include "screen.h" -#include "v_video.h" -#include "w_wad.h" -#include "z_zone.h" - -#define SPEED 5 - -#define NUM_BLOCKS_X 20 -#define NUM_BLOCKS_Y 10 -#define BLOCK_SIZE 12 -#define BORDER_SIZE 12 - -#define MAP_WIDTH (NUM_BLOCKS_X * BLOCK_SIZE) -#define MAP_HEIGHT (NUM_BLOCKS_Y * BLOCK_SIZE) - -#define LEFT_X ((BASEVIDWIDTH - MAP_WIDTH) / 2 - BORDER_SIZE) -#define RIGHT_X (LEFT_X + MAP_WIDTH + BORDER_SIZE * 2 - 1) -#define BOTTOM_Y (BASEVIDHEIGHT - 48) -#define TOP_Y (BOTTOM_Y - MAP_HEIGHT - BORDER_SIZE * 2 + 1) - -enum bonustype_s { - BONUS_NONE = 0, - BONUS_SLOW, - BONUS_FAST, - BONUS_GHOST, - BONUS_NUKE, - BONUS_SCISSORS, - BONUS_REVERSE, - BONUS_EGGMAN, - NUM_BONUSES, -}; - -typedef struct snake_s -{ - boolean paused; - boolean pausepressed; - tic_t time; - tic_t nextupdate; - boolean gameover; - UINT8 background; - - UINT16 snakelength; - enum bonustype_s snakebonus; - tic_t snakebonustime; - UINT8 snakex[NUM_BLOCKS_X * NUM_BLOCKS_Y]; - UINT8 snakey[NUM_BLOCKS_X * NUM_BLOCKS_Y]; - UINT8 snakedir[NUM_BLOCKS_X * NUM_BLOCKS_Y]; - - UINT8 applex; - UINT8 appley; - - enum bonustype_s bonustype; - UINT8 bonusx; - UINT8 bonusy; -} snake_t; - -static const char *bonuspatches[] = { - NULL, - "DL_SLOW", - "TVSSC0", - "TVIVC0", - "TVARC0", - "DL_SCISSORS", - "TVRCC0", - "TVEGC0", -}; - -static const char *backgrounds[] = { - "RVPUMICF", - "FRSTRCKF", - "TAR", - "MMFLRB4", - "RVDARKF1", - "RVZWALF1", - "RVZWALF4", - "RVZWALF5", - "RVZGRS02", - "RVZGRS04", -}; - -static void Initialise(snake_t *snake) -{ - snake->paused = false; - snake->pausepressed = false; - snake->time = 0; - snake->nextupdate = SPEED; - snake->gameover = false; - snake->background = M_RandomKey(sizeof(backgrounds) / sizeof(*backgrounds)); - - snake->snakelength = 1; - snake->snakebonus = BONUS_NONE; - snake->snakex[0] = M_RandomKey(NUM_BLOCKS_X); - snake->snakey[0] = M_RandomKey(NUM_BLOCKS_Y); - snake->snakedir[0] = 0; - snake->snakedir[1] = 0; - - snake->applex = M_RandomKey(NUM_BLOCKS_X); - snake->appley = M_RandomKey(NUM_BLOCKS_Y); - - snake->bonustype = BONUS_NONE; -} - -static UINT8 GetOppositeDir(UINT8 dir) -{ - if (dir == 1 || dir == 3) - return dir + 1; - else if (dir == 2 || dir == 4) - return dir - 1; - else - return 12 + 5 - dir; -} - -static void FindFreeSlot(snake_t *snake, UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) -{ - UINT8 x, y; - UINT16 i; - - do - { - x = M_RandomKey(NUM_BLOCKS_X); - y = M_RandomKey(NUM_BLOCKS_Y); - - for (i = 0; i < snake->snakelength; i++) - if (x == snake->snakex[i] && y == snake->snakey[i]) - break; - } while (i < snake->snakelength || (x == headx && y == heady) - || (x == snake->applex && y == snake->appley) - || (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy)); - - *freex = x; - *freey = y; -} - -void Snake_Allocate(void **opaque) -{ - if (*opaque) - Snake_Free(opaque); - *opaque = malloc(sizeof(snake_t)); - Initialise(*opaque); -} - -void Snake_Update(void *opaque) -{ - UINT8 x, y; - UINT8 oldx, oldy; - UINT16 i; - UINT16 joystate = 0; - - snake_t *snake = opaque; - - // Handle retry - if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER])) - { - Initialise(snake); - snake->pausepressed = true; // Avoid accidental pause on respawn - } - - // Handle pause - if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER]) - { - if (!snake->pausepressed) - snake->paused = !snake->paused; - snake->pausepressed = true; - } - else - snake->pausepressed = false; - - if (snake->paused) - return; - - snake->time++; - - x = snake->snakex[0]; - y = snake->snakey[0]; - oldx = snake->snakex[1]; - oldy = snake->snakey[1]; - - // Update direction - if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) - { - if (snake->snakelength < 2 || x <= oldx) - snake->snakedir[0] = 1; - } - else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) - { - if (snake->snakelength < 2 || x >= oldx) - snake->snakedir[0] = 2; - } - else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) - { - if (snake->snakelength < 2 || y <= oldy) - snake->snakedir[0] = 3; - } - else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) - { - if (snake->snakelength < 2 || y >= oldy) - snake->snakedir[0] = 4; - } - - if (snake->snakebonustime) - { - snake->snakebonustime--; - if (!snake->snakebonustime) - snake->snakebonus = BONUS_NONE; - } - - snake->nextupdate--; - if (snake->nextupdate) - return; - if (snake->snakebonus == BONUS_SLOW) - snake->nextupdate = SPEED * 2; - else if (snake->snakebonus == BONUS_FAST) - snake->nextupdate = SPEED * 2 / 3; - else - snake->nextupdate = SPEED; - - if (snake->gameover) - return; - - // Find new position - switch (snake->snakedir[0]) - { - case 1: - if (x > 0) - x--; - else - snake->gameover = true; - break; - case 2: - if (x < NUM_BLOCKS_X - 1) - x++; - else - snake->gameover = true; - break; - case 3: - if (y > 0) - y--; - else - snake->gameover = true; - break; - case 4: - if (y < NUM_BLOCKS_Y - 1) - y++; - else - snake->gameover = true; - break; - } - - // Check collision with snake - if (snake->snakebonus != BONUS_GHOST) - for (i = 1; i < snake->snakelength - 1; i++) - if (x == snake->snakex[i] && y == snake->snakey[i]) - { - if (snake->snakebonus == BONUS_SCISSORS) - { - snake->snakebonus = BONUS_NONE; - snake->snakelength = i; - S_StartSound(NULL, sfx_adderr); - } - else - snake->gameover = true; - } - - if (snake->gameover) - { - S_StartSound(NULL, sfx_lose); - return; - } - - // Check collision with apple - if (x == snake->applex && y == snake->appley) - { - if (snake->snakelength + 3 < NUM_BLOCKS_X * NUM_BLOCKS_Y) - { - snake->snakelength++; - snake->snakex [snake->snakelength - 1] = snake->snakex [snake->snakelength - 2]; - snake->snakey [snake->snakelength - 1] = snake->snakey [snake->snakelength - 2]; - snake->snakedir[snake->snakelength - 1] = snake->snakedir[snake->snakelength - 2]; - } - - // Spawn new apple - FindFreeSlot(snake, &snake->applex, &snake->appley, x, y); - - // Spawn new bonus - if (!(snake->snakelength % 5)) - { - do - { - snake->bonustype = M_RandomKey(NUM_BONUSES - 1) + 1; - } while (snake->snakelength > NUM_BLOCKS_X * NUM_BLOCKS_Y * 3 / 4 - && (snake->bonustype == BONUS_EGGMAN || snake->bonustype == BONUS_FAST || snake->bonustype == BONUS_REVERSE)); - - FindFreeSlot(snake, &snake->bonusx, &snake->bonusy, x, y); - } - - S_StartSound(NULL, sfx_s3k6b); - } - - if (snake->snakelength > 1 && snake->snakedir[0]) - { - UINT8 dir = snake->snakedir[0]; - - oldx = snake->snakex[1]; - oldy = snake->snakey[1]; - - // Move - for (i = snake->snakelength - 1; i > 0; i--) - { - snake->snakex[i] = snake->snakex[i - 1]; - snake->snakey[i] = snake->snakey[i - 1]; - snake->snakedir[i] = snake->snakedir[i - 1]; - } - - // Handle corners - if (x < oldx && dir == 3) - dir = 5; - else if (x > oldx && dir == 3) - dir = 6; - else if (x < oldx && dir == 4) - dir = 7; - else if (x > oldx && dir == 4) - dir = 8; - else if (y < oldy && dir == 1) - dir = 9; - else if (y < oldy && dir == 2) - dir = 10; - else if (y > oldy && dir == 1) - dir = 11; - else if (y > oldy && dir == 2) - dir = 12; - snake->snakedir[1] = dir; - } - - snake->snakex[0] = x; - snake->snakey[0] = y; - - // Check collision with bonus - if (snake->bonustype != BONUS_NONE && x == snake->bonusx && y == snake->bonusy) - { - S_StartSound(NULL, sfx_ncchip); - - switch (snake->bonustype) - { - case BONUS_SLOW: - snake->snakebonus = BONUS_SLOW; - snake->snakebonustime = 20 * TICRATE; - break; - case BONUS_FAST: - snake->snakebonus = BONUS_FAST; - snake->snakebonustime = 20 * TICRATE; - break; - case BONUS_GHOST: - snake->snakebonus = BONUS_GHOST; - snake->snakebonustime = 10 * TICRATE; - break; - case BONUS_NUKE: - for (i = 0; i < snake->snakelength; i++) - { - snake->snakex [i] = snake->snakex [0]; - snake->snakey [i] = snake->snakey [0]; - snake->snakedir[i] = snake->snakedir[0]; - } - - S_StartSound(NULL, sfx_bkpoof); - break; - case BONUS_SCISSORS: - snake->snakebonus = BONUS_SCISSORS; - snake->snakebonustime = 60 * TICRATE; - break; - case BONUS_REVERSE: - for (i = 0; i < (snake->snakelength + 1) / 2; i++) - { - UINT16 i2 = snake->snakelength - 1 - i; - UINT8 tmpx = snake->snakex [i]; - UINT8 tmpy = snake->snakey [i]; - UINT8 tmpdir = snake->snakedir[i]; - - // Swap first segment with last segment - snake->snakex [i] = snake->snakex [i2]; - snake->snakey [i] = snake->snakey [i2]; - snake->snakedir[i] = GetOppositeDir(snake->snakedir[i2]); - snake->snakex [i2] = tmpx; - snake->snakey [i2] = tmpy; - snake->snakedir[i2] = GetOppositeDir(tmpdir); - } - - snake->snakedir[0] = 0; - - S_StartSound(NULL, sfx_gravch); - break; - default: - if (snake->snakebonus != BONUS_GHOST) - { - snake->gameover = true; - S_StartSound(NULL, sfx_lose); - } - } - - snake->bonustype = BONUS_NONE; - } -} - -void Snake_Draw(void *opaque) -{ - INT16 i; - - snake_t *snake = opaque; - - // Background - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - - V_DrawFlatFill( - LEFT_X + BORDER_SIZE, - TOP_Y + BORDER_SIZE, - MAP_WIDTH, - MAP_HEIGHT, - W_GetNumForName(backgrounds[snake->background]) - ); - - // Borders - V_DrawFill(LEFT_X, TOP_Y, BORDER_SIZE + MAP_WIDTH, BORDER_SIZE, 242); // Top - V_DrawFill(LEFT_X + BORDER_SIZE + MAP_WIDTH, TOP_Y, BORDER_SIZE, BORDER_SIZE + MAP_HEIGHT, 242); // Right - V_DrawFill(LEFT_X + BORDER_SIZE, TOP_Y + BORDER_SIZE + MAP_HEIGHT, BORDER_SIZE + MAP_WIDTH, BORDER_SIZE, 242); // Bottom - V_DrawFill(LEFT_X, TOP_Y + BORDER_SIZE, BORDER_SIZE, BORDER_SIZE + MAP_HEIGHT, 242); // Left - - // Apple - V_DrawFixedPatch( - (LEFT_X + BORDER_SIZE + snake->applex * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, - (TOP_Y + BORDER_SIZE + snake->appley * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, - FRACUNIT / 4, - 0, - W_CachePatchLongName("DL_APPLE", PU_HUDGFX), - NULL - ); - - // Bonus - if (snake->bonustype != BONUS_NONE) - V_DrawFixedPatch( - (LEFT_X + BORDER_SIZE + snake->bonusx * BLOCK_SIZE + BLOCK_SIZE / 2 ) * FRACUNIT, - (TOP_Y + BORDER_SIZE + snake->bonusy * BLOCK_SIZE + BLOCK_SIZE / 2 + 4) * FRACUNIT, - FRACUNIT / 2, - 0, - W_CachePatchLongName(bonuspatches[snake->bonustype], PU_HUDGFX), - NULL - ); - - // Snake - if (!snake->gameover || snake->time % 8 < 8 / 2) // Blink if game over - { - for (i = snake->snakelength - 1; i >= 0; i--) - { - const char *patchname; - UINT8 dir = snake->snakedir[i]; - - if (i == 0) // Head - { - switch (dir) - { - case 1: patchname = "DL_SNAKEHEAD_L"; break; - case 2: patchname = "DL_SNAKEHEAD_R"; break; - case 3: patchname = "DL_SNAKEHEAD_T"; break; - case 4: patchname = "DL_SNAKEHEAD_B"; break; - default: patchname = "DL_SNAKEHEAD_M"; - } - } - else // Body - { - switch (dir) - { - case 1: patchname = "DL_SNAKEBODY_L"; break; - case 2: patchname = "DL_SNAKEBODY_R"; break; - case 3: patchname = "DL_SNAKEBODY_T"; break; - case 4: patchname = "DL_SNAKEBODY_B"; break; - case 5: patchname = "DL_SNAKEBODY_LT"; break; - case 6: patchname = "DL_SNAKEBODY_RT"; break; - case 7: patchname = "DL_SNAKEBODY_LB"; break; - case 8: patchname = "DL_SNAKEBODY_RB"; break; - case 9: patchname = "DL_SNAKEBODY_TL"; break; - case 10: patchname = "DL_SNAKEBODY_TR"; break; - case 11: patchname = "DL_SNAKEBODY_BL"; break; - case 12: patchname = "DL_SNAKEBODY_BR"; break; - default: patchname = "DL_SNAKEBODY_B"; - } - } - - V_DrawFixedPatch( - (LEFT_X + BORDER_SIZE + snake->snakex[i] * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, - (TOP_Y + BORDER_SIZE + snake->snakey[i] * BLOCK_SIZE + BLOCK_SIZE / 2) * FRACUNIT, - i == 0 && dir == 0 ? FRACUNIT / 5 : FRACUNIT / 2, - snake->snakebonus == BONUS_GHOST ? V_TRANSLUCENT : 0, - W_CachePatchLongName(patchname, PU_HUDGFX), - NULL - ); - } - } - - // Length - V_DrawString(RIGHT_X + 4, TOP_Y, V_MONOSPACE, va("%u", snake->snakelength)); - - // Bonus - if (snake->snakebonus != BONUS_NONE - && (snake->snakebonustime >= 3 * TICRATE || snake->time % 4 < 4 / 2)) - V_DrawFixedPatch( - (RIGHT_X + 10) * FRACUNIT, - (TOP_Y + 24) * FRACUNIT, - FRACUNIT / 2, - 0, - W_CachePatchLongName(bonuspatches[snake->snakebonus], PU_HUDGFX), - NULL - ); -} - -void Snake_Free(void **opaque) -{ - if (*opaque) - { - free(opaque); - *opaque = NULL; - } -} diff --git a/src/snake.h b/src/snake.h deleted file mode 100644 index a3106bb0f..000000000 --- a/src/snake.h +++ /dev/null @@ -1,20 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 2023-2023 by Louis-Antoine de Moulins de Rochefort. -// -// 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 snake.h -/// \brief Snake minigame for the download screen. - -#ifndef __SNAKE__ -#define __SNAKE__ - -void Snake_Allocate(void **opaque); -void Snake_Update(void *opaque); -void Snake_Draw(void *opaque); -void Snake_Free(void **opaque); - -#endif diff --git a/src/w_wad.c b/src/w_wad.c index 1e64cf093..42c6bf83b 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -51,8 +51,8 @@ #include "filesrch.h" #include "d_main.h" -#include "netcode/d_netfil.h" -#include "netcode/d_clisrv.h" +#include "d_netfil.h" +#include "d_clisrv.h" #include "dehacked.h" #include "r_defs.h" #include "r_data.h" diff --git a/src/y_inter.c b/src/y_inter.c index de757ec70..7faceff50 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -15,7 +15,7 @@ #include "f_finale.h" #include "g_game.h" #include "hu_stuff.h" -#include "netcode/i_net.h" +#include "i_net.h" #include "i_video.h" #include "p_tick.h" #include "r_defs.h" From 43eb7526c0bc51f805517d8f1944f636fa884b5e Mon Sep 17 00:00:00 2001 From: Alam Ed Arias Date: Fri, 20 Jan 2023 19:00:50 +0000 Subject: [PATCH 63/89] Update http-mserv.c --- src/http-mserv.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/http-mserv.c b/src/http-mserv.c index 7b418c99a..6e3de4535 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -86,13 +86,8 @@ Contact_error (void) static void get_user_agent(char *buf, size_t len) { -#if defined(__STDC__) && __STDC_VERSION__ >= 201112L - if (sprintf_s(buf, len, "%s/%s (%s; %s; %i; %i) SRB2BASE/%i", SRB2APPLICATION, VERSIONSTRING, compbranch, comprevision, MODID, MODVERSION, CODEBASE) < 1) + if (snprintf(buf, len, "%s/%s (%s; %s; %i; %i) SRB2BASE/%i", SRB2APPLICATION, VERSIONSTRING, compbranch, comprevision, MODID, MODVERSION, CODEBASE) < 0) I_Error("http-mserv: get_user_agent failed"); -#else - if (sprintf(buf, "%s/%s (%s; %s; %i; %i) SRB2BASE/%i", SRB2APPLICATION, VERSIONSTRING, compbranch, comprevision, MODID, MODVERSION, CODEBASE) < 0) - I_Error("http-mserv: get_user_agent failed"); -#endif } static void From 6c54af377481a42f639da2db6cefe33d02918bce Mon Sep 17 00:00:00 2001 From: Eidolon Date: Wed, 25 Jan 2023 19:44:39 -0600 Subject: [PATCH 64/89] Fix precip interpolation Fixes STJr/SRB2#916 --- src/p_mobj.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index c14ffb251..635d4f42b 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3989,12 +3989,11 @@ void P_NullPrecipThinker(precipmobj_t *mobj) { //(void)mobj; mobj->precipflags &= ~PCF_THUNK; + R_ResetPrecipitationMobjInterpolationState(mobj); } void P_SnowThinker(precipmobj_t *mobj) { - R_ResetPrecipitationMobjInterpolationState(mobj); - P_CycleStateAnimation((mobj_t *)mobj); // adjust height @@ -4007,8 +4006,6 @@ void P_SnowThinker(precipmobj_t *mobj) void P_RainThinker(precipmobj_t *mobj) { - R_ResetPrecipitationMobjInterpolationState(mobj); - P_CycleStateAnimation((mobj_t *)mobj); if (mobj->state != &states[S_RAIN1]) From add018cb8333e7f7846a93ce41aefac7380e0b2e Mon Sep 17 00:00:00 2001 From: Eidolon Date: Wed, 25 Jan 2023 20:37:04 -0600 Subject: [PATCH 65/89] Interpolate minecart marks Fixes STJr/SRB2#906 Uses the old displacement of the minecart to position the mark relative to its destination. It's not completely correct, but it works. --- src/p_user.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/p_user.c b/src/p_user.c index 4ca4e6c8a..ba43a422f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -11038,6 +11038,21 @@ static void P_MinecartThink(player_t *player) S_StartSound(minecart, minecart->info->activesound); } } + + // Mark interpolation; the old positions need to be relative to the displacement from the minecart _after_ it's moved. + // This isn't quite correct (it captures the landing wobble) but it works well enough + if (detleft) + { + detleft->old_x = detleft->x - (minecart->old_x - minecart->old_x2); + detleft->old_y = detleft->y - (minecart->old_y - minecart->old_y2); + detleft->old_z = detleft->z - (minecart->old_z - minecart->old_z2); + } + if (detright) + { + detright->old_x = detright->x - (minecart->old_x - minecart->old_x2); + detright->old_y = detright->y - (minecart->old_y - minecart->old_y2); + detright->old_z = detright->z - (minecart->old_z - minecart->old_z2); + } } else { From b02707a4ee4966e7768cc6923fe3ab98c212ee5d Mon Sep 17 00:00:00 2001 From: Eidolon Date: Wed, 25 Jan 2023 20:44:04 -0600 Subject: [PATCH 66/89] Adjust caption pop-in by tics instead of frames Fixes STJr/SRB2#900 pop-in animation being affected by framerate --- src/screen.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/screen.c b/src/screen.c index d4785f941..3842a365d 100644 --- a/src/screen.c +++ b/src/screen.c @@ -621,7 +621,13 @@ void SCR_ClosedCaptions(void) y = basey-((i + 2)*10); if (closedcaptions[i].b) - y -= (closedcaptions[i].b--)*vid.dupy; + { + y -= closedcaptions[i].b * vid.dupy; + if (renderisnewtic) + { + closedcaptions[i].b--; + } + } if (closedcaptions[i].t < CAPTIONFADETICS) flags |= (((CAPTIONFADETICS-closedcaptions[i].t)/2)*V_10TRANS); From 42fa7f4723f25fc24c03e64a03a51aa9e02fa70c Mon Sep 17 00:00:00 2001 From: Lach Date: Thu, 26 Jan 2023 15:29:35 +1100 Subject: [PATCH 67/89] Invert OpenGL rollangle for papersprites to match software renderer behaviour --- src/hardware/hw_main.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 3a6baab71..05d1be28f 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5180,7 +5180,15 @@ static void HWR_ProjectSprite(mobj_t *thing) if (thing->rollangle && !(splat && !(thing->renderflags & RF_NOSPLATROLLANGLE))) { - rollangle = R_GetRollAngle(thing->rollangle); + if (papersprite) + { + // a positive rollangle should should pitch papersprites upwards relative to their facing angle + rollangle = R_GetRollAngle(InvAngle(thing->rollangle)); + } + else + { + rollangle = R_GetRollAngle(thing->rollangle); + } rotsprite = Patch_GetRotatedSprite(sprframe, (thing->frame & FF_FRAMEMASK), rot, flip, false, sprinfo, rollangle); if (rotsprite != NULL) @@ -5234,7 +5242,7 @@ static void HWR_ProjectSprite(mobj_t *thing) { interpmobjstate_t casterinterp = { 0 }; fixed_t groundz; - fixed_t floordiff; + fixed_t floordiff; if (R_UsingFrameInterpolation() && !paused) { @@ -5244,7 +5252,7 @@ static void HWR_ProjectSprite(mobj_t *thing) { R_InterpolateMobjState(caster, FRACUNIT, &casterinterp); } - + groundz = R_GetShadowZ(thing, NULL); floordiff = abs(((thing->eflags & MFE_VERTICALFLIP) ? caster->height : 0) + casterinterp.z - groundz); From f1a7494a31a08f5b829cb95d2511fb7d531af012 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 08:59:51 -0600 Subject: [PATCH 68/89] Handle splitscreen lua hud lists separately Fixes STJr/SRB2#935 by ensuring each drawlist is drawn separately while stplyr is set correctly. --- src/st_stuff.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/st_stuff.c b/src/st_stuff.c index ed130c912..3e75750a8 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -164,7 +164,7 @@ hudinfo_t hudinfo[NUMHUDITEMS] = { 288, 176, V_SNAPTORIGHT|V_SNAPTOBOTTOM}, // HUD_POWERUPS }; -static huddrawlist_h luahuddrawlist_game; +static huddrawlist_h luahuddrawlist_game[2]; static huddrawlist_h luahuddrawlist_titlecard; // @@ -427,7 +427,8 @@ void ST_Init(void) ST_LoadGraphics(); - luahuddrawlist_game = LUA_HUD_CreateDrawList(); + luahuddrawlist_game[0] = LUA_HUD_CreateDrawList(); + luahuddrawlist_game[1] = LUA_HUD_CreateDrawList(); luahuddrawlist_titlecard = LUA_HUD_CreateDrawList(); } @@ -2757,10 +2758,13 @@ static void ST_overlayDrawer(void) if (!(netgame || multiplayer) || !hu_showscores) { + INT32 hooklistindex = splitscreen && stplyr == &players[secondarydisplayplayer] ? 1 : 0; if (renderisnewtic) { - LUA_HUDHOOK(game, luahuddrawlist_game); + LUA_HUD_ClearDrawList(luahuddrawlist_game[hooklistindex]); + LUA_HUDHOOK(game, luahuddrawlist_game[hooklistindex]); } + LUA_HUD_DrawList(luahuddrawlist_game[hooklistindex]); } // draw level title Tails @@ -2839,10 +2843,6 @@ void ST_Drawer(void) if (st_overlay) { - if (renderisnewtic) - { - LUA_HUD_ClearDrawList(luahuddrawlist_game); - } // No deadview! stplyr = &players[displayplayer]; ST_overlayDrawer(); @@ -2852,7 +2852,5 @@ void ST_Drawer(void) stplyr = &players[secondarydisplayplayer]; ST_overlayDrawer(); } - - LUA_HUD_DrawList(luahuddrawlist_game); } } From bbd4cff75cb92cafe5f21a2cb83c1a8830184b94 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:24:21 -0600 Subject: [PATCH 69/89] Revert "Merge branch 'gamepadtweaks' into 'next'" This reverts commit 96bfd8cea7c4ed75357a25603ecdf3ab98fd625b, reversing changes made to 24f9ec4ec4039155ee2cfee7ef1eefcb46d1398a. --- src/command.c | 160 +++++++++++++++++++++++++++++++ src/d_netcmd.c | 9 ++ src/deh_tables.c | 5 + src/g_game.c | 61 ++++++++++-- src/g_game.h | 8 ++ src/g_input.c | 240 ++++++++++++++++++++++++++++------------------- src/g_input.h | 4 +- src/m_menu.c | 55 +++++------ 8 files changed, 404 insertions(+), 138 deletions(-) diff --git a/src/command.c b/src/command.c index d1db9f9c9..201cceeee 100644 --- a/src/command.c +++ b/src/command.c @@ -2181,6 +2181,132 @@ static void CV_EnforceExecVersion(void) CV_StealthSetValue(&cv_execversion, EXECVERSION); } +static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) +{ + // If ALL axis settings are previous defaults, set them to the new defaults + // EXECVERSION < 26 (2.1.21) + + if (joyaxis_default) + { + if (!stricmp(v->name, "joyaxis_turn")) + { + if (joyaxis_count > 6) return false; + // we're currently setting the new defaults, don't interfere + else if (joyaxis_count == 6) return true; + + if (!stricmp(valstr, "X-Axis")) joyaxis_count++; + else joyaxis_default = false; + } + if (!stricmp(v->name, "joyaxis_move")) + { + if (joyaxis_count > 6) return false; + else if (joyaxis_count == 6) return true; + + if (!stricmp(valstr, "Y-Axis")) joyaxis_count++; + else joyaxis_default = false; + } + if (!stricmp(v->name, "joyaxis_side")) + { + if (joyaxis_count > 6) return false; + else if (joyaxis_count == 6) return true; + + if (!stricmp(valstr, "Z-Axis")) joyaxis_count++; + else joyaxis_default = false; + } + if (!stricmp(v->name, "joyaxis_look")) + { + if (joyaxis_count > 6) return false; + else if (joyaxis_count == 6) return true; + + if (!stricmp(valstr, "None")) joyaxis_count++; + else joyaxis_default = false; + } + if (!stricmp(v->name, "joyaxis_fire") + || !stricmp(v->name, "joyaxis_firenormal")) + { + if (joyaxis_count > 6) return false; + else if (joyaxis_count == 6) return true; + + if (!stricmp(valstr, "None")) joyaxis_count++; + else joyaxis_default = false; + } + // reset all axis settings to defaults + if (joyaxis_count == 6) + { + COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis[0].name, cv_turnaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis[0].name, cv_moveaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis[0].name, cv_sideaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis[0].name, cv_lookaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis[0].name, cv_fireaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis[0].name, cv_firenaxis[0].defaultvalue)); + joyaxis_count++; + return false; + } + } + + if (joyaxis2_default) + { + if (!stricmp(v->name, "joyaxis2_turn")) + { + if (joyaxis2_count > 6) return false; + // we're currently setting the new defaults, don't interfere + else if (joyaxis2_count == 6) return true; + + if (!stricmp(valstr, "X-Axis")) joyaxis2_count++; + else joyaxis2_default = false; + } + if (!stricmp(v->name, "joyaxis2_move")) + { + if (joyaxis2_count > 6) return false; + else if (joyaxis2_count == 6) return true; + + if (!stricmp(valstr, "Y-Axis")) joyaxis2_count++; + else joyaxis2_default = false; + } + if (!stricmp(v->name, "joyaxis2_side")) + { + if (joyaxis2_count > 6) return false; + else if (joyaxis2_count == 6) return true; + + if (!stricmp(valstr, "Z-Axis")) joyaxis2_count++; + else joyaxis2_default = false; + } + if (!stricmp(v->name, "joyaxis2_look")) + { + if (joyaxis2_count > 6) return false; + else if (joyaxis2_count == 6) return true; + + if (!stricmp(valstr, "None")) joyaxis2_count++; + else joyaxis2_default = false; + } + if (!stricmp(v->name, "joyaxis2_fire") + || !stricmp(v->name, "joyaxis2_firenormal")) + { + if (joyaxis2_count > 6) return false; + else if (joyaxis2_count == 6) return true; + + if (!stricmp(valstr, "None")) joyaxis2_count++; + else joyaxis2_default = false; + } + + // reset all axis settings to defaults + if (joyaxis2_count == 6) + { + COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis[1].name, cv_turnaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis[1].name, cv_moveaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis[1].name, cv_sideaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis[1].name, cv_lookaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis[1].name, cv_fireaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis[1].name, cv_firenaxis[1].defaultvalue)); + joyaxis2_count++; + return false; + } + } + + // we haven't reached our counts yet, or we're not default + return true; +} + #ifndef OLD_GAMEPAD_AXES static boolean CV_ConvertOldJoyAxisVars(consvar_t *v, const char *valstr) { @@ -2233,6 +2359,40 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) if (!(v->flags & CV_SAVE)) return true; + if (GETMAJOREXECVERSION(cv_execversion.value) < 26) // 26 = 2.1.21 + { + // MOUSE SETTINGS + // alwaysfreelook split between first and third person (chasefreelook) + // mousemove was on by default, which invalidates the current approach + if (!stricmp(v->name, "alwaysmlook") + || !stricmp(v->name, "alwaysmlook2") + || !stricmp(v->name, "mousemove") + || !stricmp(v->name, "mousemove2")) + return false; + + // mousesens was changed from 35 to 20 due to oversensitivity + if ((!stricmp(v->name, "mousesens") + || !stricmp(v->name, "mousesens2") + || !stricmp(v->name, "mouseysens") + || !stricmp(v->name, "mouseysens2")) + && atoi(valstr) == 35) + return false; + + // GAMEPAD DEFAULTS + // use_gamepad was changed from 0 to 1 to automatically use a gamepad if available +#if defined(HAVE_SDL) || defined(_WINDOWS) + if ((!stricmp(v->name, "use_joystick") + || !stricmp(v->name, "use_joystick2")) + && atoi(valstr) == 0) + return false; +#endif + + // axis defaults were changed to be friendly to 360 controllers + // if ALL axis settings are defaults, then change them to new values + if (!CV_FilterJoyAxisVars(v, valstr)) + return false; + } + #ifndef OLD_GAMEPAD_AXES if (GETMAJOREXECVERSION(cv_execversion.value) <= 51 && GETMINOREXECVERSION(cv_execversion.value) < 1) { diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f721aa75a..07dba1777 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -815,6 +815,14 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_moveaxis[1]); CV_RegisterVar(&cv_lookaxis[0]); CV_RegisterVar(&cv_lookaxis[1]); + CV_RegisterVar(&cv_jumpaxis[0]); + CV_RegisterVar(&cv_jumpaxis[1]); + CV_RegisterVar(&cv_spinaxis[0]); + CV_RegisterVar(&cv_spinaxis[1]); + CV_RegisterVar(&cv_fireaxis[0]); + CV_RegisterVar(&cv_fireaxis[1]); + CV_RegisterVar(&cv_firenaxis[0]); + CV_RegisterVar(&cv_firenaxis[1]); CV_RegisterVar(&cv_deadzone[0]); CV_RegisterVar(&cv_deadzone[1]); CV_RegisterVar(&cv_digitaldeadzone[0]); @@ -834,6 +842,7 @@ void D_RegisterClientCommands(void) #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) CV_RegisterVar(&cv_mouse2opt); #endif + CV_RegisterVar(&cv_controlperkey); CV_RegisterVar(&cv_usemouse); CV_RegisterVar(&cv_usemouse2); diff --git a/src/deh_tables.c b/src/deh_tables.c index 90c3047a8..a2cc9732d 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -5648,6 +5648,11 @@ struct int_const_s const INT_CONST[] = { {"JA_MOVE",JA_MOVE}, {"JA_LOOK",JA_LOOK}, {"JA_STRAFE",JA_STRAFE}, + {"JA_DIGITAL",JA_DIGITAL}, + {"JA_JUMP",JA_JUMP}, + {"JA_SPIN",JA_SPIN}, + {"JA_FIRE",JA_FIRE}, + {"JA_FIRENORMAL",JA_FIRENORMAL}, {"JOYAXISRANGE",OLDJOYAXISRANGE}, // Game controls diff --git a/src/g_game.c b/src/g_game.c index dc12c2bab..b4a127a73 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -297,13 +297,17 @@ CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, #ifndef OLD_GAMEPAD_AXES #define MOVEAXIS_DEFAULT "Left Stick Y" #define SIDEAXIS_DEFAULT "Left Stick X" -#define LOOKAXIS_DEFAULT "Right Stick Y" +#define LOOKAXIS_DEFAULT "Right Stick Y-" #define TURNAXIS_DEFAULT "Right Stick X" +#define FIREAXIS_DEFAULT "Right Trigger" +#define FIRENAXIS_DEFAULT "Left Trigger" #else #define MOVEAXIS_DEFAULT "Y-Axis" #define SIDEAXIS_DEFAULT "X-Axis" #define LOOKAXIS_DEFAULT "Y-Rudder-" #define TURNAXIS_DEFAULT "X-Rudder" +#define FIREAXIS_DEFAULT "Z-Rudder" +#define FIRENAXIS_DEFAULT "Z-Axis" #endif // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. @@ -435,6 +439,22 @@ consvar_t cv_turnaxis[2] = { CVAR_INIT ("joyaxis_turn", TURNAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), CVAR_INIT ("joyaxis2_turn", TURNAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) }; +consvar_t cv_jumpaxis[2] = { + CVAR_INIT ("joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_spinaxis[2] = { + CVAR_INIT ("joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_fireaxis[2] = { + CVAR_INIT ("joyaxis_fire", FIREAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_fire", FIREAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; +consvar_t cv_firenaxis[2] = { + CVAR_INIT ("joyaxis_firenormal", FIRENAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), + CVAR_INIT ("joyaxis2_firenormal", FIRENAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) +}; consvar_t cv_deadzone[2] = { CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), CVAR_INIT ("joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) @@ -870,7 +890,7 @@ static gamepad_axis_e ConvertXboxControllerAxes(int type) } #endif -static INT16 GetJoystickAxisValue(INT32 axisval) +static INT16 GetJoystickAxisValue(UINT8 which, joyaxis_e axissel, INT32 axisval) { boolean flp = false; @@ -905,7 +925,14 @@ static INT16 GetJoystickAxisValue(INT32 axisval) } INT16 retaxis = G_GetGamepadAxisValue(0, gp_axis); - + + if (gamepads[which].digital && axissel >= JA_DIGITAL) + { + const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which) / 2; + if (-jdeadzone < retaxis && retaxis < jdeadzone) + return 0; + } + // flip it around if (flp) { @@ -938,11 +965,23 @@ INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel) case JA_STRAFE: axisval = cv_sideaxis[which].value; break; + case JA_JUMP: + axisval = cv_jumpaxis[which].value; + break; + case JA_SPIN: + axisval = cv_spinaxis[which].value; + break; + case JA_FIRE: + axisval = cv_fireaxis[which].value; + break; + case JA_FIRENORMAL: + axisval = cv_firenaxis[which].value; + break; default: return 0; } - value = GetJoystickAxisValue(axisval); + value = GetJoystickAxisValue(which, axissel, axisval); if (axissel == JA_LOOK) { // Look is inverted because +Y goes _down_ in gamepads. @@ -1053,7 +1092,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) boolean forcestrafe = false; boolean forcefullinput = false; - INT32 tspeed, forward, side, strafeaxis, moveaxis, turnaxis, lookaxis, i; + INT32 tspeed, forward, side, axis, strafeaxis, moveaxis, turnaxis, lookaxis, i; joystickvector2_t movejoystickvector, lookjoystickvector; @@ -1339,11 +1378,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } // fire with any button/key - if (G_PlayerInputDown(forplayer, GC_FIRE)) + axis = G_JoyAxis(forplayer, JA_FIRE); + if (G_PlayerInputDown(forplayer, GC_FIRE) || (usegamepad && axis > 0)) cmd->buttons |= BT_ATTACK; // fire normal with any button/key - if (G_PlayerInputDown(forplayer, GC_FIRENORMAL)) + axis = G_JoyAxis(forplayer, JA_FIRENORMAL); + if (G_PlayerInputDown(forplayer, GC_FIRENORMAL) || (usegamepad && axis > 0)) cmd->buttons |= BT_FIRENORMAL; if (G_PlayerInputDown(forplayer, GC_TOSSFLAG)) @@ -1358,7 +1399,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->buttons |= BT_CUSTOM3; // spin with any button/key - if (G_PlayerInputDown(forplayer, GC_SPIN)) + axis = G_JoyAxis(forplayer, JA_SPIN); + if (G_PlayerInputDown(forplayer, GC_SPIN) || (usegamepad && axis > 0)) cmd->buttons |= BT_SPIN; // Centerview can be a toggle in simple mode! @@ -1474,7 +1516,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) resetdown[forplayer] = false; // jump button - if (G_PlayerInputDown(forplayer, GC_JUMP)) + axis = G_JoyAxis(forplayer, JA_JUMP); + if (G_PlayerInputDown(forplayer, GC_JUMP) || (usegamepad && axis > 0)) cmd->buttons |= BT_JUMP; // player aiming shit, ahhhh... diff --git a/src/g_game.h b/src/g_game.h index 8d6e84b12..e798176af 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -71,6 +71,7 @@ extern consvar_t cv_autobrake, cv_autobrake2; extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest; extern consvar_t cv_sideaxis[2], cv_turnaxis[2], cv_moveaxis[2], cv_lookaxis[2], + cv_jumpaxis[2], cv_spinaxis[2], cv_fireaxis[2], cv_firenaxis[2], cv_deadzone[2], cv_digitaldeadzone[2]; extern CV_PossibleValue_t joyaxis_cons_t[]; @@ -97,6 +98,13 @@ typedef enum JA_MOVE, JA_LOOK, JA_STRAFE, + + JA_DIGITAL, // axes henceforth use digital deadzone + + JA_JUMP = JA_DIGITAL, + JA_SPIN, + JA_FIRE, + JA_FIRENORMAL, } joyaxis_e; INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel); diff --git a/src/g_input.c b/src/g_input.c index 87f4d5173..6b0da7e41 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -24,12 +24,14 @@ #define MAXMOUSESENSITIVITY 100 // sensitivity steps static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY, "MAX"}, {0, NULL}}; +static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}}; // mouse values are used once consvar_t cv_mousesens = CVAR_INIT ("mousesens", "20", CV_SAVE, mousesens_cons_t, NULL); consvar_t cv_mousesens2 = CVAR_INIT ("mousesens2", "20", CV_SAVE, mousesens_cons_t, NULL); consvar_t cv_mouseysens = CVAR_INIT ("mouseysens", "20", CV_SAVE, mousesens_cons_t, NULL); consvar_t cv_mouseysens2 = CVAR_INIT ("mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL); +consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL); mouse_t mouse; mouse_t mouse2; @@ -960,6 +962,9 @@ static keyname_t keynames[] = DEF_GAMEPAD_AXIS(TRIGGERLEFT, "left trigger"), DEF_GAMEPAD_AXIS(TRIGGERRIGHT, "right trigger"), +#undef DEF_GAMEPAD_NAME +#undef DEF_GAMEPAD_AXIS + {KEY_DBLMOUSE1+0, "dblmouse1"}, {KEY_DBLMOUSE1+1, "dblmouse2"}, {KEY_DBLMOUSE1+2, "dblmouse3"}, @@ -978,50 +983,7 @@ static keyname_t keynames[] = {KEY_DBL2MOUSE1+7, "dblsec_mouse8"} }; -static keyname_t oldjoynames[] = -{ - DEF_GAMEPAD_NAME(A, "joy1"), - DEF_GAMEPAD_NAME(B, "joy2"), - DEF_GAMEPAD_NAME(X, "joy3"), - DEF_GAMEPAD_NAME(Y, "joy4"), - - DEF_GAMEPAD_NAME(BACK, "joy7"), - DEF_GAMEPAD_NAME(START, "joy8"), - DEF_GAMEPAD_NAME(LEFTSTICK, "joy9"), - DEF_GAMEPAD_NAME(RIGHTSTICK, "joy10"), - - DEF_GAMEPAD_NAME(LEFTSHOULDER, "joy5"), - DEF_GAMEPAD_NAME(RIGHTSHOULDER, "joy6"), - - DEF_GAMEPAD_NAME(DPAD_UP, "hatup"), - DEF_GAMEPAD_NAME(DPAD_DOWN, "hatdown"), - DEF_GAMEPAD_NAME(DPAD_LEFT, "hatleft"), - DEF_GAMEPAD_NAME(DPAD_RIGHT, "hatright"), - - DEF_GAMEPAD_NAME(A, "sec_joy1"), - DEF_GAMEPAD_NAME(B, "sec_joy2"), - DEF_GAMEPAD_NAME(X, "sec_joy3"), - DEF_GAMEPAD_NAME(Y, "sec_joy4"), - - DEF_GAMEPAD_NAME(BACK, "sec_joy7"), - DEF_GAMEPAD_NAME(START, "sec_joy8"), - DEF_GAMEPAD_NAME(LEFTSTICK, "sec_joy9"), - DEF_GAMEPAD_NAME(RIGHTSTICK, "sec_joy10"), - - DEF_GAMEPAD_NAME(LEFTSHOULDER, "sec_joy5"), - DEF_GAMEPAD_NAME(RIGHTSHOULDER, "sec_joy6"), - - DEF_GAMEPAD_NAME(DPAD_UP, "sec_hatup"), - DEF_GAMEPAD_NAME(DPAD_DOWN, "sec_hatdown"), - DEF_GAMEPAD_NAME(DPAD_LEFT, "sec_hatleft"), - DEF_GAMEPAD_NAME(DPAD_RIGHT, "sec_hatright"), -}; - -#undef DEF_GAMEPAD_NAME -#undef DEF_GAMEPAD_AXIS - #define NUMKEYNAMES (sizeof(keynames) / sizeof(keyname_t)) -#define NUMOLDJOYNAMES (sizeof(oldjoynames) / sizeof(keyname_t)) static keyname_t displaykeynames[] = { @@ -1310,10 +1272,6 @@ INT32 G_KeyNameToNum(const char *keystr) if (!stricmp(keynames[j].name, keystr)) return keynames[j].keynum; - for (j = 0; j < NUMOLDJOYNAMES; j++) - if (!stricmp(oldjoynames[j].name, keystr)) - return oldjoynames[j].keynum; - return 0; } @@ -1419,17 +1377,15 @@ void G_DefineDefaultControls(void) gamecontroldefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B gamecontroldefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y gamecontroldefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick - gamecontroldefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // R1 - gamecontroldefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(LEFTSHOULDER); // L1 + gamecontroldefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick + gamecontroldefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB + gamecontroldefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB gamecontroldefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back gamecontroldefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start - gamecontroldefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up - gamecontroldefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left - gamecontroldefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right - gamecontroldefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down - gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick - gamecontroldefault[i][GC_FIRE ][1] = GAMEPAD_AXIS(TRIGGERRIGHT); // R2 - gamecontroldefault[i][GC_FIRENORMAL ][1] = GAMEPAD_AXIS(TRIGGERLEFT); // L2 + gamecontroldefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up + gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down + gamecontroldefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left + gamecontroldefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right // Second player only has gamepad defaults gamecontrolbisdefault[i][GC_JUMP ][1] = GAMEPAD_KEY(A); // A @@ -1437,17 +1393,15 @@ void G_DefineDefaultControls(void) gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick - gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // R1 - gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(LEFTSHOULDER); // L1 + gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick + gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB + gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back //gamecontrolbisdefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start - gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up - gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left - gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right - //gamecontrolbisdefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down - gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick - gamecontrolbisdefault[i][GC_FIRE ][1] = GAMEPAD_AXIS(TRIGGERRIGHT); // R2 - gamecontrolbisdefault[i][GC_FIRENORMAL ][1] = GAMEPAD_AXIS(TRIGGERLEFT); // L2 + gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up + gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down + gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left + //gamecontrolbisdefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right } } @@ -1517,40 +1471,42 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis } } -INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify, UINT8 player) +INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify) { INT32 result = GC_NULL; - INT32 i; - for (i = 0; i < NUM_GAMECONTROLS; i++) + if (cv_controlperkey.value == 1) { - if (gamecontrol[i][0] == keynum && player != 2) + INT32 i; + for (i = 0; i < NUM_GAMECONTROLS; i++) { - result = i; - if (modify) gamecontrol[i][0] = KEY_NULL; + if (gamecontrol[i][0] == keynum) + { + result = i; + if (modify) gamecontrol[i][0] = KEY_NULL; + } + if (gamecontrol[i][1] == keynum) + { + result = i; + if (modify) gamecontrol[i][1] = KEY_NULL; + } + if (gamecontrolbis[i][0] == keynum) + { + result = i; + if (modify) gamecontrolbis[i][0] = KEY_NULL; + } + if (gamecontrolbis[i][1] == keynum) + { + result = i; + if (modify) gamecontrolbis[i][1] = KEY_NULL; + } + if (result && !modify) + return result; } - if (gamecontrol[i][1] == keynum && player != 2) - { - result = i; - if (modify) gamecontrol[i][1] = KEY_NULL; - } - if (gamecontrolbis[i][0] == keynum && player != 1) - { - result = i; - if (modify) gamecontrolbis[i][0] = KEY_NULL; - } - if (gamecontrolbis[i][1] == keynum && player != 1) - { - result = i; - if (modify) gamecontrolbis[i][1] = KEY_NULL; - } - if (result && !modify) - return result; } - return result; } -static INT32 G_FilterSpecialKeys(INT32 keyidx, INT32 *keynum1, INT32 *keynum2) +static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT32 *keynum1, INT32 *keynum2, boolean *nestedoverride) { // Special case: ignore KEY_PAUSE because it's hardcoded if (keyidx == 0 && *keynum1 == KEY_PAUSE) @@ -1566,6 +1522,99 @@ static INT32 G_FilterSpecialKeys(INT32 keyidx, INT32 *keynum1, INT32 *keynum2) else if (keyidx == 1 && *keynum2 == KEY_PAUSE) return -1; // skip setting control + if (GETMAJOREXECVERSION(cv_execversion.value) < 27 && ( // v2.1.22 + numctrl == GC_WEAPONNEXT || numctrl == GC_WEAPONPREV || numctrl == GC_TOSSFLAG || + numctrl == GC_SPIN || numctrl == GC_CAMRESET || numctrl == GC_JUMP || + numctrl == GC_PAUSE || numctrl == GC_SYSTEMMENU || numctrl == GC_CAMTOGGLE || + numctrl == GC_SCREENSHOT || numctrl == GC_TALKKEY || numctrl == GC_SCORES || + numctrl == GC_CENTERVIEW + )) + { + INT32 keynum = 0, existingctrl = 0; + INT32 defaultkey; + boolean defaultoverride = false; + + // get the default gamecontrol + if (player == 0 && numctrl == GC_SYSTEMMENU) + defaultkey = gamecontrol[numctrl][0]; + else + defaultkey = (player == 1 ? gamecontrolbis[numctrl][0] : gamecontrol[numctrl][1]); + + // Assign joypad button defaults if there is an open slot. + // At this point, gamecontrol/bis should have the default controls + // (unless LOADCONFIG is being run) + // + // If the player runs SETCONTROL in-game, this block should not be reached + // because EXECVERSION is locked onto the latest version. + if (keyidx == 0 && !*keynum1) + { + if (*keynum2) // push keynum2 down; this is an edge case + { + *keynum1 = *keynum2; + *keynum2 = 0; + keynum = *keynum1; + } + else + { + keynum = defaultkey; + defaultoverride = true; + } + } + else if (keyidx == 1 && (!*keynum2 || (!*keynum1 && *keynum2))) // last one is the same edge case as above + { + keynum = defaultkey; + defaultoverride = true; + } + else // default to the specified keynum + keynum = (keyidx == 1 ? *keynum2 : *keynum1); + + // Did our last call override keynum2? + if (*nestedoverride) + { + defaultoverride = true; + *nestedoverride = false; + } + + // Fill keynum2 with the default control + if (keyidx == 0 && !*keynum2) + { + *keynum2 = defaultkey; + // Tell the next call that this is an override + *nestedoverride = true; + + // if keynum2 already matches keynum1, we probably recursed + // so unset it + if (*keynum1 == *keynum2) + { + *keynum2 = 0; + *nestedoverride = false; + } + } + + // check if the key is being used somewhere else before passing it + // pass it through if it's the same numctrl. This is an edge case -- when using + // LOADCONFIG, gamecontrol is not reset with default. + // + // Also, only check if we're actually overriding, to preserve behavior where + // config'd keys overwrite default keys. + if (defaultoverride) + existingctrl = G_CheckDoubleUsage(keynum, false); + + if (keynum && (!existingctrl || existingctrl == numctrl)) + return keynum; + else if (keyidx == 0 && *keynum2) + { + // try it again and push down keynum2 + *keynum1 = *keynum2; + *keynum2 = 0; + return G_FilterKeyByVersion(numctrl, keyidx, player, keynum1, keynum2, nestedoverride); + // recursion *should* be safe because we only assign keynum2 to a joy default + // and then clear it if we find that keynum1 already has the joy default. + } + else + return 0; + } + // All's good, so pass the keynum as-is if (keyidx == 1) return *keynum2; @@ -1579,6 +1628,7 @@ static void setcontrol(INT32 (*gc)[2]) const char *namectrl; INT32 keynum, keynum1, keynum2; INT32 player = ((void*)gc == (void*)&gamecontrolbis ? 1 : 0); + boolean nestedoverride = false; // Update me for 2.3 namectrl = (stricmp(COM_Argv(1), "use")) ? COM_Argv(1) : "spin"; @@ -1593,20 +1643,20 @@ static void setcontrol(INT32 (*gc)[2]) } keynum1 = G_KeyNameToNum(COM_Argv(2)); keynum2 = G_KeyNameToNum(COM_Argv(3)); - keynum = G_FilterSpecialKeys(0, &keynum1, &keynum2); + keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride); if (keynum >= 0) { - (void)G_CheckDoubleUsage(keynum, true, player+1); + (void)G_CheckDoubleUsage(keynum, true); // if keynum was rejected, try it again with keynum2 if (!keynum && keynum2) { keynum1 = keynum2; // push down keynum2 keynum2 = 0; - keynum = G_FilterSpecialKeys(0, &keynum1, &keynum2); + keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride); if (keynum >= 0) - (void)G_CheckDoubleUsage(keynum, true, player+1); + (void)G_CheckDoubleUsage(keynum, true); } } @@ -1615,7 +1665,7 @@ static void setcontrol(INT32 (*gc)[2]) if (keynum2) { - keynum = G_FilterSpecialKeys(1, &keynum1, &keynum2); + keynum = G_FilterKeyByVersion(numctrl, 1, player, &keynum1, &keynum2, &nestedoverride); if (keynum >= 0) { if (keynum != gc[numctrl][0]) diff --git a/src/g_input.h b/src/g_input.h index bb50d3516..fe623034a 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -188,7 +188,6 @@ typedef enum } key_input_e; #define GAMEPAD_KEY(key) (KEY_GAMEPAD + GAMEPAD_BUTTON_##key) -#define GAMEPAD_AXIS(key) (KEY_AXES + GAMEPAD_AXIS_##key) typedef enum { @@ -249,6 +248,7 @@ typedef enum // mouse values are used once extern consvar_t cv_mousesens, cv_mouseysens; extern consvar_t cv_mousesens2, cv_mouseysens2; +extern consvar_t cv_controlperkey; typedef struct { @@ -354,7 +354,7 @@ void G_DefineDefaultControls(void); INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen); void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen); void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis)[2]); -INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify, UINT8 player); +INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify); // sets the members of a mouse_t given position deltas void G_SetMouseDeltas(INT32 dx, INT32 dy, UINT8 ssplayer); diff --git a/src/m_menu.c b/src/m_menu.c index 1e7e1a761..1d5bd65f3 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1070,9 +1070,9 @@ static menuitem_t OP_MainMenu[] = { {IT_SUBMENU | IT_STRING, NULL, "Player 1 Controls...", &OP_P1ControlsDef, 10}, {IT_SUBMENU | IT_STRING, NULL, "Player 2 Controls...", &OP_P2ControlsDef, 20}, + {IT_CVAR | IT_STRING, NULL, "Controls per key", &cv_controlperkey, 30}, - {IT_CALL | IT_STRING, NULL, "Video Options...", M_VideoOptions, 40}, - + {IT_CALL | IT_STRING, NULL, "Video Options...", M_VideoOptions, 50}, {IT_SUBMENU | IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 60}, {IT_CALL | IT_STRING, NULL, "Server Options...", M_ServerOptions, 80}, @@ -1080,16 +1080,6 @@ static menuitem_t OP_MainMenu[] = {IT_SUBMENU | IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 100}, }; -enum -{ - opplayer1, - opplayer2, - opvideo, - opsound, - opserver, - opdata -}; - static menuitem_t OP_P1ControlsMenu[] = { {IT_CALL | IT_STRING, NULL, "Control Configuration...", M_Setup1PControlsMenu, 10}, @@ -1177,11 +1167,15 @@ static menuitem_t OP_Gamepad1Menu[] = {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis[0] , 40}, {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis[0] , 50}, {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis[0] , 60}, + {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis[0] , 70}, + {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis[0] , 80}, + {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis[0] , 90}, + {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis[0] ,100}, - {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook, 80}, - {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook, 90}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[0], 100}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[0], 110}, + {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook, 120}, + {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook, 130}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[0], 140}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[0], 150}, }; static menuitem_t OP_Gamepad2Menu[] = @@ -1191,11 +1185,15 @@ static menuitem_t OP_Gamepad2Menu[] = {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis[1] , 40}, {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis[1] , 50}, {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis[1] , 60}, + {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis[1] , 70}, + {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis[1] , 80}, + {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis[1] , 90}, + {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis[1] ,100}, - {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook2,80}, - {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 90}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[1],100}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[1],110}, + {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook2,120}, + {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[1],140}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[1],150}, }; static menuitem_t OP_GamepadSetMenu[MAX_CONNECTED_GAMEPADS + 1]; @@ -1543,13 +1541,6 @@ static menuitem_t OP_DataOptionsMenu[] = {IT_STRING | IT_SUBMENU, NULL, "\x85" "Erase Data...", &OP_EraseDataDef, 40}, }; -enum -{ - opdataaddon, - opdatascreenshot, - opdataerase -}; - static menuitem_t OP_ScreenshotOptionsMenu[] = { {IT_HEADER, NULL, "General", NULL, 0}, @@ -3475,7 +3466,7 @@ boolean M_Responder(event_t *ev) if (!useEventHandler) { UINT16 type = currentMenu->menuitems[itemOn].alphaKey; - if (type == MM_YESNO && !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER || ch == KEY_DEL || ch == KEY_BACKSPACE)) + if (type == MM_YESNO && !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER || ch == KEY_DEL)) return true; if (routine) routine(ch); @@ -7046,10 +7037,10 @@ static void M_Options(INT32 choice) (void)choice; // if the player is not admin or server, disable server options - OP_MainMenu[opserver].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); + OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // if the player is playing _at all_, disable the erase data options - OP_DataOptionsMenu[opdataerase].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); + OP_DataOptionsMenu[2].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); OP_MainDef.prevMenu = currentMenu; M_SetupNextMenu(&OP_MainDef); @@ -9124,7 +9115,7 @@ static void M_LoadGame(INT32 choice) if (tutorialmap && cv_tutorialprompt.value) { - M_StartMessage("Do you want to \x82play a brief Tutorial\x80?\n\nWe highly recommend this because \nthe controls are slightly different \nfrom other games.\n\nPress the\x82 Y\x80 key or the\x83 A button\x80 to go\nPress the\x82 N\x80 key or the\x83 X button\x80 to skip\n", + M_StartMessage("Do you want to \x82play a brief Tutorial\x80?\n\nWe highly recommend this because \nthe controls are slightly different \nfrom other games.\n\nPress the\x82 Y\x80 key or the\x83 A button\x80 to go\nPress the\x82 N\x80 key or the\x83 Y button\x80 to skip\n", M_FirstTimeResponse, MM_YESNO); return; } @@ -13032,7 +13023,7 @@ static void M_ChangeControlResponse(event_t *ev) setupcontrols[control][1] = KEY_NULL; //replace key 1,clear key2 } - G_CheckDoubleUsage(ch, true, setupcontrols_secondaryplayer ? 2 : 1); + G_CheckDoubleUsage(ch, true); setupcontrols[control][found] = ch; } From 5827a19b89dd6a7bfd264faa90924c7348eb3cf0 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:24:52 -0600 Subject: [PATCH 70/89] Revert "Merge branch 'controller-fixes' into 'next'" This reverts commit 71736f358f6b08962bb1e6af40c6886b905e2eb1, reversing changes made to 4e3f78784a99c50d31e18f7b7947e3a435cebbea. --- src/g_input.c | 4 ++-- src/m_menu.c | 14 +++----------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/g_input.c b/src/g_input.c index 6b0da7e41..465db0316 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -543,9 +543,9 @@ INT32 G_RemapGamepadEvent(event_t *event, INT32 *type) const INT16 value = G_GetGamepadAxisValue(event->which, event->key); if (value < -jdeadzone || value > jdeadzone) - *type = ev_keydown; - else *type = ev_keyup; + else + *type = ev_keydown; if (value < -jdeadzone) return KEY_INV_AXES + event->key; diff --git a/src/m_menu.c b/src/m_menu.c index 1d5bd65f3..51d13df98 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3224,7 +3224,6 @@ boolean M_Responder(event_t *ev) static INT32 pjoyx = 0, pjoyy = 0; static INT32 pmousex = 0, pmousey = 0; static INT32 lastx = 0, lasty = 0; - boolean shouldswallowevent = false; void (*routine)(INT32 choice); // for some casting problem if (dedicated || (demoplayback && titledemo) @@ -3240,18 +3239,11 @@ boolean M_Responder(event_t *ev) boolean useEventHandler = false; - if (menuactive && ev->type == ev_gamepad_axis && ev->which == 0) - { - // ALWAYS swallow gamepad axis events, to prevent trickling down to game input - // this applies even if the axis event does not get remapped - shouldswallowevent = true; - } - if (noFurtherInput) { // Ignore input after enter/escape/other buttons // (but still allow shift keyup so caps doesn't get stuck) - return shouldswallowevent; + return false; } else if (menuactive) { @@ -3376,7 +3368,7 @@ boolean M_Responder(event_t *ev) } if (!useEventHandler && ch == -1) - return shouldswallowevent; + return false; else if (ch == gamecontrol[GC_SYSTEMMENU][0] || ch == gamecontrol[GC_SYSTEMMENU][1]) // allow remappable ESC key ch = KEY_ESCAPE; @@ -3609,7 +3601,7 @@ boolean M_Responder(event_t *ev) //currentMenu->lastOn = itemOn; //if (currentMenu->prevMenu) // M_SetupNextMenu(currentMenu->prevMenu); - return shouldswallowevent; + return false; default: CON_Responder(ev); From f9c9e0829a665071bcf5e8b0e98e6d98041dfa2f Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:25:53 -0600 Subject: [PATCH 71/89] Revert "sdl: I can't believe it's more version-guards" This reverts commit fdb6f2aff93e91cfc1dac24804c912d8b0dc31e6. --- src/sdl/i_gamepad.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index ecde251fb..b2b50e43d 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -103,7 +103,6 @@ INT32 I_NumGamepads(void) #define USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 0x4001 #define USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA 0x4002 -#if SDL_VERSION_ATLEAST(2,0,6) static boolean IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id) { if (vendor_id == USB_VENDOR_MICROSOFT) { @@ -142,7 +141,6 @@ static boolean IsJoystickXboxSeriesXS(Uint16 vendor_id, Uint16 product_id) return false; } -#endif // Opens a controller device static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) @@ -887,7 +885,6 @@ boolean I_SetGamepadSmallMotorFreq(UINT8 which, fixed_t freq) void I_SetGamepadRumblePaused(UINT8 which, boolean pause) { -#if SDL_VERSION_ATLEAST(2,0,9) if (!I_RumbleSupported() || which >= NUM_GAMEPADS) return; @@ -918,11 +915,6 @@ void I_SetGamepadRumblePaused(UINT8 which, boolean pause) } controller->info->rumble.paused = pause; -#else - (void)which; - (void)pause; - return; -#endif } boolean I_GetGamepadRumbleSupported(UINT8 which) From 40a5580e653a32df51f0d8744bce2b367eebf5ea Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:25:56 -0600 Subject: [PATCH 72/89] Revert "sdl: Yet more SDL rumble version-guards" This reverts commit df28ffd72eb168bf392db7cf88a4c58218fe2923. --- src/sdl/i_gamepad.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index b2b50e43d..109d37d4a 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -66,11 +66,7 @@ void I_InitGamepads(void) if (!InitGamepadSubsystems()) return; -#if SDL_VERSION_ATLEAST(2,0,9) rumble_supported = !M_CheckParm("-norumble"); -#else - rumble_supported = false; -#endif for (UINT8 i = 0; i < NUM_GAMEPADS; i++) controllers[i].info = &gamepads[i]; @@ -731,20 +727,14 @@ boolean I_RumbleSupported(void) static boolean Controller_Rumble(ControllerInfo *c) { -#if SDL_VERSION_ATLEAST(2,0,9) if (SDL_GameControllerRumble(c->dev, c->rumble.large_magnitude, c->rumble.small_magnitude, 0) == -1) return false; return true; -#else - (void)c; - return false; -#endif } void I_ToggleControllerRumble(boolean unpause) { -#if SDL_VERSION_ATLEAST(2,0,9) if (!I_RumbleSupported() || rumble_paused == !unpause) return; @@ -764,10 +754,6 @@ void I_ToggleControllerRumble(boolean unpause) controller->rumble.expiration = controller->rumble.time_left = 0; } } -#else - (void)unpause; - return; -#endif } void I_UpdateControllers(void) From 2810a46095ec6568a5ff4c38f70d6e36b4feb7b1 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:25:59 -0600 Subject: [PATCH 73/89] Revert "sdl: Version-guard rumble and extended buttons" This reverts commit f5f224136ba6d8db1319eb091c9b9877173d8e55. --- src/sdl/i_gamepad.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index 109d37d4a..28f9681be 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -231,7 +231,6 @@ static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) controller->info->type = GAMEPAD_TYPE_UNKNOWN; #endif // SDL_VERSION_ATLEAST(2,0,12) -#if SDL_VERSION_ATLEAST(2,0,6) // Check the device vendor and product to find out what controller this actually is Uint16 vendor = SDL_JoystickGetDeviceVendor(devindex); Uint16 product = SDL_JoystickGetDeviceProduct(devindex); @@ -240,17 +239,13 @@ static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) controller->info->type = GAMEPAD_TYPE_XBOX_SERIES_XS; else if (IsJoystickXboxOneElite(vendor, product)) controller->info->type = GAMEPAD_TYPE_XBOX_ELITE; -#endif CONS_Debug(DBG_GAMELOGIC, M_GetText(" Type: %s\n"), G_GamepadTypeToString(controller->info->type)); -#if SDL_VERSION_ATLEAST(2,0,12) // Change the ring LEDs on Xbox 360 controllers // FIXME: Doesn't seem to work? SDL_GameControllerSetPlayerIndex(controller->dev, which); -#endif -#if SDL_VERSION_ATLEAST(2,0,18) // Check if rumble is supported if (SDL_GameControllerHasRumble(controller->dev) == SDL_TRUE) { @@ -260,12 +255,8 @@ static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) else { controller->info->rumble.supported = false; - CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: No\n")); + CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: No\n"));; } -#else - controller->info->rumble.supported = true; - CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: Maybe\n")); -#endif // SDL_VERSION_ATLEAST(2,0,18) if (!controller->info->connected) { @@ -609,14 +600,12 @@ void I_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint32 type) GAMEPAD_BUTTON_CASE(DPAD_DOWN); GAMEPAD_BUTTON_CASE(DPAD_LEFT); GAMEPAD_BUTTON_CASE(DPAD_RIGHT); -#if SDL_VERSION_ATLEAST(2,0,14) GAMEPAD_BUTTON_CASE(MISC1); GAMEPAD_BUTTON_CASE(PADDLE1); GAMEPAD_BUTTON_CASE(PADDLE2); GAMEPAD_BUTTON_CASE(PADDLE3); GAMEPAD_BUTTON_CASE(PADDLE4); GAMEPAD_BUTTON_CASE(TOUCHPAD); -#endif default: return; } @@ -674,10 +663,8 @@ static void Controller_StopRumble(UINT8 num) gamepad->rumble.data.small_magnitude = 0; gamepad->rumble.data.duration = 0; -#if SDL_VERSION_ATLEAST(2,0,9) if (gamepad->rumble.supported) SDL_GameControllerRumble(controller->dev, 0, 0, 0); -#endif } static void Controller_Close(UINT8 num) From bbf6e8ccce0bde7698363594f1941f78049bac4a Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:26:02 -0600 Subject: [PATCH 74/89] Revert "sdl: SDL version-guard controller type and hidapi" This reverts commit ec58b1504d28513ae4a972b4d2a32bfed01dec4a. --- src/sdl/i_gamepad.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index 28f9681be..dbafc1f63 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -39,10 +39,8 @@ static boolean InitGamepadSubsystems(void) { if (M_CheckParm("-noxinput")) SDL_SetHintWithPriority(SDL_HINT_XINPUT_ENABLED, "0", SDL_HINT_OVERRIDE); -#if SDL_VERSION_ATLEAST(2,0,9) if (M_CheckParm("-nohidapi")) SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI, "0", SDL_HINT_OVERRIDE); -#endif if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == 0) { @@ -201,7 +199,6 @@ static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) CONS_Debug(DBG_GAMELOGIC, M_GetText("Controller %d: %s\n"), which, SDL_GameControllerName(controller->dev)); -#if SDL_VERSION_ATLEAST(2,0,12) #define GAMEPAD_TYPE_CASE(ctrl) \ case SDL_CONTROLLER_TYPE_##ctrl: \ controller->info->type = GAMEPAD_TYPE_##ctrl; \ @@ -214,22 +211,15 @@ static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) GAMEPAD_TYPE_CASE(XBOXONE); GAMEPAD_TYPE_CASE(PS3); GAMEPAD_TYPE_CASE(PS4); -#if SDL_VERSION_ATLEAST(2,0,14) GAMEPAD_TYPE_CASE(PS5); -#endif GAMEPAD_TYPE_CASE(NINTENDO_SWITCH_PRO); -#if SDL_VERSION_ATLEAST(2,0,16) GAMEPAD_TYPE_CASE(GOOGLE_STADIA); GAMEPAD_TYPE_CASE(AMAZON_LUNA); -#endif GAMEPAD_TYPE_CASE(VIRTUAL); default: break; } + #undef GAMEPAD_BUTTON_CASE -#else - // Under older versions of SDL, we aren't provided controller type information. - controller->info->type = GAMEPAD_TYPE_UNKNOWN; -#endif // SDL_VERSION_ATLEAST(2,0,12) // Check the device vendor and product to find out what controller this actually is Uint16 vendor = SDL_JoystickGetDeviceVendor(devindex); From 7ad54af89fca8a62faa8edd1aba00e64a0df7ee1 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:26:17 -0600 Subject: [PATCH 75/89] Revert "Fix axis inversion and camera look inversion" This reverts commit 6428d6fc612e31753d424e2efcfcc6afc6d2e93a. --- src/g_game.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index b4a127a73..b406b8f94 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -935,12 +935,7 @@ static INT16 GetJoystickAxisValue(UINT8 which, joyaxis_e axissel, INT32 axisval) // flip it around if (flp) - { - if (retaxis == -32768) - retaxis = 32767; - else - retaxis = -retaxis; - } + retaxis = -retaxis; return retaxis; } @@ -948,7 +943,6 @@ static INT16 GetJoystickAxisValue(UINT8 which, joyaxis_e axissel, INT32 axisval) INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel) { INT32 axisval; - INT32 value; // find what axis to get switch (axissel) @@ -981,16 +975,7 @@ INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel) return 0; } - value = GetJoystickAxisValue(which, axissel, axisval); - if (axissel == JA_LOOK) - { - // Look is inverted because +Y goes _down_ in gamepads. - if (value == -32768) - value = 32767; - else - value = -value; - } - return value; + return GetJoystickAxisValue(which, axissel, axisval); } static INT16 GetAnalogInput(UINT8 which, gamecontrols_e gc) From 6cc48a6e893999f3b35a2305d2539b081fe07aa2 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:26:21 -0600 Subject: [PATCH 76/89] Revert "Merge branch 'gamepad-refactor-lua-rumble' into 'next'" This reverts commit 7d8e8e20b7c1b40231ac47c812225cd63358b12a, reversing changes made to 7f1cafcd3d0a0738bf106b8edcaf9a23f9cefa19. --- src/lua_baselib.c | 82 +---------------------------------------------- src/lua_script.h | 1 - 2 files changed, 1 insertion(+), 82 deletions(-) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index c94e9e91e..6212a77f0 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -15,7 +15,6 @@ #include "p_local.h" #include "p_setup.h" // So we can have P_SetupLevelSky #include "p_slopes.h" // P_GetSlopeZAt -#include "p_haptic.h" #include "z_zone.h" #include "r_main.h" #include "r_draw.h" @@ -1734,78 +1733,6 @@ static int lib_pPlayerShouldUseSpinHeight(lua_State *L) return 1; } -// P_HAPTIC -/////////// -#define GET_OPTIONAL_PLAYER(arg) \ - player_t *player = NULL; \ - if (!lua_isnoneornil(L, arg)) { \ - player = *((player_t **)luaL_checkudata(L, arg, META_PLAYER)); \ - if (!player) \ - return LUA_ErrInvalid(L, "player_t"); \ - } - -static int lib_pDoRumble(lua_State *L) -{ - GET_OPTIONAL_PLAYER(1); - fixed_t large_magnitude = luaL_checkfixed(L, 2); - fixed_t small_magnitude = luaL_optfixed(L, 3, large_magnitude); - tic_t duration = luaL_optinteger(L, 4, 0); - -#define CHECK_MAGNITUDE(which) \ - if (which##_magnitude < 0 || which##_magnitude > FRACUNIT) \ - return luaL_error(L, va(#which " motor frequency %f out of range (minimum is 0.0, maximum is 1.0)", \ - FixedToFloat(which##_magnitude))) - - CHECK_MAGNITUDE(large); - CHECK_MAGNITUDE(small); - -#undef CHECK_MAGNITUDE - - lua_pushboolean(L, P_DoRumble(player, large_magnitude, small_magnitude, duration)); - return 1; -} - -static int lib_pPauseRumble(lua_State *L) -{ - GET_OPTIONAL_PLAYER(1); - P_PauseRumble(player); - return 0; -} - -static int lib_pUnpauseRumble(lua_State *L) -{ - GET_OPTIONAL_PLAYER(1); - P_UnpauseRumble(player); - return 0; -} - -static int lib_pIsRumbleEnabled(lua_State *L) -{ - GET_OPTIONAL_PLAYER(1); - if (player && P_IsLocalPlayer(player)) - lua_pushboolean(L, P_IsRumbleEnabled(player)); - else - lua_pushnil(L); - return 1; -} - -static int lib_pIsRumblePaused(lua_State *L) -{ - GET_OPTIONAL_PLAYER(1); - if (player && P_IsLocalPlayer(player)) - lua_pushboolean(L, P_IsRumblePaused(player)); - else - lua_pushnil(L); - return 1; -} - -static int lib_pStopRumble(lua_State *L) -{ - GET_OPTIONAL_PLAYER(1); - P_StopRumble(player); - return 0; -} - // P_MAP /////////// @@ -3442,6 +3369,7 @@ static int lib_sResumeMusic(lua_State *L) // G_GAME //////////// +// Copypasted from lib_cvRegisterVar :] static int lib_gAddGametype(lua_State *L) { const char *k; @@ -4163,14 +4091,6 @@ static luaL_Reg lib[] = { {"P_PlayerCanEnterSpinGaps",lib_pPlayerCanEnterSpinGaps}, {"P_PlayerShouldUseSpinHeight",lib_pPlayerShouldUseSpinHeight}, - // p_haptic - {"P_DoRumble",lib_pDoRumble}, - {"P_PauseRumble",lib_pPauseRumble}, - {"P_UnpauseRumble",lib_pUnpauseRumble}, - {"P_IsRumbleEnabled",lib_pIsRumbleEnabled}, - {"P_IsRumblePaused",lib_pIsRumblePaused}, - {"P_StopRumble",lib_pStopRumble}, - // p_map {"P_CheckPosition",lib_pCheckPosition}, {"P_TryMove",lib_pTryMove}, diff --git a/src/lua_script.h b/src/lua_script.h index 1c98a32f3..080427039 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -36,7 +36,6 @@ // angle_t casting // TODO deal with signedness #define luaL_checkangle(L, i) ((angle_t)luaL_checkinteger(L, i)) -#define luaL_optangle(L, i, o) ((angle_t)luaL_optinteger(L, i, o)) #define lua_pushangle(L, a) lua_pushinteger(L, a) #ifdef _DEBUG From 366ee4ad92db728602917ce851106f8feee1d860 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:26:24 -0600 Subject: [PATCH 77/89] Revert "Merge branch 'gamepad-refactor-lua-features' into 'next'" This reverts commit 7f1cafcd3d0a0738bf106b8edcaf9a23f9cefa19, reversing changes made to 696e2ab9097a674716e579eb848024b0b8539205. --- src/g_game.c | 11 -- src/g_game.h | 3 - src/lua_baselib.c | 1 - src/lua_hook.h | 6 - src/lua_hooklib.c | 23 --- src/lua_inputlib.c | 372 -------------------------------------------- src/lua_libs.h | 1 - src/lua_script.c | 18 --- src/lua_script.h | 1 - src/sdl/i_gamepad.c | 8 +- 10 files changed, 2 insertions(+), 442 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index b406b8f94..ce41ccaf6 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1808,15 +1808,8 @@ INT32 G_GetGamepadDeviceIndex(INT32 player) return cv_usegamepad[player].value; } -void G_OnGamepadConnect(UINT8 which) -{ - LUA_HookGamepadEvent(which, HOOK(GamepadAdded)); -} - void G_OnGamepadDisconnect(UINT8 which) { - LUA_HookGamepadEvent(which, HOOK(GamepadRemoved)); - if (!cv_gamepad_autopause.value) return; @@ -2340,10 +2333,6 @@ boolean G_LuaResponder(event_t *ev) cancelled = LUA_HookKey(ev, HOOK(KeyUp)); LUA_InvalidateUserdata(ev); } - else if (ev->type == ev_gamepad_down) - cancelled = LUA_HookGamepadButton(ev, HOOK(GamepadButtonDown)); - else if (ev->type == ev_gamepad_up) - cancelled = LUA_HookGamepadButton(ev, HOOK(GamepadButtonUp)); return cancelled; } diff --git a/src/g_game.h b/src/g_game.h index e798176af..6c24054a0 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -133,9 +133,6 @@ INT32 G_GetGamepadDeviceIndex(INT32 player); // returns a player's gamepad index INT16 G_GetGamepadForPlayer(player_t *player); -// called when a player's gamepad is connected -void G_OnGamepadConnect(UINT8 which); - // called when a player's gamepad is disconnected void G_OnGamepadDisconnect(UINT8 which); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 6212a77f0..29adb478a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -220,7 +220,6 @@ static const struct { {META_LUABANKS, "luabanks[]"}, {META_KEYEVENT, "keyevent_t"}, - {META_GAMEPAD, "gamepad_t"}, {META_MOUSE, "mouse_t"}, {NULL, NULL} }; diff --git a/src/lua_hook.h b/src/lua_hook.h index ff6470d76..4fa3a1a17 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -74,10 +74,6 @@ automatically. X (PlayerCanEnterSpinGaps),\ X (KeyDown),\ X (KeyUp),\ - X (GamepadButtonDown),\ - X (GamepadButtonUp),\ - X (GamepadAdded),\ - X (GamepadRemoved),\ #define STRING_HOOK_LIST(X) \ X (BotAI),/* B_BuildTailsTiccmd by skin name */\ @@ -129,8 +125,6 @@ void LUA_HookBool(boolean value, int hook); int LUA_HookPlayer(player_t *, int hook); int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); int LUA_HookKey(event_t *event, int hook); // Hooks for key events -int LUA_HookGamepadButton(event_t *event, int hook); -void LUA_HookGamepadEvent(UINT8 which, int hook); void LUA_HookThinkFrame(void); int LUA_HookMobjLineCollide(mobj_t *, line_t *); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index ef8426d7a..c4083c9ad 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -14,7 +14,6 @@ #include "doomstat.h" #include "p_mobj.h" #include "g_game.h" -#include "g_input.h" #include "r_skins.h" #include "b_bot.h" #include "z_zone.h" @@ -647,28 +646,6 @@ int LUA_HookKey(event_t *event, int hook_type) return hook.status; } -int LUA_HookGamepadButton(event_t *event, int hook_type) -{ - Hook_State hook; - if (prepare_hook(&hook, false, hook_type)) - { - LUA_PushUserdata(gL, &gamepads[event->which], META_GAMEPAD); - lua_pushstring(gL, gamepad_button_names[event->key]); - call_hooks(&hook, 1, res_true); - } - return hook.status; -} - -void LUA_HookGamepadEvent(UINT8 which, int hook_type) -{ - Hook_State hook; - if (prepare_hook(&hook, 0, hook_type)) - { - LUA_PushUserdata(gL, &gamepads[which], META_GAMEPAD); - call_hooks(&hook, 0, res_none); - } -} - void LUA_HookHUD(int hook_type, huddrawlist_h list) { const hook_t * map = &hudHookIds[hook_type]; diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index e4962a0fb..88efc3490 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -129,21 +129,6 @@ static int lib_getCursorPosition(lua_State *L) return 2; } -static int lib_getPlayerGamepad(lua_State *L) -{ - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - if (!player) - return LUA_ErrInvalid(L, "player_t"); - - INT16 which = G_GetGamepadForPlayer(player); - if (which >= 0) - LUA_PushUserdata(L, &gamepads[which], META_GAMEPAD); - else - lua_pushnil(L); - - return 1; -} - static luaL_Reg lib[] = { {"gameControlDown", lib_gameControlDown}, {"gameControl2Down", lib_gameControl2Down}, @@ -158,7 +143,6 @@ static luaL_Reg lib[] = { {"getMouseGrab", lib_getMouseGrab}, {"setMouseGrab", lib_setMouseGrab}, {"getCursorPosition", lib_getCursorPosition}, - {"getPlayerGamepad", lib_getPlayerGamepad}, {NULL, NULL} }; @@ -214,341 +198,6 @@ static int keyevent_get(lua_State *L) return 1; } -///////////// -// GAMEPAD // -///////////// - -enum gamepad_leftright_e { - gamepad_opt_left, - gamepad_opt_right -}; - -static const char *const gamepad_leftright_opt[] = { - "left", - "right", - NULL}; - -// Buttons -static int gamepad_isButtonDown(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - gamepad_button_e button = luaL_checkoption(L, 2, NULL, gamepad_button_names); - lua_pushboolean(L, gamepad->buttons[button] == 1); - return 1; -} - -// Axes -static int gamepad_getAxis(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - gamepad_axis_e axis = luaL_checkoption(L, 2, NULL, gamepad_axis_names); - boolean applyDeadzone = luaL_opt(L, luaL_checkboolean, 3, true); - lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, axis, applyDeadzone)); - return 1; -} - -// Sticks -static int gamepad_getStick(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - enum gamepad_leftright_e stick = luaL_checkoption(L, 2, NULL, gamepad_leftright_opt); - boolean applyDeadzone = luaL_opt(L, luaL_checkboolean, 3, true); - - switch (stick) - { - case gamepad_opt_left: - lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_LEFTX, applyDeadzone)); - lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_LEFTY, applyDeadzone)); - break; - case gamepad_opt_right: - lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_RIGHTX, applyDeadzone)); - lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, GAMEPAD_AXIS_RIGHTY, applyDeadzone)); - break; - } - - return 2; -} - -// Triggers -static int gamepad_getTrigger(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - enum gamepad_leftright_e stick = luaL_checkoption(L, 2, NULL, gamepad_leftright_opt); - boolean applyDeadzone = luaL_opt(L, luaL_checkboolean, 3, true); - gamepad_axis_e axis = 0; - - switch (stick) - { - case gamepad_opt_left: - axis = GAMEPAD_AXIS_TRIGGERLEFT; - break; - case gamepad_opt_right: - axis = GAMEPAD_AXIS_TRIGGERRIGHT; - break; - } - - lua_pushfixed(L, G_GetAdjustedGamepadAxis(gamepad->num, axis, applyDeadzone)); - return 1; -} - -// Button and axis names -static int gamepad_getButtonName(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - gamepad_button_e button = luaL_checkoption(L, 2, NULL, gamepad_button_names); - lua_pushstring(L, G_GetGamepadButtonString(gamepad->type, button, GAMEPAD_STRING_DEFAULT)); - return 1; -} - -static int gamepad_getAxisName(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - gamepad_axis_e axis = luaL_checkoption(L, 2, NULL, gamepad_axis_names); - lua_pushstring(L, G_GetGamepadAxisString(gamepad->type, axis, GAMEPAD_STRING_DEFAULT, false)); - return 1; -} - -static int gamepad_getTriggerName(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - enum gamepad_leftright_e stick = luaL_checkoption(L, 2, NULL, gamepad_leftright_opt); - gamepad_axis_e axis = 0; - - switch (stick) - { - case gamepad_opt_left: - axis = GAMEPAD_AXIS_TRIGGERLEFT; - break; - case gamepad_opt_right: - axis = GAMEPAD_AXIS_TRIGGERRIGHT; - break; - } - - lua_pushstring(L, G_GetGamepadAxisString(gamepad->type, axis, GAMEPAD_STRING_DEFAULT, false)); - return 1; -} - -// Rumble -static int gamepad_doRumble(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - fixed_t large_magnitude = luaL_checkfixed(L, 2); - fixed_t small_magnitude = luaL_optfixed(L, 3, large_magnitude); - tic_t duration = luaL_optinteger(L, 4, 0); - -#define CHECK_MAGNITUDE(which) \ - if (which##_magnitude < 0 || which##_magnitude > FRACUNIT) \ - return luaL_error(L, va(#which " motor frequency %f out of range (minimum is 0.0, maximum is 1.0)", \ - FixedToFloat(which##_magnitude))) - - CHECK_MAGNITUDE(large); - CHECK_MAGNITUDE(small); - -#undef CHECK_MAGNITUDE - - lua_pushboolean(L, G_RumbleGamepad(gamepad->num, large_magnitude, small_magnitude, duration)); - return 1; -} - -static int gamepad_stopRumble(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - G_StopGamepadRumble(gamepad->num); - return 0; -} - -// Accessing gamepad userdata -enum gamepad_opt_e { - gamepad_opt_connected, - gamepad_opt_type, - gamepad_opt_isXbox, - gamepad_opt_isPlayStation, - gamepad_opt_isNintendoSwitch, - gamepad_opt_isJoyCon, - gamepad_opt_hasRumble, - gamepad_opt_isRumbling, - gamepad_opt_isRumblePaused, - gamepad_opt_largeMotorFrequency, - gamepad_opt_smallMotorFrequency, - gamepad_opt_isButtonDown, - gamepad_opt_getAxis, - gamepad_opt_getStick, - gamepad_opt_getTrigger, - gamepad_opt_getButtonName, - gamepad_opt_getAxisName, - gamepad_opt_getTriggerName, - gamepad_opt_rumble, - gamepad_opt_stopRumble -}; - -static const char *const gamepad_opt[] = { - "connected", - "type", - "isXbox", - "isPlayStation", - "isNintendoSwitch", - "isJoyCon", - "hasRumble", - "isRumbling", - "isRumblePaused", - "largeMotorFrequency", - "smallMotorFrequency", - "isButtonDown", - "getAxis", - "getStick", - "getTrigger", - "getButtonName", - "getAxisName", - "getTriggerName", - "rumble", - "stopRumble", - NULL}; - -static int (*gamepad_fn_list[9])(lua_State *L) = { - gamepad_isButtonDown, - gamepad_getAxis, - gamepad_getStick, - gamepad_getTrigger, - gamepad_getButtonName, - gamepad_getAxisName, - gamepad_getTriggerName, - gamepad_doRumble, - gamepad_stopRumble -}; - -static int gamepad_get(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - enum gamepad_opt_e field = luaL_checkoption(L, 2, NULL, gamepad_opt); - - switch (field) - { - case gamepad_opt_connected: - lua_pushboolean(L, gamepad->connected); - break; - case gamepad_opt_type: - lua_pushstring(L, G_GamepadTypeToString(gamepad->type)); - break; - case gamepad_opt_isXbox: - lua_pushboolean(L, G_GamepadTypeIsXbox(gamepad->type)); - break; - case gamepad_opt_isPlayStation: - lua_pushboolean(L, G_GamepadTypeIsPlayStation(gamepad->type)); - break; - case gamepad_opt_isNintendoSwitch: - lua_pushboolean(L, G_GamepadTypeIsNintendoSwitch(gamepad->type)); - break; - case gamepad_opt_isJoyCon: - // No, this does not include the grip. - lua_pushboolean(L, G_GamepadTypeIsJoyCon(gamepad->type)); - break; - case gamepad_opt_hasRumble: - lua_pushboolean(L, G_RumbleSupported(gamepad->num)); - break; - case gamepad_opt_isRumbling: - lua_pushboolean(L, gamepad->rumble.active); - break; - case gamepad_opt_isRumblePaused: - lua_pushboolean(L, G_GetGamepadRumblePaused(gamepad->num)); - break; - case gamepad_opt_largeMotorFrequency: - lua_pushfixed(L, G_GetLargeMotorFreq(gamepad->num)); - break; - case gamepad_opt_smallMotorFrequency: - lua_pushfixed(L, G_GetSmallMotorFreq(gamepad->num)); - break; - case gamepad_opt_isButtonDown: - case gamepad_opt_getAxis: - case gamepad_opt_getStick: - case gamepad_opt_getTrigger: - case gamepad_opt_getButtonName: - case gamepad_opt_getAxisName: - case gamepad_opt_getTriggerName: - case gamepad_opt_rumble: - case gamepad_opt_stopRumble: - lua_pushcfunction(L, gamepad_fn_list[field - gamepad_opt_isButtonDown]); - break; - } - return 1; -} - -static int gamepad_set(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - enum gamepad_opt_e field = luaL_checkoption(L, 2, NULL, gamepad_opt); - - switch (field) - { - case gamepad_opt_isRumblePaused: - G_SetGamepadRumblePaused(gamepad->num, luaL_checkboolean(L, 3)); - break; - case gamepad_opt_largeMotorFrequency: - G_SetLargeMotorFreq(gamepad->num, luaL_checkfixed(L, 3)); - break; - case gamepad_opt_smallMotorFrequency: - G_SetSmallMotorFreq(gamepad->num, luaL_checkfixed(L, 3)); - break; - default: - return luaL_error(L, LUA_QL("gamepad") " field " LUA_QS " should not be set directly.", gamepad_opt[field]); - } - return 1; -} - -static int gamepad_num(lua_State *L) -{ - gamepad_t *gamepad = *((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)); - lua_pushinteger(L, gamepad->num + 1); - return 1; -} - -static int lib_iterateGamepads(lua_State *L) -{ - INT32 i = -1; - if (lua_gettop(L) < 2) - { - lua_pushcfunction(L, lib_iterateGamepads); - return 1; - } - lua_settop(L, 2); - lua_remove(L, 1); // State is unused - if (!lua_isnil(L, 1)) - i = (INT32)(*((gamepad_t **)luaL_checkudata(L, 1, META_GAMEPAD)) - gamepads); - for (i++; i < NUM_GAMEPADS; i++) - { - if (!gamepads[i].connected) - continue; - LUA_PushUserdata(L, &gamepads[i], META_GAMEPAD); - return 1; - } - return 0; -} - -static int lib_getGamepad(lua_State *L) -{ - if (lua_type(L, 2) == LUA_TNUMBER) - { - lua_Integer i = luaL_checkinteger(L, 2); - if (i < 1 || i > NUM_GAMEPADS) - return luaL_error(L, "gamepads[] index %d out of range (1 - %d)", i, NUM_GAMEPADS); - LUA_PushUserdata(L, &gamepads[i - 1], META_GAMEPAD); - return 1; - } - - if (fastcmp(luaL_checkstring(L, 2), "iterate")) - { - lua_pushcfunction(L, lib_iterateGamepads); - return 1; - } - - return 0; -} - -static int lib_lenGamepad(lua_State *L) -{ - lua_pushinteger(L, NUM_GAMEPADS); - return 1; -} - /////////// // MOUSE // /////////// @@ -609,27 +258,6 @@ int LUA_InputLib(lua_State *L) lua_setfield(L, -2, "__index"); lua_pop(L, 1); - luaL_newmetatable(L, META_GAMEPAD); - lua_pushcfunction(L, gamepad_get); - lua_setfield(L, -2, "__index"); - - lua_pushcfunction(L, gamepad_set); - lua_setfield(L, -2, "__newindex"); - - lua_pushcfunction(L, gamepad_num); - lua_setfield(L, -2, "__len"); - lua_pop(L, 1); - - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getGamepad); - lua_setfield(L, -2, "__index"); - - lua_pushcfunction(L, lib_lenGamepad); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "gamepads"); - luaL_newmetatable(L, META_MOUSE); lua_pushcfunction(L, mouse_get); lua_setfield(L, -2, "__index"); diff --git a/src/lua_libs.h b/src/lua_libs.h index 68e976234..b4a891edb 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -91,7 +91,6 @@ extern boolean mousegrabbedbylua; #define META_LUABANKS "LUABANKS[]*" #define META_KEYEVENT "KEYEVENT_T*" -#define META_GAMEPAD "GAMEPAD_T*" #define META_MOUSE "MOUSE_T*" boolean luaL_checkboolean(lua_State *L, int narg); diff --git a/src/lua_script.c b/src/lua_script.c index 8d8fb295c..f166fb4e6 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -949,7 +949,6 @@ enum ARCH_MAPHEADER, ARCH_SKINCOLOR, ARCH_MOUSE, - ARCH_GAMEPAD, ARCH_TEND=0xFF, }; @@ -977,7 +976,6 @@ static const struct { {META_SLOPE, ARCH_SLOPE}, {META_MAPHEADER, ARCH_MAPHEADER}, {META_SKINCOLOR, ARCH_SKINCOLOR}, - {META_GAMEPAD, ARCH_GAMEPAD}, {META_MOUSE, ARCH_MOUSE}, {NULL, ARCH_NULL} }; @@ -1293,13 +1291,6 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) WRITEUINT16(save_p, info - skincolors); break; } - case ARCH_GAMEPAD: - { - gamepad_t *gamepad = *((gamepad_t **)lua_touserdata(gL, myindex)); - WRITEUINT8(save_p, ARCH_GAMEPAD); - WRITEUINT8(save_p, gamepad->num); - break; - } case ARCH_MOUSE: { mouse_t *m = *((mouse_t **)lua_touserdata(gL, myindex)); @@ -1550,15 +1541,6 @@ static UINT8 UnArchiveValue(int TABLESINDEX) case ARCH_SKINCOLOR: LUA_PushUserdata(gL, &skincolors[READUINT16(save_p)], META_SKINCOLOR); break; - case ARCH_GAMEPAD: - { - UINT8 which = READUINT8(save_p); - if (which < NUM_GAMEPADS) - LUA_PushUserdata(gL, &gamepads[which], META_GAMEPAD); - else // Wait, what? - lua_pushnil(gL); - break; - } case ARCH_MOUSE: LUA_PushUserdata(gL, READUINT16(save_p) == 1 ? &mouse : &mouse2, META_MOUSE); break; diff --git a/src/lua_script.h b/src/lua_script.h index 080427039..e586b04a8 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -30,7 +30,6 @@ // TODO add some distinction between fixed numbers and integer numbers // for at least the purpose of printing and maybe math. #define luaL_checkfixed(L, i) luaL_checkinteger(L, i) -#define luaL_optfixed(L, i, o) luaL_optinteger(L, i, o) #define lua_pushfixed(L, f) lua_pushinteger(L, f) // angle_t casting diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c index dbafc1f63..69f03d662 100644 --- a/src/sdl/i_gamepad.c +++ b/src/sdl/i_gamepad.c @@ -233,7 +233,7 @@ static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) CONS_Debug(DBG_GAMELOGIC, M_GetText(" Type: %s\n"), G_GamepadTypeToString(controller->info->type)); // Change the ring LEDs on Xbox 360 controllers - // FIXME: Doesn't seem to work? + // TODO: Doesn't seem to work? SDL_GameControllerSetPlayerIndex(controller->dev, which); // Check if rumble is supported @@ -248,11 +248,7 @@ static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: No\n"));; } - if (!controller->info->connected) - { - controller->info->connected = true; - G_OnGamepadConnect(which); - } + controller->info->connected = true; } return controller->started; From e5dea805fa6c66b151b9510703795bc0070a1ff1 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Thu, 26 Jan 2023 19:26:28 -0600 Subject: [PATCH 78/89] Revert "Merge branch 'gamepad-refactor' into next" This reverts commit 696e2ab9097a674716e579eb848024b0b8539205, reversing changes made to d98d59494f493307b3cae8d741e9a3969c2af2eb. --- src/Sourcefile | 1 - src/command.c | 81 +-- src/console.c | 9 +- src/d_clisrv.c | 89 ++- src/d_event.h | 14 +- src/d_main.c | 8 - src/d_netcmd.c | 140 ++--- src/d_netcmd.h | 12 +- src/deh_tables.c | 10 +- src/doomdef.h | 5 +- src/f_finale.c | 76 ++- src/g_demo.c | 6 +- src/g_game.c | 705 ++++++++++------------- src/g_game.h | 24 +- src/g_input.c | 1331 ++++++++++++------------------------------- src/g_input.h | 216 +------ src/hu_stuff.c | 22 +- src/i_gamepad.h | 58 -- src/i_joy.h | 58 ++ src/i_system.h | 94 ++- src/lua_inputlib.c | 9 +- src/m_cheat.c | 32 +- src/m_menu.c | 756 ++++++++++-------------- src/m_menu.h | 28 +- src/p_haptic.c | 115 ---- src/p_haptic.h | 27 - src/p_inter.c | 70 ++- src/p_local.h | 15 + src/p_user.c | 6 +- src/sdl/Sourcefile | 1 - src/sdl/i_gamepad.c | 914 ----------------------------- src/sdl/i_system.c | 767 ++++++++++++++++++++++++- src/sdl/i_video.c | 448 +++++++++++++-- src/sdl/sdlmain.h | 72 ++- 34 files changed, 2663 insertions(+), 3556 deletions(-) delete mode 100644 src/i_gamepad.h create mode 100644 src/i_joy.h delete mode 100644 src/p_haptic.c delete mode 100644 src/p_haptic.h delete mode 100644 src/sdl/i_gamepad.c diff --git a/src/Sourcefile b/src/Sourcefile index 9de90eee4..de90bb609 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -51,7 +51,6 @@ p_spec.c p_telept.c p_tick.c p_user.c -p_haptic.c p_slopes.c tables.c r_bsp.c diff --git a/src/command.c b/src/command.c index 201cceeee..b22f03d1a 100644 --- a/src/command.c +++ b/src/command.c @@ -77,6 +77,7 @@ CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}}; // Filter consvars by EXECVERSION // First implementation is 26 (2.1.21), so earlier configs default at 25 (2.1.20) +// Also set CV_HIDEN during runtime, after config is loaded static boolean execversion_enabled = false; consvar_t cv_execversion = CVAR_INIT ("execversion","25",CV_CALL,CV_Unsigned, CV_EnforceExecVersion); @@ -2233,12 +2234,12 @@ static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) // reset all axis settings to defaults if (joyaxis_count == 6) { - COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis[0].name, cv_turnaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis[0].name, cv_moveaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis[0].name, cv_sideaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis[0].name, cv_lookaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis[0].name, cv_fireaxis[0].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis[0].name, cv_firenaxis[0].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis.name, cv_turnaxis.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis.name, cv_moveaxis.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis.name, cv_sideaxis.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis.name, cv_lookaxis.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis.name, cv_fireaxis.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis.name, cv_firenaxis.defaultvalue)); joyaxis_count++; return false; } @@ -2292,12 +2293,12 @@ static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) // reset all axis settings to defaults if (joyaxis2_count == 6) { - COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis[1].name, cv_turnaxis[1].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis[1].name, cv_moveaxis[1].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis[1].name, cv_sideaxis[1].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis[1].name, cv_lookaxis[1].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis[1].name, cv_fireaxis[1].defaultvalue)); - COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis[1].name, cv_firenaxis[1].defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis2.name, cv_turnaxis2.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis2.name, cv_moveaxis2.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis2.name, cv_sideaxis2.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis2.name, cv_lookaxis2.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis2.name, cv_fireaxis2.defaultvalue)); + COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis2.name, cv_firenaxis2.defaultvalue)); joyaxis2_count++; return false; } @@ -2307,49 +2308,6 @@ static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr) return true; } -#ifndef OLD_GAMEPAD_AXES -static boolean CV_ConvertOldJoyAxisVars(consvar_t *v, const char *valstr) -{ - static struct { - const char *old; - const char *new; - } axis_names[] = { - {"X-Axis", "Left Stick X"}, - {"Y-Axis", "Left Stick Y"}, - {"X-Axis-", "Left Stick X-"}, - {"Y-Axis-", "Left Stick Y-"}, - {"X-Rudder", "Right Stick X"}, - {"Y-Rudder", "Right Stick Y"}, - {"X-Rudder-", "Right Stick X-"}, - {"Y-Rudder-", "Right Stick Y-"}, - {"Z-Axis", "Left Trigger"}, - {"Z-Rudder", "Right Trigger"}, - {"Z-Axis-", "Left Trigger"}, - {"Z-Rudder-", "Right Trigger"}, - {NULL, NULL} - }; - - if (v->PossibleValue != joyaxis_cons_t) - return true; - - for (unsigned i = 0;; i++) - { - if (axis_names[i].old == NULL) - { - CV_SetCVar(v, "None", false); - return false; - } - else if (!stricmp(valstr, axis_names[i].old)) - { - CV_SetCVar(v, axis_names[i].new, false); - return false; - } - } - - return true; -} -#endif - static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) { // True means allow the CV change, False means block it @@ -2378,8 +2336,8 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) && atoi(valstr) == 35) return false; - // GAMEPAD DEFAULTS - // use_gamepad was changed from 0 to 1 to automatically use a gamepad if available + // JOYSTICK DEFAULTS + // use_joystick was changed from 0 to 1 to automatically use a joystick if available #if defined(HAVE_SDL) || defined(_WINDOWS) if ((!stricmp(v->name, "use_joystick") || !stricmp(v->name, "use_joystick2")) @@ -2392,15 +2350,6 @@ static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr) if (!CV_FilterJoyAxisVars(v, valstr)) return false; } - -#ifndef OLD_GAMEPAD_AXES - if (GETMAJOREXECVERSION(cv_execversion.value) <= 51 && GETMINOREXECVERSION(cv_execversion.value) < 1) - { - if (!CV_ConvertOldJoyAxisVars(v, valstr)) - return false; - } -#endif - return true; } diff --git a/src/console.c b/src/console.c index 7cad4aee5..40fb43121 100644 --- a/src/console.c +++ b/src/console.c @@ -918,8 +918,7 @@ boolean CON_Responder(event_t *ev) static INT32 alias_skips; const char *cmd = NULL; - INT32 key = ev->key; - boolean key_is_console = (key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]); + INT32 key; if (chat_on) return false; @@ -927,18 +926,20 @@ boolean CON_Responder(event_t *ev) // let go keyup events, don't eat them if (ev->type != ev_keydown && ev->type != ev_console) { - if (key_is_console) + if (ev->key == gamecontrol[GC_CONSOLE][0] || ev->key == gamecontrol[GC_CONSOLE][1]) consdown = false; return false; } + key = ev->key; + // check for console toggle key if (ev->type != ev_console) { if (modeattacking || metalrecording || marathonmode) return false; - if (key_is_console) + if (key == gamecontrol[GC_CONSOLE][0] || key == gamecontrol[GC_CONSOLE][1]) { if (consdown) // ignore repeat return true; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 3091f3344..1ff053e5c 100755 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -25,8 +25,7 @@ #include "st_stuff.h" #include "hu_stuff.h" #include "keys.h" -#include "g_input.h" -#include "i_gamepad.h" +#include "g_input.h" // JOY1 #include "m_menu.h" #include "console.h" #include "d_netfil.h" @@ -34,7 +33,6 @@ #include "p_saveg.h" #include "z_zone.h" #include "p_local.h" -#include "p_haptic.h" #include "m_misc.h" #include "am_map.h" #include "m_random.h" @@ -51,7 +49,7 @@ #include "m_perfstats.h" // aaaaaa -#include "i_gamepad.h" +#include "i_joy.h" #ifndef NONET // cl loading screen @@ -655,6 +653,22 @@ static UINT8 Snake_GetOppositeDir(UINT8 dir) return 12 + 5 - dir; } +event_t *snakejoyevents[MAXEVENTS]; +UINT16 joyeventcount = 0; + +// I'm screaming the hack is clean - ashi +static boolean Snake_Joy_Grabber(event_t *ev) +{ + if (ev->type == ev_joystick && ev->key == 0) + { + snakejoyevents[joyeventcount] = ev; + joyeventcount++; + return true; + } + else + return false; +} + static void Snake_FindFreeSlot(UINT8 *freex, UINT8 *freey, UINT8 headx, UINT8 heady) { UINT8 x, y; @@ -681,17 +695,19 @@ static void Snake_Handle(void) UINT8 x, y; UINT8 oldx, oldy; UINT16 i; + UINT16 j; UINT16 joystate = 0; + static INT32 pjoyx = 0, pjoyy = 0; // Handle retry - if (snake->gameover && (G_PlayerInputDown(0, GC_JUMP) || gamekeydown[KEY_ENTER])) + if (snake->gameover && (PLAYER1INPUTDOWN(GC_JUMP) || gamekeydown[KEY_ENTER])) { Snake_Initialise(); snake->pausepressed = true; // Avoid accidental pause on respawn } // Handle pause - if (G_PlayerInputDown(0, GC_PAUSE) || gamekeydown[KEY_ENTER]) + if (PLAYER1INPUTDOWN(GC_PAUSE) || gamekeydown[KEY_ENTER]) { if (!snake->pausepressed) snake->paused = !snake->paused; @@ -710,23 +726,58 @@ static void Snake_Handle(void) oldx = snake->snakex[1]; oldy = snake->snakey[1]; + // process the input events in here dear lord + for (j = 0; j < joyeventcount; j++) + { + event_t *ev = snakejoyevents[j]; + const INT32 jdeadzone = (JOYAXISRANGE * cv_digitaldeadzone.value) / FRACUNIT; + if (ev->y != INT32_MAX) + { + if (Joystick.bGamepadStyle || abs(ev->y) > jdeadzone) + { + if (ev->y < 0 && pjoyy >= 0) + joystate = 1; + else if (ev->y > 0 && pjoyy <= 0) + joystate = 2; + pjoyy = ev->y; + } + else + pjoyy = 0; + } + + if (ev->x != INT32_MAX) + { + if (Joystick.bGamepadStyle || abs(ev->x) > jdeadzone) + { + if (ev->x < 0 && pjoyx >= 0) + joystate = 3; + else if (ev->x > 0 && pjoyx <= 0) + joystate = 4; + pjoyx = ev->x; + } + else + pjoyx = 0; + } + } + joyeventcount = 0; + // Update direction - if (G_PlayerInputDown(0, GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) + if (PLAYER1INPUTDOWN(GC_STRAFELEFT) || gamekeydown[KEY_LEFTARROW] || joystate == 3) { if (snake->snakelength < 2 || x <= oldx) snake->snakedir[0] = 1; } - else if (G_PlayerInputDown(0, GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) + else if (PLAYER1INPUTDOWN(GC_STRAFERIGHT) || gamekeydown[KEY_RIGHTARROW] || joystate == 4) { if (snake->snakelength < 2 || x >= oldx) snake->snakedir[0] = 2; } - else if (G_PlayerInputDown(0, GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) + else if (PLAYER1INPUTDOWN(GC_FORWARD) || gamekeydown[KEY_UPARROW] || joystate == 1) { if (snake->snakelength < 2 || y <= oldy) snake->snakedir[0] = 3; } - else if (G_PlayerInputDown(0, GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) + else if (PLAYER1INPUTDOWN(GC_BACKWARD) || gamekeydown[KEY_DOWNARROW] || joystate == 2) { if (snake->snakelength < 2 || y >= oldy) snake->snakedir[0] = 4; @@ -1652,8 +1703,6 @@ static void CL_LoadReceivedSavegame(boolean reloading) titledemo = false; automapactive = false; - P_StopRumble(NULL); - // load a base level if (P_LoadNetGame(reloading)) { @@ -1940,10 +1989,9 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) static void M_ConfirmConnect(event_t *ev) { #ifndef NONET - - if (ev->type == ev_keydown || ev->type == ev_gamepad_down) + if (ev->type == ev_keydown) { - if ((ev->type == ev_keydown && (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_A)) + if (ev->key == ' ' || ev->key == 'y' || ev->key == KEY_ENTER || ev->key == KEY_JOY1) { if (totalfilesrequestednum > 0) { @@ -1958,7 +2006,7 @@ static void M_ConfirmConnect(event_t *ev) M_ClearMenus(true); } - else if ((ev->type == ev_keydown && (ev->key == 'n' || ev->key == KEY_ESCAPE)) || (ev->type == ev_gamepad_down && ev->which == 0 && ev->key == GAMEPAD_BUTTON_B)) + else if (ev->key == 'n' || ev->key == KEY_ESCAPE || ev->key == KEY_JOY1 + 3) { cl_mode = CL_ABORTED; M_ClearMenus(true); @@ -2392,11 +2440,14 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic // my hand has been forced and I am dearly sorry for this awful hack :vomit: for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { - G_MapEventsToControls(&events[eventtail]); +#ifndef NONET + if (!Snake_Joy_Grabber(&events[eventtail])) +#endif + G_MapEventsToControls(&events[eventtail]); } } - if (gamekeydown[KEY_ESCAPE] || gamepads[0].buttons[GAMEPAD_BUTTON_B] || cl_mode == CL_ABORTED) + 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); @@ -5179,7 +5230,7 @@ static void Local_Maketic(INT32 realtics) // game responder calls HU_Responder, AM_Responder, // and G_MapEventsToControls if (!dedicated) rendergametic = gametic; - // translate inputs (keyboard/mouse/gamepad) into game controls + // translate inputs (keyboard/mouse/joystick) into game controls G_BuildTiccmd(&localcmds, realtics, 1); if (splitscreen || botingame) G_BuildTiccmd(&localcmds2, realtics, 2); diff --git a/src/d_event.h b/src/d_event.h index 9448b9c5a..c0b9cef77 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -24,21 +24,19 @@ typedef enum ev_keyup, ev_console, ev_mouse, + ev_joystick, ev_mouse2, - ev_gamepad_up, - ev_gamepad_down, - ev_gamepad_axis + ev_joystick2, } evtype_t; // Event structure. typedef struct { evtype_t type; - INT32 key; // key, mouse button, or gamepad button/axis type - INT32 x; // mouse x move, or gamepad axis value - INT32 y; // mouse y move - UINT8 which; // which gamepad or mouse ID - boolean repeated; // is the event repeated? + INT32 key; // keys/mouse/joystick buttons + INT32 x; // mouse/joystick x move + INT32 y; // mouse/joystick y move + boolean repeated; // key repeat } event_t; // diff --git a/src/d_main.c b/src/d_main.c index 1af8d090c..3566e7f3d 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -43,7 +43,6 @@ #include "i_time.h" #include "i_threads.h" #include "i_video.h" -#include "i_gamepad.h" #include "m_argv.h" #include "m_menu.h" #include "m_misc.h" @@ -987,7 +986,6 @@ void D_StartTitle(void) G_SetGametype(GT_COOP); paused = false; advancedemo = false; - P_StopRumble(NULL); F_InitMenuPresValues(); F_StartTitleScreen(); @@ -1398,9 +1396,6 @@ void D_SRB2Main(void) CONS_Printf("I_InitializeTime()...\n"); I_InitializeTime(); - // Initializes the game logic side of gamepads - G_InitGamepads(); - // Make backups of some SOCcable tables. P_BackupTables(); @@ -1456,9 +1451,6 @@ void D_SRB2Main(void) D_RegisterServerCommands(); D_RegisterClientCommands(); // be sure that this is called before D_CheckNetGame - - I_InitGamepads(); - R_RegisterEngineStuff(); S_RegisterSoundStuff(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 07dba1777..f63f38a74 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -21,7 +21,6 @@ #include "g_game.h" #include "hu_stuff.h" #include "g_input.h" -#include "i_gamepad.h" #include "m_menu.h" #include "r_local.h" #include "r_skins.h" @@ -183,6 +182,14 @@ static CV_PossibleValue_t mouse2port_cons_t[] = {{1, "COM1"}, {2, "COM2"}, {3, " {0, NULL}}; #endif +#ifdef LJOYSTICK +static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"}, + {4, "/dev/js3"}, {0, NULL}}; +#else +// accept whatever value - it is in fact the joystick device number +#define usejoystick_cons_t NULL +#endif + static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}}; static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}}; @@ -241,61 +248,19 @@ INT32 cv_debug; consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse); consvar_t cv_usemouse2 = CVAR_INIT ("use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2); -// We use cv_usegamepad.string as the USER-SET var -// and cv_usegamepad.value as the INTERNAL var -// -// In practice, if cv_usegamepad.string == 0, this overrides -// cv_usegamepad.value and always disables - -static void UseGamepad_OnChange(void) -{ - I_ChangeGamepad(0); -} - -static void UseGamepad2_OnChange(void) -{ - I_ChangeGamepad(1); -} - -consvar_t cv_usegamepad[2] = { - CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, NULL, UseGamepad_OnChange), - CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, NULL, UseGamepad2_OnChange) -}; - -static void PadScale_OnChange(void) -{ - I_SetGamepadDigital(0, cv_gamepad_scale[0].value == 0); -} - -static void PadScale2_OnChange(void) -{ - I_SetGamepadDigital(1, cv_gamepad_scale[1].value == 0); -} - -consvar_t cv_gamepad_scale[2] = { - CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, PadScale_OnChange), - CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, PadScale2_OnChange) -}; - -static void PadRumble_OnChange(void) -{ - if (!cv_gamepad_rumble[0].value) - I_StopGamepadRumble(0); -} - -static void PadRumble2_OnChange(void) -{ - if (!cv_gamepad_rumble[1].value) - I_StopGamepadRumble(1); -} - -consvar_t cv_gamepad_rumble[2] = { - CVAR_INIT ("padrumble", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble_OnChange), - CVAR_INIT ("padrumble2", "Off", CV_SAVE|CV_CALL, CV_OnOff, PadRumble2_OnChange) -}; - -consvar_t cv_gamepad_autopause = CVAR_INIT ("pauseongamepaddisconnect", "On", CV_SAVE, CV_OnOff, NULL); - +consvar_t cv_usejoystick = CVAR_INIT ("use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick); +consvar_t cv_usejoystick2 = CVAR_INIT ("use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2); +#if (defined (LJOYSTICK) || defined (HAVE_SDL)) +#ifdef LJOYSTICK +consvar_t cv_joyport = CVAR_INIT ("padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); +consvar_t cv_joyport2 = CVAR_INIT ("padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL); //Alam: for later +#endif +consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale); +consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2); +#else +consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save +consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save +#endif #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL); consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL); @@ -807,26 +772,26 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_pauseifunfocused); // g_input.c - CV_RegisterVar(&cv_sideaxis[0]); - CV_RegisterVar(&cv_sideaxis[1]); - CV_RegisterVar(&cv_turnaxis[0]); - CV_RegisterVar(&cv_turnaxis[1]); - CV_RegisterVar(&cv_moveaxis[0]); - CV_RegisterVar(&cv_moveaxis[1]); - CV_RegisterVar(&cv_lookaxis[0]); - CV_RegisterVar(&cv_lookaxis[1]); - CV_RegisterVar(&cv_jumpaxis[0]); - CV_RegisterVar(&cv_jumpaxis[1]); - CV_RegisterVar(&cv_spinaxis[0]); - CV_RegisterVar(&cv_spinaxis[1]); - CV_RegisterVar(&cv_fireaxis[0]); - CV_RegisterVar(&cv_fireaxis[1]); - CV_RegisterVar(&cv_firenaxis[0]); - CV_RegisterVar(&cv_firenaxis[1]); - CV_RegisterVar(&cv_deadzone[0]); - CV_RegisterVar(&cv_deadzone[1]); - CV_RegisterVar(&cv_digitaldeadzone[0]); - CV_RegisterVar(&cv_digitaldeadzone[1]); + CV_RegisterVar(&cv_sideaxis); + CV_RegisterVar(&cv_sideaxis2); + CV_RegisterVar(&cv_turnaxis); + CV_RegisterVar(&cv_turnaxis2); + CV_RegisterVar(&cv_moveaxis); + CV_RegisterVar(&cv_moveaxis2); + CV_RegisterVar(&cv_lookaxis); + CV_RegisterVar(&cv_lookaxis2); + CV_RegisterVar(&cv_jumpaxis); + CV_RegisterVar(&cv_jumpaxis2); + CV_RegisterVar(&cv_spinaxis); + CV_RegisterVar(&cv_spinaxis2); + CV_RegisterVar(&cv_fireaxis); + CV_RegisterVar(&cv_fireaxis2); + CV_RegisterVar(&cv_firenaxis); + CV_RegisterVar(&cv_firenaxis2); + CV_RegisterVar(&cv_deadzone); + CV_RegisterVar(&cv_deadzone2); + CV_RegisterVar(&cv_digitaldeadzone); + CV_RegisterVar(&cv_digitaldeadzone2); // filesrch.c CV_RegisterVar(&cv_addons_option); @@ -855,14 +820,14 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_mousemove); CV_RegisterVar(&cv_mousemove2); - for (i = 0; i < 2; i++) - { - CV_RegisterVar(&cv_usegamepad[i]); - CV_RegisterVar(&cv_gamepad_scale[i]); - CV_RegisterVar(&cv_gamepad_rumble[i]); - } - - CV_RegisterVar(&cv_gamepad_autopause); + CV_RegisterVar(&cv_usejoystick); + CV_RegisterVar(&cv_usejoystick2); +#ifdef LJOYSTICK + CV_RegisterVar(&cv_joyport); + CV_RegisterVar(&cv_joyport2); +#endif + CV_RegisterVar(&cv_joyscale); + CV_RegisterVar(&cv_joyscale2); // Analog Control CV_RegisterVar(&cv_analog[0]); @@ -2251,14 +2216,9 @@ static void Got_Pause(UINT8 **cp, INT32 playernum) { if (!menuactive || netgame) S_PauseAudio(); - - P_PauseRumble(NULL); } else - { S_ResumeAudio(); - P_UnpauseRumble(NULL); - } } I_UpdateMouseGrab(); @@ -4653,8 +4613,6 @@ void Command_ExitGame_f(void) emeralds = 0; memset(&luabanks, 0, sizeof(luabanks)); - P_StopRumble(NULL); - if (dirmenu) closefilemenu(true); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 47f68a17e..0beeae154 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -33,10 +33,14 @@ extern consvar_t cv_defaultskin2; extern consvar_t cv_seenames, cv_allowseenames; extern consvar_t cv_usemouse; -extern consvar_t cv_usegamepad[2]; -extern consvar_t cv_gamepad_scale[2]; -extern consvar_t cv_gamepad_rumble[2]; -extern consvar_t cv_gamepad_autopause; +extern consvar_t cv_usejoystick; +extern consvar_t cv_usejoystick2; +#ifdef LJOYSTICK +extern consvar_t cv_joyport; +extern consvar_t cv_joyport2; +#endif +extern consvar_t cv_joyscale; +extern consvar_t cv_joyscale2; // splitscreen with second mouse extern consvar_t cv_mouse2port; diff --git a/src/deh_tables.c b/src/deh_tables.c index a2cc9732d..4a3467f78 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -22,9 +22,9 @@ #include "v_video.h" // video flags (for lua) #include "i_sound.h" // musictype_t (for lua) #include "g_state.h" // gamestate_t (for lua) -#include "g_game.h" // Gamepad axes (for lua) +#include "g_game.h" // Joystick axes (for lua) +#include "i_joy.h" #include "g_input.h" // Game controls (for lua) -#include "i_gamepad.h" #include "deh_tables.h" @@ -4841,7 +4841,7 @@ const char *const MENUTYPES_LIST[] = { "OP_CHANGECONTROLS", // OP_ChangeControlsDef shared with P2 "OP_P1MOUSE", "OP_P1JOYSTICK", - "OP_JOYSTICKSET", // OP_GamepadSetDef shared with P2 + "OP_JOYSTICKSET", // OP_JoystickSetDef shared with P2 "OP_P1CAMERA", "OP_P2CONTROLS", @@ -5642,7 +5642,7 @@ struct int_const_s const INT_CONST[] = { {"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER}, {"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS}, - // Gamepad axes + // Joystick axes {"JA_NONE",JA_NONE}, {"JA_TURN",JA_TURN}, {"JA_MOVE",JA_MOVE}, @@ -5653,7 +5653,7 @@ struct int_const_s const INT_CONST[] = { {"JA_SPIN",JA_SPIN}, {"JA_FIRE",JA_FIRE}, {"JA_FIRENORMAL",JA_FIRENORMAL}, - {"JOYAXISRANGE",OLDJOYAXISRANGE}, + {"JOYAXISRANGE",JOYAXISRANGE}, // Game controls {"GC_NULL",GC_NULL}, diff --git a/src/doomdef.h b/src/doomdef.h index 24b4fa980..2b62bcd6e 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -209,7 +209,7 @@ extern char logfilename[1024]; // to an increment in MODVERSION. This might never happen in practice. // If MODVERSION increases, set MINOREXECVERSION to 0. #define MAJOREXECVERSION MODVERSION -#define MINOREXECVERSION 1 +#define MINOREXECVERSION 0 // (It would have been nice to use VERSION and SUBVERSION but those are zero'd out for DEVELOP builds) // Macros @@ -556,6 +556,9 @@ UINT32 quickncasehash (const char *p, size_t n) #define max(x, y) (((x) > (y)) ? (x) : (y)) #endif +// Max gamepad/joysticks that can be detected/used. +#define MAX_JOYSTICKS 4 + #ifndef M_PIl #define M_PIl 3.1415926535897932384626433832795029L #endif diff --git a/src/f_finale.c b/src/f_finale.c index 307e00aaa..bca8e3ba6 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -37,7 +37,6 @@ #include "m_cond.h" #include "p_local.h" #include "p_setup.h" -#include "p_haptic.h" #include "st_stuff.h" // hud hiding #include "fastcmp.h" #include "console.h" @@ -511,7 +510,6 @@ void F_StartIntro(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - P_StopRumble(NULL); F_NewCutscene(introtext[0]); intro_scenenum = 0; @@ -993,10 +991,9 @@ void F_IntroTicker(void) // boolean F_IntroResponder(event_t *event) { - INT32 type = event->type; - INT32 key = G_RemapGamepadEvent(event, &type); + INT32 key = event->key; - // remap virtual keys (mouse & gamepad buttons) + // remap virtual keys (mouse & joystick buttons) switch (key) { case KEY_MOUSE1: @@ -1005,30 +1002,34 @@ boolean F_IntroResponder(event_t *event) case KEY_MOUSE1 + 1: key = KEY_BACKSPACE; break; - case GAMEPAD_KEY(START): - case GAMEPAD_KEY(A): - case GAMEPAD_KEY(X): - case GAMEPAD_KEY(B): + case KEY_JOY1: + case KEY_JOY1 + 2: key = KEY_ENTER; break; - case GAMEPAD_KEY(DPAD_UP): + case KEY_JOY1 + 3: + key = 'n'; + break; + case KEY_JOY1 + 1: + key = KEY_BACKSPACE; + break; + case KEY_HAT1: key = KEY_UPARROW; break; - case GAMEPAD_KEY(DPAD_DOWN): + case KEY_HAT1 + 1: key = KEY_DOWNARROW; break; - case GAMEPAD_KEY(DPAD_LEFT): + case KEY_HAT1 + 2: key = KEY_LEFTARROW; break; - case GAMEPAD_KEY(DPAD_RIGHT): + case KEY_HAT1 + 3: key = KEY_RIGHTARROW; break; } - if (type != ev_keydown) + if (event->type != ev_keydown && key != 301) return false; - if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_SPACE && key != KEY_BACKSPACE) + if (key != 27 && key != KEY_ENTER && key != KEY_SPACE && key != KEY_BACKSPACE) return false; if (keypressed) @@ -1263,7 +1264,6 @@ void F_StartCredits(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - P_StopRumble(NULL); S_StopMusic(); S_StopSounds(); @@ -1376,10 +1376,9 @@ void F_CreditTicker(void) boolean F_CreditResponder(event_t *event) { - INT32 type = event->type; - INT32 key = G_RemapGamepadEvent(event, &type); + INT32 key = event->key; - // remap virtual keys (mouse & gamepad buttons) + // remap virtual keys (mouse & joystick buttons) switch (key) { case KEY_MOUSE1: @@ -1388,22 +1387,26 @@ boolean F_CreditResponder(event_t *event) case KEY_MOUSE1 + 1: key = KEY_BACKSPACE; break; - case GAMEPAD_KEY(START): - case GAMEPAD_KEY(A): - case GAMEPAD_KEY(X): - case GAMEPAD_KEY(B): + case KEY_JOY1: + case KEY_JOY1 + 2: key = KEY_ENTER; break; - case GAMEPAD_KEY(DPAD_UP): + case KEY_JOY1 + 3: + key = 'n'; + break; + case KEY_JOY1 + 1: + key = KEY_BACKSPACE; + break; + case KEY_HAT1: key = KEY_UPARROW; break; - case GAMEPAD_KEY(DPAD_DOWN): + case KEY_HAT1 + 1: key = KEY_DOWNARROW; break; - case GAMEPAD_KEY(DPAD_LEFT): + case KEY_HAT1 + 2: key = KEY_LEFTARROW; break; - case GAMEPAD_KEY(DPAD_RIGHT): + case KEY_HAT1 + 3: key = KEY_RIGHTARROW; break; } @@ -1411,7 +1414,7 @@ boolean F_CreditResponder(event_t *event) if (!(timesBeaten) && !(netgame || multiplayer) && !cv_debug) return false; - if (type != ev_keydown) + if (event->type != ev_keydown) return false; if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_SPACE && key != KEY_BACKSPACE) @@ -1452,7 +1455,6 @@ void F_StartGameEvaluation(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - P_StopRumble(NULL); finalecount = -1; sparklloop = 0; @@ -1778,7 +1780,6 @@ void F_StartEnding(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - P_StopRumble(NULL); S_StopMusic(); // todo: placeholder S_StopSounds(); @@ -2224,7 +2225,6 @@ void F_StartGameEnd(void) paused = false; CON_ToggleOff(); S_StopSounds(); - P_StopRumble(NULL); // In case menus are still up?!! M_ClearMenus(true); @@ -3567,7 +3567,6 @@ void F_StartContinue(void) keypressed = false; paused = false; CON_ToggleOff(); - P_StopRumble(NULL); // In case menus are still up?!! M_ClearMenus(true); @@ -3820,26 +3819,24 @@ void F_ContinueTicker(void) boolean F_ContinueResponder(event_t *event) { + INT32 key = event->key; + if (keypressed) return true; - INT32 type = event->type; - INT32 key = G_RemapGamepadEvent(event, &type); - if (timetonext >= 21*TICRATE/2) return false; if (event->type != ev_keydown) return false; - // remap virtual keys (mouse & gamepad buttons) + // remap virtual keys (mouse & joystick buttons) switch (key) { case KEY_ENTER: case KEY_SPACE: case KEY_MOUSE1: - case GAMEPAD_KEY(START): - case GAMEPAD_KEY(A): - case GAMEPAD_KEY(X): + case KEY_JOY1: + case KEY_JOY1 + 2: break; default: return false; @@ -3957,7 +3954,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset gameaction = ga_nothing; paused = false; CON_ToggleOff(); - P_StopRumble(NULL); F_NewCutscene(cutscenes[cutscenenum]->scene[0].text); diff --git a/src/g_demo.c b/src/g_demo.c index 9099adc71..2da5a76ab 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -32,7 +32,7 @@ #include "z_zone.h" #include "i_video.h" #include "byteptr.h" -#include "i_gamepad.h" +#include "i_joy.h" #include "r_local.h" #include "r_skins.h" #include "y_inter.h" @@ -1527,9 +1527,9 @@ void G_BeginRecording(void) buf |= 0x08; pflags |= PF_AUTOBRAKE; } - if (cv_usegamepad[0].value) + if (cv_usejoystick.value) buf |= 0x10; - CV_SetValue(&cv_showinputjoy, !!(cv_usegamepad[0].value)); + CV_SetValue(&cv_showinputjoy, !!(cv_usejoystick.value)); WRITEUINT8(demo_p,buf); player->pflags = pflags; diff --git a/src/g_game.c b/src/g_game.c index ce41ccaf6..74bc42711 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -24,7 +24,6 @@ #include "am_map.h" #include "m_random.h" #include "p_local.h" -#include "p_haptic.h" #include "r_draw.h" #include "r_main.h" #include "s_sound.h" @@ -39,7 +38,7 @@ #include "z_zone.h" #include "i_video.h" #include "byteptr.h" -#include "i_gamepad.h" +#include "i_joy.h" #include "r_local.h" #include "r_skins.h" #include "y_inter.h" @@ -60,6 +59,9 @@ boolean botingame; UINT8 botskin; UINT16 botcolor; +JoyType_t Joystick; +JoyType_t Joystick2; + // 1024 bytes is plenty for a savegame #define SAVEGAMESIZE (1024) @@ -254,6 +256,12 @@ UINT32 timesBeaten; UINT32 timesBeatenWithEmeralds; UINT32 timesBeatenUltimate; +typedef struct joystickvector2_s +{ + INT32 xaxis; + INT32 yaxis; +} joystickvector2_t; + boolean precache = true; // if true, load all graphics at start INT16 prevmap, nextmap; @@ -272,42 +280,21 @@ static void AutoBrake2_OnChange(void); void SendWeaponPref(void); void SendWeaponPref2(void); -CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, -#ifndef OLD_GAMEPAD_AXES - {1, "Left Stick X"}, {2, "Left Stick Y"}, - {3, "Right Stick X"},{4, "Right Stick Y"}, - {-1, "Left Stick X-"}, {-2, "Left Stick Y-"}, - {-3, "Right Stick X-"}, {-4, "Right Stick Y-"}, - {5, "Left Trigger"}, {6, "Right Trigger"}, -#else - {1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, - #if JOYAXISSET > 1 - {3, "Z-Axis"}, {4, "X-Rudder"}, {-3, "Z-Axis-"}, {-4, "X-Rudder-"}, - #endif - #if JOYAXISSET > 2 - {5, "Y-Rudder"}, {6, "Z-Rudder"}, {-5, "Y-Rudder-"}, {-6, "Z-Rudder-"}, - #endif - #if JOYAXISSET > 3 - {7, "U-Axis"}, {8, "V-Axis"}, {-7, "U-Axis-"}, {-8, "V-Axis-"}, - #endif +static CV_PossibleValue_t crosshair_cons_t[] = {{0, "Off"}, {1, "Cross"}, {2, "Angle"}, {3, "Point"}, {0, NULL}}; +static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, +{1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, +#if JOYAXISSET > 1 +{3, "Z-Axis"}, {4, "X-Rudder"}, {-3, "Z-Axis-"}, {-4, "X-Rudder-"}, #endif - {0, NULL} -}; - -#ifndef OLD_GAMEPAD_AXES -#define MOVEAXIS_DEFAULT "Left Stick Y" -#define SIDEAXIS_DEFAULT "Left Stick X" -#define LOOKAXIS_DEFAULT "Right Stick Y-" -#define TURNAXIS_DEFAULT "Right Stick X" -#define FIREAXIS_DEFAULT "Right Trigger" -#define FIRENAXIS_DEFAULT "Left Trigger" -#else -#define MOVEAXIS_DEFAULT "Y-Axis" -#define SIDEAXIS_DEFAULT "X-Axis" -#define LOOKAXIS_DEFAULT "Y-Rudder-" -#define TURNAXIS_DEFAULT "X-Rudder" -#define FIREAXIS_DEFAULT "Z-Rudder" -#define FIRENAXIS_DEFAULT "Z-Axis" +#if JOYAXISSET > 2 +{5, "Y-Rudder"}, {6, "Z-Rudder"}, {-5, "Y-Rudder-"}, {-6, "Z-Rudder-"}, +#endif +#if JOYAXISSET > 3 +{7, "U-Axis"}, {8, "V-Axis"}, {-7, "U-Axis-"}, {-8, "V-Axis-"}, +#endif + {0, NULL}}; +#if JOYAXISSET > 4 +"More Axis Sets" #endif // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. @@ -343,7 +330,6 @@ consvar_t cv_consolechat = CVAR_INIT ("chatmode", "Window", CV_SAVE, consolechat // Pause game upon window losing focus consvar_t cv_pauseifunfocused = CVAR_INIT ("pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL); -static CV_PossibleValue_t crosshair_cons_t[] = {{0, "Off"}, {1, "Cross"}, {2, "Angle"}, {3, "Point"}, {0, NULL}}; consvar_t cv_crosshair = CVAR_INIT ("crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL); consvar_t cv_crosshair2 = CVAR_INIT ("crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL); consvar_t cv_invertmouse = CVAR_INIT ("invertmouse", "Off", CV_SAVE, CV_OnOff, NULL); @@ -423,46 +409,27 @@ consvar_t cv_cam_lockonboss[2] = { CVAR_INIT ("cam2_lockaimassist", "Full", CV_SAVE, lockedassist_cons_t, NULL), }; -consvar_t cv_moveaxis[2] = { - CVAR_INIT ("joyaxis_move", MOVEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_move", MOVEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_sideaxis[2] = { - CVAR_INIT ("joyaxis_side", SIDEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_side", SIDEAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_lookaxis[2] = { - CVAR_INIT ("joyaxis_look", LOOKAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_look", LOOKAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_turnaxis[2] = { - CVAR_INIT ("joyaxis_turn", TURNAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_turn", TURNAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_jumpaxis[2] = { - CVAR_INIT ("joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_spinaxis[2] = { - CVAR_INIT ("joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_fireaxis[2] = { - CVAR_INIT ("joyaxis_fire", FIREAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_fire", FIREAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_firenaxis[2] = { - CVAR_INIT ("joyaxis_firenormal", FIRENAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL), - CVAR_INIT ("joyaxis2_firenormal", FIRENAXIS_DEFAULT, CV_SAVE, joyaxis_cons_t, NULL) -}; -consvar_t cv_deadzone[2] = { - CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) -}; -consvar_t cv_digitaldeadzone[2] = { - CVAR_INIT ("joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL), - CVAR_INIT ("joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL) -}; +consvar_t cv_moveaxis = CVAR_INIT ("joyaxis_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_sideaxis = CVAR_INIT ("joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_lookaxis = CVAR_INIT ("joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_turnaxis = CVAR_INIT ("joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_jumpaxis = CVAR_INIT ("joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_spinaxis = CVAR_INIT ("joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_fireaxis = CVAR_INIT ("joyaxis_fire", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_firenaxis = CVAR_INIT ("joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_deadzone = CVAR_INIT ("joy_deadzone", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); +consvar_t cv_digitaldeadzone = CVAR_INIT ("joy_digdeadzone", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); + +consvar_t cv_moveaxis2 = CVAR_INIT ("joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_sideaxis2 = CVAR_INIT ("joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_lookaxis2 = CVAR_INIT ("joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_turnaxis2 = CVAR_INIT ("joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_jumpaxis2 = CVAR_INIT ("joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_spinaxis2 = CVAR_INIT ("joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_fireaxis2 = CVAR_INIT ("joyaxis2_fire", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_firenaxis2 = CVAR_INIT ("joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL); +consvar_t cv_deadzone2 = CVAR_INIT ("joy_deadzone2", "0.125", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); +consvar_t cv_digitaldeadzone2 = CVAR_INIT ("joy_digdeadzone2", "0.25", CV_FLOAT|CV_SAVE, zerotoone_cons_t, NULL); player_t *seenplayer; // player we're aiming at right now @@ -861,171 +828,194 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming) return (INT16)((*aiming)>>16); } -#ifdef OLD_GAMEPAD_AXES -static gamepad_axis_e ConvertXboxControllerAxes(int type) -{ - switch (type) - { - // Left stick - case 1: // X-Axis - return GAMEPAD_AXIS_LEFTX; - case 2: // Y-Axis - return GAMEPAD_AXIS_LEFTY; - - // Right stick - case 4: // X-Rudder - return GAMEPAD_AXIS_RIGHTX; - case 5: // Y-Rudder - return GAMEPAD_AXIS_RIGHTY; - - // Triggers - case 3: // Z-Axis - return GAMEPAD_AXIS_TRIGGERLEFT; - case 6: // Z-Rudder - return GAMEPAD_AXIS_TRIGGERRIGHT; - - default: // All the other ones - return NUM_GAMEPAD_AXES; - } -} -#endif - -static INT16 GetJoystickAxisValue(UINT8 which, joyaxis_e axissel, INT32 axisval) +INT32 JoyAxis(joyaxis_e axissel) { + INT32 retaxis; + INT32 axisval; boolean flp = false; - if (axisval < 0) // odd -axes - { - axisval = -axisval; - flp = true; - } - else if (axisval == 0) - return 0; - - if (axisval > JOYAXISSET*2) - return 0; - - gamepad_axis_e gp_axis; - -#ifdef OLD_GAMEPAD_AXES - gp_axis = ConvertXboxControllerAxes(axisval); -#else - gp_axis = axisval - 1; -#endif - - if (gp_axis >= NUM_GAMEPAD_AXES) - return 0; - - if (axisval % 2) - axisval /= 2; - else - { - axisval--; - axisval /= 2; - } - - INT16 retaxis = G_GetGamepadAxisValue(0, gp_axis); - - if (gamepads[which].digital && axissel >= JA_DIGITAL) - { - const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which) / 2; - if (-jdeadzone < retaxis && retaxis < jdeadzone) - return 0; - } - - // flip it around - if (flp) - retaxis = -retaxis; - - return retaxis; -} - -INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel) -{ - INT32 axisval; - - // find what axis to get + //find what axis to get switch (axissel) { case JA_TURN: - axisval = cv_turnaxis[which].value; + axisval = cv_turnaxis.value; break; case JA_MOVE: - axisval = cv_moveaxis[which].value; + axisval = cv_moveaxis.value; break; case JA_LOOK: - axisval = cv_lookaxis[which].value; + axisval = cv_lookaxis.value; break; case JA_STRAFE: - axisval = cv_sideaxis[which].value; + axisval = cv_sideaxis.value; break; case JA_JUMP: - axisval = cv_jumpaxis[which].value; + axisval = cv_jumpaxis.value; break; case JA_SPIN: - axisval = cv_spinaxis[which].value; + axisval = cv_spinaxis.value; break; case JA_FIRE: - axisval = cv_fireaxis[which].value; + axisval = cv_fireaxis.value; break; case JA_FIRENORMAL: - axisval = cv_firenaxis[which].value; + axisval = cv_firenaxis.value; break; default: return 0; } - return GetJoystickAxisValue(which, axissel, axisval); -} - -static INT16 GetAnalogInput(UINT8 which, gamecontrols_e gc) -{ - for (UINT8 i = 0; i < 2; i++) + if (axisval < 0) //odd -axises { - SINT8 isAnalog = G_PlayerInputIsAnalog(which, gc, i); - if (!isAnalog) - continue; + axisval = -axisval; + flp = true; + } + if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None + return 0; - INT16 value = G_GetAnalogPlayerInput(which, gc, i); - if (value > 0 && isAnalog == 1) - return value; - else if (value < 0 && isAnalog == -1) - return max(min(-value, INT16_MAX), INT16_MIN); + if (axisval%2) + { + axisval /= 2; + retaxis = joyxmove[axisval]; + } + else + { + axisval--; + axisval /= 2; + retaxis = joyymove[axisval]; } - return 0; + if (retaxis < (-JOYAXISRANGE)) + retaxis = -JOYAXISRANGE; + if (retaxis > (+JOYAXISRANGE)) + retaxis = +JOYAXISRANGE; + + if (!Joystick.bGamepadStyle && axissel >= JA_DIGITAL) + { + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone.value) >> FRACBITS; + if (-jdeadzone < retaxis && retaxis < jdeadzone) + return 0; + } + + if (flp) retaxis = -retaxis; //flip it around + return retaxis; } -static boolean CheckAxesUsable(UINT8 which, gamecontrols_e gc1, gamecontrols_e gc2) +INT32 Joy2Axis(joyaxis_e axissel) { - INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; + INT32 retaxis; + INT32 axisval; + boolean flp = false; -#define CHECK_RANGE(x, y, z) \ - (controls[x][y] >= KEY_AXES && controls[x][y] < KEY_AXES + NUM_GAMEPAD_AXES \ - && controls[x][z] >= KEY_INV_AXES && controls[x][z] < KEY_INV_AXES + NUM_GAMEPAD_AXES) + //find what axis to get + switch (axissel) + { + case JA_TURN: + axisval = cv_turnaxis2.value; + break; + case JA_MOVE: + axisval = cv_moveaxis2.value; + break; + case JA_LOOK: + axisval = cv_lookaxis2.value; + break; + case JA_STRAFE: + axisval = cv_sideaxis2.value; + break; + case JA_JUMP: + axisval = cv_jumpaxis2.value; + break; + case JA_SPIN: + axisval = cv_spinaxis2.value; + break; + case JA_FIRE: + axisval = cv_fireaxis2.value; + break; + case JA_FIRENORMAL: + axisval = cv_firenaxis2.value; + break; + default: + return 0; + } - if (CHECK_RANGE(gc1, 0, 1) || CHECK_RANGE(gc2, 0, 1)) - return false; - if (CHECK_RANGE(gc1, 1, 0) || CHECK_RANGE(gc2, 1, 0)) - return false; -#undef CHECK_RANGE + if (axisval < 0) //odd -axises + { + axisval = -axisval; + flp = true; + } - return true; + if (axisval > JOYAXISSET*2 || axisval == 0) //not there in array or None + return 0; + + if (axisval%2) + { + axisval /= 2; + retaxis = joy2xmove[axisval]; + } + else + { + axisval--; + axisval /= 2; + retaxis = joy2ymove[axisval]; + } + + if (retaxis < (-JOYAXISRANGE)) + retaxis = -JOYAXISRANGE; + if (retaxis > (+JOYAXISRANGE)) + retaxis = +JOYAXISRANGE; + + if (!Joystick2.bGamepadStyle && axissel >= JA_DIGITAL) + { + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone2.value) >> FRACBITS; + if (-jdeadzone < retaxis && retaxis < jdeadzone) + return 0; + } + + if (flp) retaxis = -retaxis; //flip it around + return retaxis; } -typedef struct + +#define PlayerJoyAxis(p, ax) ((p) == 1 ? JoyAxis(ax) : Joy2Axis(ax)) + +// Take a magnitude of two axes, and adjust it to take out the deadzone +// Will return a value between 0 and JOYAXISRANGE +static INT32 G_BasicDeadZoneCalculation(INT32 magnitude, fixed_t deadZone) { - INT32 xaxis, yaxis; -} joystickvector2_t; + const INT32 jdeadzone = (JOYAXISRANGE * deadZone) / FRACUNIT; + INT32 deadzoneAppliedValue = 0; + INT32 adjustedMagnitude = abs(magnitude); + + if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%... + return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 + else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone + { + adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); + + adjustedMagnitude -= jdeadzone; + + deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); + } + + return deadzoneAppliedValue; +} // Get the actual sensible radial value for a joystick axis when accounting for a deadzone -static void G_HandleAxisDeadZone(UINT8 playernum, joystickvector2_t *joystickvector) +static void G_HandleAxisDeadZone(UINT8 splitnum, joystickvector2_t *joystickvector) { - if (!gamepads[playernum].digital) - { - const UINT16 deadZone = G_GetGamepadDeadZone(playernum); + INT32 gamepadStyle = Joystick.bGamepadStyle; + fixed_t deadZone = cv_deadzone.value; + if (splitnum == 1) + { + gamepadStyle = Joystick2.bGamepadStyle; + deadZone = cv_deadzone2.value; + } + + // When gamepadstyle is "true" the values are just -1, 0, or 1. This is done in the interface code. + if (!gamepadStyle) + { // Get the total magnitude of the 2 axes INT32 magnitude = (joystickvector->xaxis * joystickvector->xaxis) + (joystickvector->yaxis * joystickvector->yaxis); INT32 normalisedXAxis; @@ -1039,18 +1029,18 @@ static void G_HandleAxisDeadZone(UINT8 playernum, joystickvector2_t *joystickvec normalisedYAxis = (joystickvector->yaxis * magnitude) / JOYAXISRANGE; // Apply the deadzone to the magnitude to give a correct value between 0 and JOYAXISRANGE - normalisedMagnitude = G_BasicDeadZoneCalculation(abs(magnitude), deadZone); + normalisedMagnitude = G_BasicDeadZoneCalculation(magnitude, deadZone); // Apply the deadzone to the xy axes joystickvector->xaxis = (normalisedXAxis * normalisedMagnitude) / JOYAXISRANGE; joystickvector->yaxis = (normalisedYAxis * normalisedMagnitude) / JOYAXISRANGE; - } - // Cap the values so they don't go above the correct maximum - joystickvector->xaxis = min(joystickvector->xaxis, JOYAXISRANGE); - joystickvector->xaxis = max(joystickvector->xaxis, -JOYAXISRANGE - 1); - joystickvector->yaxis = min(joystickvector->yaxis, JOYAXISRANGE); - joystickvector->yaxis = max(joystickvector->yaxis, -JOYAXISRANGE - 1); + // Cap the values so they don't go above the correct maximum + joystickvector->xaxis = min(joystickvector->xaxis, JOYAXISRANGE); + joystickvector->xaxis = max(joystickvector->xaxis, -JOYAXISRANGE); + joystickvector->yaxis = min(joystickvector->yaxis, JOYAXISRANGE); + joystickvector->yaxis = max(joystickvector->yaxis, -JOYAXISRANGE); + } } // @@ -1073,8 +1063,6 @@ boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object? void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { - UINT8 forplayer = ssplayer - 1; - boolean forcestrafe = false; boolean forcefullinput = false; INT32 tspeed, forward, side, axis, strafeaxis, moveaxis, turnaxis, lookaxis, i; @@ -1083,17 +1071,15 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) const INT32 speed = 1; // these ones used for multiple conditions - boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming; - boolean analogaxismove, digitalaxismove, thisjoyaiming; + boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming; boolean strafeisturn; // Simple controls only player_t *player = &players[ssplayer == 2 ? secondarydisplayplayer : consoleplayer]; camera_t *thiscam = ((ssplayer == 1 || player->bot == BOT_2PHUMAN) ? &camera : &camera2); angle_t *myangle = (ssplayer == 1 ? &localangle : &localangle2); INT32 *myaiming = (ssplayer == 1 ? &localaiming : &localaiming2); - gamepad_t *gamepad = &gamepads[forplayer]; angle_t drawangleoffset = (player->powers[pw_carry] == CR_ROLLOUT) ? ANGLE_180 : 0; - INT32 chasecam, chasefreelook, alwaysfreelook, usegamepad, invertmouse, turnmultiplier, mousemove; + INT32 chasecam, chasefreelook, alwaysfreelook, usejoystick, invertmouse, turnmultiplier, mousemove; controlstyle_e controlstyle = G_ControlStyle(ssplayer); INT32 mdx, mdy, mldy; @@ -1107,11 +1093,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) static fixed_t tta_factor[2] = {FRACUNIT, FRACUNIT}; // disables turn-to-angle when manually turning camera until movement happens boolean centerviewdown = false; + UINT8 forplayer = ssplayer-1; + if (ssplayer == 1) { chasecam = cv_chasecam.value; chasefreelook = cv_chasefreelook.value; alwaysfreelook = cv_alwaysfreelook.value; + usejoystick = cv_usejoystick.value; invertmouse = cv_invertmouse.value; turnmultiplier = cv_cam_turnmultiplier.value; mousemove = cv_mousemove.value; @@ -1125,6 +1114,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) chasecam = cv_chasecam2.value; chasefreelook = cv_chasefreelook2.value; alwaysfreelook = cv_alwaysfreelook2.value; + usejoystick = cv_usejoystick2.value; invertmouse = cv_invertmouse2.value; turnmultiplier = cv_cam2_turnmultiplier.value; mousemove = cv_mousemove2.value; @@ -1134,8 +1124,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver } - usegamepad = cv_usegamepad[forplayer].value; - if (menuactive || CON_Ready() || chat_on) mdx = mdy = mldy = 0; @@ -1154,14 +1142,13 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) return; } - // Axes for turning or strafing are ignored here - turnright = G_CheckDigitalPlayerInput(forplayer, GC_TURNRIGHT); - turnleft = G_CheckDigitalPlayerInput(forplayer, GC_TURNLEFT); + turnright = PLAYERINPUTDOWN(ssplayer, GC_TURNRIGHT); + turnleft = PLAYERINPUTDOWN(ssplayer, GC_TURNLEFT); - straferkey = G_CheckDigitalPlayerInput(forplayer, GC_STRAFERIGHT); - strafelkey = G_CheckDigitalPlayerInput(forplayer, GC_STRAFELEFT); - movefkey = G_CheckDigitalPlayerInput(forplayer, GC_FORWARD); - movebkey = G_CheckDigitalPlayerInput(forplayer, GC_BACKWARD); + straferkey = PLAYERINPUTDOWN(ssplayer, GC_STRAFERIGHT); + strafelkey = PLAYERINPUTDOWN(ssplayer, GC_STRAFELEFT); + movefkey = PLAYERINPUTDOWN(ssplayer, GC_FORWARD); + movebkey = PLAYERINPUTDOWN(ssplayer, GC_BACKWARD); if (strafeisturn) { @@ -1170,10 +1157,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) straferkey = strafelkey = false; } - mouseaiming = (G_PlayerInputDown(forplayer, GC_MOUSEAIMING)) ^ + mouseaiming = (PLAYERINPUTDOWN(ssplayer, GC_MOUSEAIMING)) ^ ((chasecam && !player->spectator) ? chasefreelook : alwaysfreelook); - analogaxismove = usegamepad && !gamepad->digital; - digitalaxismove = usegamepad && gamepad->digital; + analogjoystickmove = usejoystick && !Joystick.bGamepadStyle; + gamepadjoystickmove = usejoystick && Joystick.bGamepadStyle; thisjoyaiming = (chasecam && !player->spectator) ? chasefreelook : alwaysfreelook; @@ -1182,38 +1169,19 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) *myaiming = 0; joyaiming[forplayer] = thisjoyaiming; - turnaxis = G_JoyAxis(forplayer, JA_TURN); + turnaxis = PlayerJoyAxis(ssplayer, JA_TURN); if (strafeisturn) - turnaxis += G_JoyAxis(forplayer, JA_STRAFE); - lookaxis = G_JoyAxis(forplayer, JA_LOOK); - - if (usegamepad) - { - turnaxis -= GetAnalogInput(forplayer, GC_TURNLEFT); - turnaxis += GetAnalogInput(forplayer, GC_TURNRIGHT); - - if (strafeisturn) - { - turnaxis -= GetAnalogInput(forplayer, GC_STRAFELEFT); - turnaxis += GetAnalogInput(forplayer, GC_STRAFERIGHT); - } - - lookaxis += GetAnalogInput(forplayer, GC_LOOKUP); - lookaxis -= GetAnalogInput(forplayer, GC_LOOKDOWN); - } - - // Handle deadzones + turnaxis += PlayerJoyAxis(ssplayer, JA_STRAFE); + lookaxis = PlayerJoyAxis(ssplayer, JA_LOOK); lookjoystickvector.xaxis = turnaxis; lookjoystickvector.yaxis = lookaxis; G_HandleAxisDeadZone(forplayer, &lookjoystickvector); - // Do digital axis turning - if (digitalaxismove && lookjoystickvector.xaxis != 0) + if (gamepadjoystickmove && lookjoystickvector.xaxis != 0) { turnright = turnright || (lookjoystickvector.xaxis > 0); turnleft = turnleft || (lookjoystickvector.xaxis < 0); } - forward = side = 0; // use two stage accelerative turning @@ -1252,10 +1220,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (turnleft) side -= sidemove[speed]; - if (analogaxismove && lookjoystickvector.xaxis != 0) + if (analogjoystickmove && lookjoystickvector.xaxis != 0) { - // JOYAXISRANGE is supposed to be 32767 (divide by 32768) - side += ((lookjoystickvector.xaxis * sidemove[1]) >> 15); + // JOYAXISRANGE is supposed to be 1023 (divide by 1024) + side += ((lookjoystickvector.xaxis * sidemove[1]) >> 10); } } else if (controlstyle == CS_LMAOGALOG) // Analog @@ -1273,69 +1241,47 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else if (turnleft) cmd->angleturn = (INT16)(cmd->angleturn + ((angleturn[tspeed] * turnmultiplier)>>FRACBITS)); - if (analogaxismove && lookjoystickvector.xaxis != 0) + if (analogjoystickmove && lookjoystickvector.xaxis != 0) { - // JOYAXISRANGE should be 32767 (divide by 32768) - cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 15) * turnmultiplier)>>FRACBITS)); // ANALOG! + // JOYAXISRANGE should be 1023 (divide by 1024) + cmd->angleturn = (INT16)(cmd->angleturn - ((((lookjoystickvector.xaxis * angleturn[1]) >> 10) * turnmultiplier)>>FRACBITS)); // ANALOG! } if (turnright || turnleft || abs(cmd->angleturn) > angleturn[2]) tta_factor[forplayer] = 0; // suspend turn to angle } - // Strafing axes (moving left and right) - if (strafeisturn) - strafeaxis = 0; - else - { - strafeaxis = G_JoyAxis(forplayer, JA_STRAFE); - - if (usegamepad && CheckAxesUsable(forplayer, GC_STRAFELEFT, GC_STRAFERIGHT)) - { - strafeaxis -= GetAnalogInput(forplayer, GC_STRAFELEFT); - strafeaxis += GetAnalogInput(forplayer, GC_STRAFERIGHT); - } - } - - // Moving axes (moving forwards and backwards) - moveaxis = G_JoyAxis(forplayer, JA_MOVE); - if (usegamepad && CheckAxesUsable(forplayer, GC_FORWARD, GC_BACKWARD)) - { - moveaxis -= GetAnalogInput(forplayer, GC_FORWARD); - moveaxis += GetAnalogInput(forplayer, GC_BACKWARD); - } - + strafeaxis = strafeisturn ? 0 : PlayerJoyAxis(ssplayer, JA_STRAFE); + moveaxis = PlayerJoyAxis(ssplayer, JA_MOVE); movejoystickvector.xaxis = strafeaxis; movejoystickvector.yaxis = moveaxis; G_HandleAxisDeadZone(forplayer, &movejoystickvector); - if (digitalaxismove && movejoystickvector.xaxis != 0) + if (gamepadjoystickmove && movejoystickvector.xaxis != 0) { - // Do digital axis movement if (movejoystickvector.xaxis > 0) side += sidemove[speed]; else if (movejoystickvector.xaxis < 0) side -= sidemove[speed]; } - else if (analogaxismove && movejoystickvector.xaxis != 0) + else if (analogjoystickmove && movejoystickvector.xaxis != 0) { - // JOYAXISRANGE is supposed to be 32767 (divide by 32768) - side += ((movejoystickvector.xaxis * sidemove[1]) >> 15); + // JOYAXISRANGE is supposed to be 1023 (divide by 1024) + side += ((movejoystickvector.xaxis * sidemove[1]) >> 10); } // forward with key or button - // also handles digital axis movement - if (movefkey || (digitalaxismove && movejoystickvector.yaxis < 0) + if (movefkey || (gamepadjoystickmove && movejoystickvector.yaxis < 0) || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (G_CheckDigitalPlayerInput(forplayer, GC_LOOKUP) || (digitalaxismove && lookjoystickvector.yaxis > 0)))) + && (PLAYERINPUTDOWN(ssplayer, GC_LOOKUP) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)))) forward = forwardmove[speed]; - if (movebkey || (digitalaxismove && movejoystickvector.yaxis > 0) + if (movebkey || (gamepadjoystickmove && movejoystickvector.yaxis > 0) || ((player->powers[pw_carry] == CR_NIGHTSMODE) - && (G_CheckDigitalPlayerInput(forplayer, GC_LOOKDOWN) || (digitalaxismove && lookjoystickvector.yaxis < 0)))) + && (PLAYERINPUTDOWN(ssplayer, GC_LOOKDOWN) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)))) forward -= forwardmove[speed]; - if (analogaxismove && movejoystickvector.yaxis != 0) - forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 15); // ANALOG! + if (analogjoystickmove && movejoystickvector.yaxis != 0) + forward -= ((movejoystickvector.yaxis * forwardmove[1]) >> 10); // ANALOG! // some people strafe left & right with mouse buttons // those people are weird @@ -1344,54 +1290,53 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (strafelkey) side -= sidemove[speed]; - if (G_PlayerInputDown(forplayer, GC_WEAPONNEXT)) + if (PLAYERINPUTDOWN(ssplayer, GC_WEAPONNEXT)) cmd->buttons |= BT_WEAPONNEXT; // Next Weapon - if (G_PlayerInputDown(forplayer, GC_WEAPONPREV)) + if (PLAYERINPUTDOWN(ssplayer, GC_WEAPONPREV)) cmd->buttons |= BT_WEAPONPREV; // Previous Weapon #if NUM_WEAPONS > 10 -#error "Add extra inputs to g_input.h/gamecontrols_e" +"Add extra inputs to g_input.h/gamecontrols_e" #endif - //use the four avaliable bits to determine the weapon. cmd->buttons &= ~BT_WEAPONMASK; for (i = 0; i < NUM_WEAPONS; ++i) - if (G_PlayerInputDown(forplayer, GC_WEPSLOT1 + i)) + if (PLAYERINPUTDOWN(ssplayer, GC_WEPSLOT1 + i)) { cmd->buttons |= (UINT16)(i + 1); break; } // fire with any button/key - axis = G_JoyAxis(forplayer, JA_FIRE); - if (G_PlayerInputDown(forplayer, GC_FIRE) || (usegamepad && axis > 0)) + axis = PlayerJoyAxis(ssplayer, JA_FIRE); + if (PLAYERINPUTDOWN(ssplayer, GC_FIRE) || (usejoystick && axis > 0)) cmd->buttons |= BT_ATTACK; // fire normal with any button/key - axis = G_JoyAxis(forplayer, JA_FIRENORMAL); - if (G_PlayerInputDown(forplayer, GC_FIRENORMAL) || (usegamepad && axis > 0)) + axis = PlayerJoyAxis(ssplayer, JA_FIRENORMAL); + if (PLAYERINPUTDOWN(ssplayer, GC_FIRENORMAL) || (usejoystick && axis > 0)) cmd->buttons |= BT_FIRENORMAL; - if (G_PlayerInputDown(forplayer, GC_TOSSFLAG)) + if (PLAYERINPUTDOWN(ssplayer, GC_TOSSFLAG)) cmd->buttons |= BT_TOSSFLAG; // Lua scriptable buttons - if (G_PlayerInputDown(forplayer, GC_CUSTOM1)) + if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM1)) cmd->buttons |= BT_CUSTOM1; - if (G_PlayerInputDown(forplayer, GC_CUSTOM2)) + if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM2)) cmd->buttons |= BT_CUSTOM2; - if (G_PlayerInputDown(forplayer, GC_CUSTOM3)) + if (PLAYERINPUTDOWN(ssplayer, GC_CUSTOM3)) cmd->buttons |= BT_CUSTOM3; - // spin with any button/key - axis = G_JoyAxis(forplayer, JA_SPIN); - if (G_PlayerInputDown(forplayer, GC_SPIN) || (usegamepad && axis > 0)) + // use with any button/key + axis = PlayerJoyAxis(ssplayer, JA_SPIN); + if (PLAYERINPUTDOWN(ssplayer, GC_SPIN) || (usejoystick && axis > 0)) cmd->buttons |= BT_SPIN; // Centerview can be a toggle in simple mode! { static boolean last_centerviewdown[2], centerviewhold[2]; // detect taps for toggle behavior - boolean down = G_PlayerInputDown(forplayer, GC_CENTERVIEW); + boolean down = PLAYERINPUTDOWN(ssplayer, GC_CENTERVIEW); if (!(controlstyle == CS_SIMPLE && cv_cam_centertoggle[forplayer].value)) centerviewdown = down; @@ -1490,7 +1435,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (ticcmd_centerviewdown[forplayer] && controlstyle == CS_SIMPLE) controlstyle = CS_LEGACY; - if (G_PlayerInputDown(forplayer, GC_CAMRESET)) + if (PLAYERINPUTDOWN(ssplayer, GC_CAMRESET)) { if (thiscam->chase && !resetdown[forplayer]) P_ResetCamera(&players[ssplayer == 1 ? displayplayer : secondarydisplayplayer], thiscam); @@ -1500,9 +1445,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) else resetdown[forplayer] = false; + // jump button - axis = G_JoyAxis(forplayer, JA_JUMP); - if (G_PlayerInputDown(forplayer, GC_JUMP) || (usegamepad && axis > 0)) + axis = PlayerJoyAxis(ssplayer, JA_JUMP); + if (PLAYERINPUTDOWN(ssplayer, GC_JUMP) || (usejoystick && axis > 0)) cmd->buttons |= BT_JUMP; // player aiming shit, ahhhh... @@ -1512,6 +1458,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) (player->mo && (player->mo->eflags & MFE_VERTICALFLIP) && (!thiscam->chase || player->pflags & PF_FLIPCAM)) //because chasecam's not inverted ? -1 : 1; // set to -1 or 1 to multiply + INT32 configlookaxis = ssplayer == 1 ? cv_lookaxis.value : cv_lookaxis2.value; // mouse look stuff (mouse look is not the same as mouse aim) if (mouseaiming) @@ -1522,21 +1469,21 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) *myaiming += (mldy<<19)*player_invert*screen_invert; } - if (joyaiming[forplayer] && lookjoystickvector.yaxis != 0) - *myaiming += (lookjoystickvector.yaxis<<11) * screen_invert; + if (analogjoystickmove && joyaiming[forplayer] && lookjoystickvector.yaxis != 0 && configlookaxis != 0) + *myaiming += (lookjoystickvector.yaxis<<16) * screen_invert; // spring back if not using keyboard neither mouselookin' - if (!keyboard_look[forplayer] && !joyaiming[forplayer] && !mouseaiming) + if (!keyboard_look[forplayer] && configlookaxis == 0 && !joyaiming[forplayer] && !mouseaiming) *myaiming = 0; if (!(player->powers[pw_carry] == CR_NIGHTSMODE)) { - if (G_CheckDigitalPlayerInput(forplayer, GC_LOOKUP) || (digitalaxismove && lookjoystickvector.yaxis < 0)) + if (PLAYERINPUTDOWN(ssplayer, GC_LOOKUP) || (gamepadjoystickmove && lookjoystickvector.yaxis < 0)) { *myaiming += KB_LOOKSPEED * screen_invert; keyboard_look[forplayer] = true; } - else if (G_CheckDigitalPlayerInput(forplayer, GC_LOOKDOWN) || (digitalaxismove && lookjoystickvector.yaxis > 0)) + else if (PLAYERINPUTDOWN(ssplayer, GC_LOOKDOWN) || (gamepadjoystickmove && lookjoystickvector.yaxis > 0)) { *myaiming -= KB_LOOKSPEED * screen_invert; keyboard_look[forplayer] = true; @@ -1769,57 +1716,6 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) return dest; } -static player_t *G_GetInputPlayer(UINT8 which) -{ - if (which == 0) - return &players[displayplayer]; - else if (which == 1) - { - if (splitscreen) - return &players[secondarydisplayplayer]; - else if (playeringame[1] && players[1].bot == BOT_2PHUMAN) - return &players[1]; - } - - return NULL; -} - -// Returns a player's gamepad index, even if it's disabled -// Gamepad indexes correspond to the local player index. -INT16 G_GetGamepadForPlayer(player_t *player) -{ - for (UINT8 i = 0; i < 2; i++) - { - if (player == G_GetInputPlayer(i)) - return i; - } - - return -1; -} - -// Gets the user-set gamepad device for a specific player -INT32 G_GetGamepadDeviceIndex(INT32 player) -{ -#ifdef GAMEPAD_HOTPLUG - if (atoi(cv_usegamepad[player].string) > I_NumGamepads()) - return atoi(cv_usegamepad[player].string); - else -#endif - return cv_usegamepad[player].value; -} - -void G_OnGamepadDisconnect(UINT8 which) -{ - if (!cv_gamepad_autopause.value) - return; - - if (gamestate != GS_LEVEL || paused || netgame || splitscreen) - return; - - if (which == 0 || (which == 1 && playeringame[1] && players[1].bot == BOT_2PHUMAN)) - COM_ImmedExecute("pause"); -} - // User has designated that they want // analog ON, so tell the game to stop // fudging with it. @@ -1891,25 +1787,6 @@ static void AutoBrake2_OnChange(void) SendWeaponPref2(); } -static void G_ResetInputs(void) -{ - memset(gamekeydown, 0, sizeof (gamekeydown)); - - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - for (UINT8 j = 0; j < NUM_GAMEPAD_BUTTONS; j++) - gamepads[i].buttons[j] = 0; - - for (UINT8 j = 0; j < NUM_GAMEPAD_AXES; j++) - gamepads[i].axes[j] = 0; - } - - G_SetMouseDeltas(0, 0, 1); - G_SetMouseDeltas(0, 0, 2); - - P_StopRumble(NULL); -} - // // G_DoLoadLevel // @@ -1978,7 +1855,14 @@ void G_DoLoadLevel(boolean resetplayer) P_ResetCamera(&players[secondarydisplayplayer], &camera2); // clear cmd building stuff - G_ResetInputs(); + memset(gamekeydown, 0, sizeof (gamekeydown)); + for (i = 0;i < JOYAXISSET; i++) + { + joyxmove[i] = joyymove[i] = 0; + joy2xmove[i] = joy2ymove[i] = 0; + } + G_SetMouseDeltas(0, 0, 1); + G_SetMouseDeltas(0, 0, 2); // clear hud messages remains (usually from game startup) CON_ClearHUD(); @@ -2160,14 +2044,11 @@ static boolean ViewpointSwitchResponder(event_t *ev) // boolean G_Responder(event_t *ev) { - INT32 evtype = ev->type; - INT32 key = G_RemapGamepadEvent(ev, &evtype); - // any other key pops up menu if in demos if (gameaction == ga_nothing && !singledemo && ((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN)) { - if (evtype == ev_keydown && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE)) + if (ev->type == ev_keydown && ev->key != 301 && !(gamestate == GS_TITLESCREEN && finalecount < TICRATE)) { M_StartControlPanel(); return true; @@ -2192,7 +2073,7 @@ boolean G_Responder(event_t *ev) return true; // chat ate the event if (AM_Responder(ev)) return true; // automap ate it - // map the event (key/mouse/gamepad) to a gamecontrol + // map the event (key/mouse/joy) to a gamecontrol } // Intro else if (gamestate == GS_INTRO) @@ -2247,16 +2128,16 @@ boolean G_Responder(event_t *ev) // update keys current state G_MapEventsToControls(ev); - switch (evtype) + switch (ev->type) { case ev_keydown: - if (key == gamecontrol[GC_PAUSE][0] - || key == gamecontrol[GC_PAUSE][1] - || key == KEY_PAUSE) + if (ev->key == gamecontrol[GC_PAUSE][0] + || ev->key == gamecontrol[GC_PAUSE][1] + || ev->key == KEY_PAUSE) { if (modeattacking && !demoplayback && (gamestate == GS_LEVEL)) { - pausebreakkey = (key == KEY_PAUSE); + pausebreakkey = (ev->key == KEY_PAUSE); if (menuactive || pausedelay < 0 || leveltime < 2) return true; @@ -2281,8 +2162,8 @@ boolean G_Responder(event_t *ev) } } } - if (key == gamecontrol[GC_CAMTOGGLE][0] - || key == gamecontrol[GC_CAMTOGGLE][1]) + if (ev->key == gamecontrol[GC_CAMTOGGLE][0] + || ev->key == gamecontrol[GC_CAMTOGGLE][1]) { if (!camtoggledelay) { @@ -2290,8 +2171,8 @@ boolean G_Responder(event_t *ev) CV_SetValue(&cv_chasecam, cv_chasecam.value ? 0 : 1); } } - if (key == gamecontrolbis[GC_CAMTOGGLE][0] - || key == gamecontrolbis[GC_CAMTOGGLE][1]) + if (ev->key == gamecontrolbis[GC_CAMTOGGLE][0] + || ev->key == gamecontrolbis[GC_CAMTOGGLE][1]) { if (!camtoggledelay2) { @@ -2304,9 +2185,15 @@ boolean G_Responder(event_t *ev) case ev_keyup: return false; // always let key up events filter down - case ev_mouse: // eat events - case ev_gamepad_axis: - return true; + case ev_mouse: + return true; // eat events + + case ev_joystick: + return true; // eat events + + case ev_joystick2: + return true; // eat events + default: break; @@ -3300,7 +3187,14 @@ void G_DoReborn(INT32 playernum) P_ResetCamera(&players[secondarydisplayplayer], &camera2); // clear cmd building stuff - G_ResetInputs(); + memset(gamekeydown, 0, sizeof (gamekeydown)); + for (i = 0; i < JOYAXISSET; i++) + { + joyxmove[i] = joyymove[i] = 0; + joy2xmove[i] = joy2ymove[i] = 0; + } + G_SetMouseDeltas(0, 0, 1); + G_SetMouseDeltas(0, 0, 2); // clear hud messages remains (usually from game startup) CON_ClearHUD(); @@ -4708,7 +4602,11 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) } save_p += VERSIONSIZE; - P_StopRumble(NULL); +// if (demoplayback) // reset game engine +// G_StopDemo(); + +// paused = false; +// automapactive = false; // dearchive all the modifications if (!P_LoadGame(mapoverride)) @@ -4971,7 +4869,6 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean { INT32 i; - P_StopRumble(NULL); Y_CleanupScreenBuffer(); if (paused) diff --git a/src/g_game.h b/src/g_game.h index 6c24054a0..dca043f2e 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -68,14 +68,10 @@ typedef enum { #define P_ControlStyle(player) ((((player)->pflags & PF_ANALOGMODE) ? CS_LMAOGALOG : 0) | (((player)->pflags & PF_DIRECTIONCHAR) ? CS_STANDARD : 0)) extern consvar_t cv_autobrake, cv_autobrake2; +extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_jumpaxis,cv_spinaxis,cv_fireaxis,cv_firenaxis,cv_deadzone,cv_digitaldeadzone; +extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_jumpaxis2,cv_spinaxis2,cv_fireaxis2,cv_firenaxis2,cv_deadzone2,cv_digitaldeadzone2; extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest; -extern consvar_t cv_sideaxis[2], cv_turnaxis[2], cv_moveaxis[2], cv_lookaxis[2], - cv_jumpaxis[2], cv_spinaxis[2], cv_fireaxis[2], cv_firenaxis[2], - cv_deadzone[2], cv_digitaldeadzone[2]; - -extern CV_PossibleValue_t joyaxis_cons_t[]; - // hi here's some new controls extern consvar_t cv_cam_shiftfacing[2], cv_cam_turnfacing[2], cv_cam_turnfacingability[2], cv_cam_turnfacingspindash[2], cv_cam_turnfacinginput[2], @@ -88,12 +84,10 @@ typedef enum LOCK_INTERESTS = 1<<2, } lockassist_e; -// Legacy axis stuff -#define JOYAXISSET 4 // 4 Sets of 2 axes typedef enum { - JA_NONE, + JA_NONE = 0, JA_TURN, JA_MOVE, JA_LOOK, @@ -107,7 +101,8 @@ typedef enum JA_FIRENORMAL, } joyaxis_e; -INT16 G_JoyAxis(UINT8 which, joyaxis_e axissel); +INT32 JoyAxis(joyaxis_e axissel); +INT32 Joy2Axis(joyaxis_e axissel); // mouseaiming (looking up/down with the mouse or keyboard) #define KB_LOOKSPEED (1<<25) @@ -127,15 +122,6 @@ ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n); // copy ticcmd_t to and fro network packets ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n); -// gets the user-set gamepad device for a specific player -INT32 G_GetGamepadDeviceIndex(INT32 player); - -// returns a player's gamepad index -INT16 G_GetGamepadForPlayer(player_t *player); - -// called when a player's gamepad is disconnected -void G_OnGamepadDisconnect(UINT8 which); - // clip the console player aiming to the view INT16 G_ClipAimingPitch(INT32 *aiming); INT16 G_SoftwareClipAimingPitch(INT32 *aiming); diff --git a/src/g_input.c b/src/g_input.c index 465db0316..79bd2a4a2 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -8,14 +8,12 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file g_input.c -/// \brief handle mouse/keyboard/gamepad inputs, +/// \brief handle mouse/keyboard/joystick inputs, /// maps inputs to game controls (forward, spin, jump...) #include "doomdef.h" #include "doomstat.h" -#include "g_game.h" #include "g_input.h" -#include "i_gamepad.h" #include "keys.h" #include "hu_stuff.h" // need HUFONT start & end #include "d_net.h" @@ -36,7 +34,8 @@ consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecont mouse_t mouse; mouse_t mouse2; -gamepad_t gamepads[NUM_GAMEPADS]; +// joystick values are repeated +INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET]; // current state of the keys: true if pushed UINT8 gamekeydown[NUMINPUTS]; @@ -87,77 +86,114 @@ const INT32 gcl_jump_spin[num_gcl_jump_spin] = { GC_JUMP, GC_SPIN }; -static boolean CheckInputDown(UINT8 which, gamecontrols_e gc, boolean checkaxes) -{ - INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; - - for (unsigned i = 0; i < 2; i++) - { - INT32 key = controls[gc][i]; - - if (key >= KEY_GAMEPAD && key < KEY_AXES) - { - if (gamepads[which].buttons[key - KEY_GAMEPAD]) - return true; - } - else if (checkaxes && (key >= KEY_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES)) - { - const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which); - const INT16 value = G_GetGamepadAxisValue(which, (key - KEY_AXES) % NUM_GAMEPAD_AXES); - - if (abs(value) > jdeadzone) - return true; - } - else if (gamekeydown[key]) - return true; - } - - return false; -} - -boolean G_PlayerInputDown(UINT8 which, gamecontrols_e gc) -{ - return CheckInputDown(which, gc, true); -} - -boolean G_CheckDigitalPlayerInput(UINT8 which, gamecontrols_e gc) -{ - return CheckInputDown(which, gc, false); -} - -SINT8 G_PlayerInputIsAnalog(UINT8 which, gamecontrols_e gc, UINT8 settings) -{ - INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; - INT32 key = controls[gc][settings]; - - if (key >= KEY_AXES && key < KEY_AXES + NUM_GAMEPAD_AXES) - return 1; - else if (key >= KEY_INV_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES) - return -1; - - return 0; -} - -INT16 G_GetAnalogPlayerInput(UINT8 which, gamecontrols_e gc, UINT8 settings) -{ - INT32 (*controls)[2] = which == 0 ? gamecontrol : gamecontrolbis; - INT32 key = controls[gc][settings]; - - if (key >= KEY_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES) - return G_GetGamepadAxisValue(which, (key - KEY_AXES) % NUM_GAMEPAD_AXES); - - return 0; -} - typedef struct { UINT8 time; UINT8 state; UINT8 clicks; } dclick_t; - static dclick_t mousedclicks[MOUSEBUTTONS]; +static dclick_t joydclicks[JOYBUTTONS + JOYHATS*4]; static dclick_t mouse2dclicks[MOUSEBUTTONS]; +static dclick_t joy2dclicks[JOYBUTTONS + JOYHATS*4]; + +// protos +static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt); + +// +// Remaps the inputs to game controls. +// +// A game control can be triggered by one or more keys/buttons. +// +// Each key/mousebutton/joybutton triggers ONLY ONE game control. +// +void G_MapEventsToControls(event_t *ev) +{ + INT32 i; + UINT8 flag; + + switch (ev->type) + { + case ev_keydown: + if (ev->key < NUMINPUTS) + gamekeydown[ev->key] = 1; +#ifdef PARANOIA + else + { + CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->key); + } + +#endif + break; + + case ev_keyup: + if (ev->key < NUMINPUTS) + gamekeydown[ev->key] = 0; +#ifdef PARANOIA + else + { + CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->key); + } +#endif + break; + + case ev_mouse: // buttons are virtual keys + mouse.rdx = ev->x; + mouse.rdy = ev->y; + break; + + case ev_joystick: // buttons are virtual keys + i = ev->key; + if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on) + break; + if (ev->x != INT32_MAX) joyxmove[i] = ev->x; + if (ev->y != INT32_MAX) joyymove[i] = ev->y; + break; + + case ev_joystick2: // buttons are virtual keys + i = ev->key; + if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on) + break; + if (ev->x != INT32_MAX) joy2xmove[i] = ev->x; + if (ev->y != INT32_MAX) joy2ymove[i] = ev->y; + break; + + case ev_mouse2: // buttons are virtual keys + if (menuactive || CON_Ready() || chat_on) + break; + mouse2.rdx = ev->x; + mouse2.rdy = ev->y; + break; + + default: + break; + } + + // ALWAYS check for mouse & joystick double-clicks even if no mouse event + for (i = 0; i < MOUSEBUTTONS; i++) + { + flag = G_CheckDoubleClick(gamekeydown[KEY_MOUSE1+i], &mousedclicks[i]); + gamekeydown[KEY_DBLMOUSE1+i] = flag; + } + + for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) + { + flag = G_CheckDoubleClick(gamekeydown[KEY_JOY1+i], &joydclicks[i]); + gamekeydown[KEY_DBLJOY1+i] = flag; + } + + for (i = 0; i < MOUSEBUTTONS; i++) + { + flag = G_CheckDoubleClick(gamekeydown[KEY_2MOUSE1+i], &mouse2dclicks[i]); + gamekeydown[KEY_DBL2MOUSE1+i] = flag; + } + + for (i = 0; i < JOYBUTTONS + JOYHATS*4; i++) + { + flag = G_CheckDoubleClick(gamekeydown[KEY_2JOY1+i], &joy2dclicks[i]); + gamekeydown[KEY_DBL2JOY1+i] = flag; + } +} // // General double-click detection routine for any kind of input. @@ -189,641 +225,6 @@ static UINT8 G_CheckDoubleClick(UINT8 state, dclick_t *dt) return false; } -// -// Remaps the inputs to game controls. -// -// A game control can be triggered by one or more keys/buttons. -// -// Each key/mouse button/gamepad button triggers ONLY ONE game control. -// -void G_MapEventsToControls(event_t *ev) -{ - INT32 i; - UINT8 flag; - - switch (ev->type) - { - case ev_keydown: - if (ev->key < NUMINPUTS) - gamekeydown[ev->key] = 1; -#ifdef PARANOIA - else - CONS_Debug(DBG_GAMELOGIC, "Bad downkey input %d\n",ev->key); - -#endif - break; - - case ev_keyup: - if (ev->key < NUMINPUTS) - gamekeydown[ev->key] = 0; -#ifdef PARANOIA - else - CONS_Debug(DBG_GAMELOGIC, "Bad upkey input %d\n",ev->key); -#endif - break; - - case ev_gamepad_down: - case ev_gamepad_up: -#ifdef PARANOIA - if (ev->which < NUM_GAMEPADS) -#endif - gamepads[ev->which].buttons[ev->key] = ev->type == ev_gamepad_down ? 1 : 0; - break; - - case ev_gamepad_axis: -#ifdef PARANOIA - if (ev->which < NUM_GAMEPADS) -#endif - gamepads[ev->which].axes[ev->key] = ev->x; - break; - - case ev_mouse: - mouse.rdx = ev->x; - mouse.rdy = ev->y; - break; - - case ev_mouse2: - mouse2.rdx = ev->x; - mouse2.rdy = ev->y; - break; - - default: - break; - } - - // ALWAYS check for mouse double-clicks even if there were no such events - for (i = 0; i < MOUSEBUTTONS; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_MOUSE1+i], &mousedclicks[i]); - gamekeydown[KEY_DBLMOUSE1+i] = flag; - } - - for (i = 0; i < MOUSEBUTTONS; i++) - { - flag = G_CheckDoubleClick(gamekeydown[KEY_2MOUSE1+i], &mouse2dclicks[i]); - gamekeydown[KEY_DBL2MOUSE1+i] = flag; - } -} - -const char *const gamepad_button_names[NUM_GAMEPAD_BUTTONS + 1] = { - "a", - "b", - "x", - "y", - "back", - "guide", - "start", - "left-stick", - "right-stick", - "left-shoulder", - "right-shoulder", - "dpad-up", - "dpad-down", - "dpad-left", - "dpad-right", - "misc1", - "paddle1", - "paddle2", - "paddle3", - "paddle4", - "touchpad", - NULL}; - -const char *const gamepad_axis_names[NUM_GAMEPAD_AXES + 1] = { - "left-x", - "left-y", - "right-x", - "right-y", - "trigger-left", - "trigger-right", - NULL}; - -boolean G_GamepadTypeIsXbox(gamepadtype_e type) -{ - switch (type) - { - case GAMEPAD_TYPE_XBOX360: - case GAMEPAD_TYPE_XBOXONE: - case GAMEPAD_TYPE_XBOX_SERIES_XS: - case GAMEPAD_TYPE_XBOX_ELITE: - return true; - default: - return false; - } -} - -boolean G_GamepadTypeIsPlayStation(gamepadtype_e type) -{ - switch (type) - { - case GAMEPAD_TYPE_PS3: - case GAMEPAD_TYPE_PS4: - case GAMEPAD_TYPE_PS5: - return true; - default: - return false; - } -} - -boolean G_GamepadTypeIsNintendoSwitch(gamepadtype_e type) -{ - switch (type) - { - case GAMEPAD_TYPE_NINTENDO_SWITCH_PRO: - case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_GRIP: - return true; - default: - return G_GamepadTypeIsJoyCon(type); - } -} - -boolean G_GamepadTypeIsJoyCon(gamepadtype_e type) -{ - switch (type) - { - case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_LEFT: - case GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_RIGHT: - return true; - default: - return false; - } -} - -boolean G_RumbleSupported(UINT8 which) -{ - if (!I_RumbleSupported() || which >= NUM_GAMEPADS) - return 0; - - return I_GetGamepadRumbleSupported(which); -} - -boolean G_RumbleGamepad(UINT8 which, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration) -{ - haptic_t effect; - - if (!G_RumbleSupported(which)) - return false; - - effect.large_magnitude = large_magnitude; - effect.small_magnitude = small_magnitude; - effect.duration = duration; - - return I_RumbleGamepad(which, &effect); -} - -void G_StopGamepadRumble(UINT8 which) -{ - if (G_RumbleSupported(which)) - I_StopGamepadRumble(which); -} - -fixed_t G_GetLargeMotorFreq(UINT8 which) -{ - if (!G_RumbleSupported(which) || which >= NUM_GAMEPADS) - return 0; - - gamepad_t *gamepad = &gamepads[which]; - return gamepad->rumble.data.large_magnitude; -} - -fixed_t G_GetSmallMotorFreq(UINT8 which) -{ - if (!G_RumbleSupported(which) || which >= NUM_GAMEPADS) - return 0; - - gamepad_t *gamepad = &gamepads[which]; - return gamepad->rumble.data.small_magnitude; -} - -boolean G_GetGamepadRumblePaused(UINT8 which) -{ - return I_GetGamepadRumblePaused(which); -} - -boolean G_SetLargeMotorFreq(UINT8 which, fixed_t freq) -{ - return I_SetGamepadLargeMotorFreq(which, freq); -} - -boolean G_SetSmallMotorFreq(UINT8 which, fixed_t freq) -{ - return I_SetGamepadSmallMotorFreq(which, freq); -} - -void G_SetGamepadRumblePaused(UINT8 which, boolean pause) -{ - if (G_RumbleSupported(which)) - I_SetGamepadRumblePaused(which, pause); -} - -// Obtains the value of an axis, and makes it digital if needed -INT16 G_GamepadAxisEventValue(UINT8 which, INT16 value) -{ - gamepad_t *gamepad = &gamepads[which]; - - if (gamepad->digital) - { - const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(which); - - if (value < -jdeadzone) - value = -JOYAXISRANGE - 1; - else if (value > jdeadzone) - value = JOYAXISRANGE; - else - value = 0; - } - - return value; -} - -INT16 G_GetGamepadAxisValue(UINT8 which, gamepad_axis_e axis) -{ - gamepad_t *gamepad = &gamepads[which]; - - if (axis >= NUM_GAMEPAD_AXES) - return 0; - - return G_GamepadAxisEventValue(which, gamepad->axes[axis]); -} - -fixed_t G_GetAdjustedGamepadAxis(UINT8 which, gamepad_axis_e axis, boolean applyDeadzone) -{ - gamepad_t *gamepad = &gamepads[which]; - - if (axis >= NUM_GAMEPAD_AXES) - return 0; - - INT32 value = gamepad->axes[axis]; - - if (applyDeadzone && gamepad->digital) - { - INT16 deadzone = G_GetGamepadDigitalDeadZone(which); - - if (value < -deadzone) - value = -JOYAXISRANGE; - else if (value > deadzone) - value = JOYAXISRANGE; - else - value = 0; - } - else if (applyDeadzone) - { - INT32 sign = value < 0 ? -1 : 1; - INT16 deadzone = G_GetGamepadDeadZone(which); - INT32 magnitude = value * value; - INT32 nAxis = magnitude / JOYAXISRANGE; - INT32 nMagnitude = G_BasicDeadZoneCalculation(magnitude, deadzone); - - value = (nAxis * nMagnitude) / JOYAXISRANGE; - value = min(value * sign, JOYAXISRANGE); - value = max(value, -JOYAXISRANGE); - } - - return (value / 32767.0) * FRACUNIT; -} - -static UINT16 CalcGamepadDeadZone(fixed_t deadzone) -{ - INT32 value = (JOYAXISRANGE * deadzone) / FRACUNIT; - - if (value < 0) - value = 0; - else if (value > JOYAXISRANGE) - value = JOYAXISRANGE; - - return value; -} - -UINT16 G_GetGamepadDeadZone(UINT8 which) -{ - return CalcGamepadDeadZone(cv_deadzone[which].value); -} - -UINT16 G_GetGamepadDigitalDeadZone(UINT8 which) -{ - return CalcGamepadDeadZone(cv_digitaldeadzone[which].value); -} - -// Take a magnitude of two axes, and adjust it to take out the deadzone -// Will return a value between 0 and JOYAXISRANGE -INT32 G_BasicDeadZoneCalculation(INT32 magnitude, const UINT16 jdeadzone) -{ - INT32 deadzoneAppliedValue = 0; - INT32 adjustedMagnitude = abs(magnitude); - - if (jdeadzone >= JOYAXISRANGE && adjustedMagnitude >= JOYAXISRANGE) // If the deadzone and magnitude are both 100%... - return JOYAXISRANGE; // ...return 100% input directly, to avoid dividing by 0 - else if (adjustedMagnitude > jdeadzone) // Otherwise, calculate how much the magnitude exceeds the deadzone - { - adjustedMagnitude = min(adjustedMagnitude, JOYAXISRANGE); - - adjustedMagnitude -= jdeadzone; - - deadzoneAppliedValue = (adjustedMagnitude * JOYAXISRANGE) / (JOYAXISRANGE - jdeadzone); - } - - return deadzoneAppliedValue; -} - -INT32 G_RemapGamepadEvent(event_t *event, INT32 *type) -{ - if (event->type == ev_gamepad_down) - { - *type = ev_keydown; - return KEY_GAMEPAD + event->key; - } - else if (event->type == ev_gamepad_up) - { - *type = ev_keyup; - return KEY_GAMEPAD + event->key; - } - else if (event->type == ev_gamepad_axis) - { - const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(event->which); - const INT16 value = G_GetGamepadAxisValue(event->which, event->key); - - if (value < -jdeadzone || value > jdeadzone) - *type = ev_keyup; - else - *type = ev_keydown; - - if (value < -jdeadzone) - return KEY_INV_AXES + event->key; - else - return KEY_AXES + event->key; - } - - return event->key; -} - -typedef struct -{ - const char *name; - const char *menu1; - const char *menu2; -} button_strings_t; - -#define DEF_NAME_BUTTON(str) {.name = str, .menu1 = str " Button", .menu2 = "the " str " Button"} -#define DEF_NAME_SIMPLE(str) {.name = str, .menu1 = NULL, .menu2 = "the " str} -#define DEF_NAME_DPAD(a, b) {.name = "D-Pad " a, .menu1 = "D-Pad " b, .menu2 = a} - -#define PARTIAL_DEF_START [GAMEPAD_BUTTON_A] = { NULL } -#define PARTIAL_DEF_END [NUM_GAMEPAD_BUTTONS - 1] = { NULL } - -static const char *GetStringFromButtonList(const button_strings_t *names, gamepad_button_e button, gamepad_string_e type) -{ - switch (type) - { - case GAMEPAD_STRING_DEFAULT: - return names[button].name; - case GAMEPAD_STRING_MENU1: - if (names[button].menu1) - return names[button].menu1; - else - return names[button].name; - case GAMEPAD_STRING_MENU2: - if (names[button].menu2) - return names[button].menu2; - else - return names[button].name; - } - - return NULL; -} - -const char *G_GetGamepadButtonString(gamepadtype_e type, gamepad_button_e button, gamepad_string_e strtype) -{ - static const button_strings_t base_names[] = { - [GAMEPAD_BUTTON_A] = DEF_NAME_BUTTON("A"), - [GAMEPAD_BUTTON_B] = DEF_NAME_BUTTON("B"), - [GAMEPAD_BUTTON_X] = DEF_NAME_BUTTON("X"), - [GAMEPAD_BUTTON_Y] = DEF_NAME_BUTTON("Y"), - [GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("Back"), - [GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("Guide"), - [GAMEPAD_BUTTON_START] = DEF_NAME_BUTTON("Start"), - [GAMEPAD_BUTTON_LEFTSTICK] = DEF_NAME_SIMPLE("Left Stick"), - [GAMEPAD_BUTTON_RIGHTSTICK] = DEF_NAME_SIMPLE("Right Stick"), - [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_SIMPLE("Left Shoulder"), - [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_SIMPLE("Right Shoulder"), - [GAMEPAD_BUTTON_DPAD_UP] = DEF_NAME_DPAD("Up", "\x1A"), - [GAMEPAD_BUTTON_DPAD_DOWN] = DEF_NAME_DPAD("Down", "\x1B"), - [GAMEPAD_BUTTON_DPAD_LEFT] = DEF_NAME_DPAD("Left", "\x1C"), - [GAMEPAD_BUTTON_DPAD_RIGHT] = DEF_NAME_DPAD("Right", "\x1D"), - [GAMEPAD_BUTTON_PADDLE1] = DEF_NAME_SIMPLE("Paddle 1"), - [GAMEPAD_BUTTON_PADDLE2] = DEF_NAME_SIMPLE("Paddle 2"), - [GAMEPAD_BUTTON_PADDLE3] = DEF_NAME_SIMPLE("Paddle 3"), - [GAMEPAD_BUTTON_PADDLE4] = DEF_NAME_SIMPLE("Paddle 4"), - [GAMEPAD_BUTTON_TOUCHPAD] = DEF_NAME_SIMPLE("Touchpad"), - - // This one's a bit weird - // Suffix the numbers in the event SDL adds more misc buttons - [GAMEPAD_BUTTON_MISC1] = { - .name = "Misc. Button", - .menu1 = "Gamepad Misc.", - .menu2 = "the Misc. Button" - }, - }; - - button_strings_t const *names = NULL; - - if (G_GamepadTypeIsXbox(type)) - { - #define BASE_XBOX_NAMES \ - [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_SIMPLE("Left Bumper"), \ - [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_SIMPLE("Right Bumper") - - static const button_strings_t xbox_names[] = { PARTIAL_DEF_START, - BASE_XBOX_NAMES, - PARTIAL_DEF_END }; - - static const button_strings_t series_xs_names[] = { PARTIAL_DEF_START, - BASE_XBOX_NAMES, - [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Share"), - PARTIAL_DEF_END }; - - static const button_strings_t elite_names[] = { PARTIAL_DEF_START, - BASE_XBOX_NAMES, - [GAMEPAD_BUTTON_PADDLE1] = DEF_NAME_SIMPLE("P1 Paddle"), - [GAMEPAD_BUTTON_PADDLE2] = DEF_NAME_SIMPLE("P2 Paddle"), - [GAMEPAD_BUTTON_PADDLE3] = DEF_NAME_SIMPLE("P3 Paddle"), - [GAMEPAD_BUTTON_PADDLE4] = DEF_NAME_SIMPLE("P4 Paddle"), - PARTIAL_DEF_END }; - - if (type == GAMEPAD_TYPE_XBOX_SERIES_XS) // X|S controllers have a Share button - names = series_xs_names; - else if (type == GAMEPAD_TYPE_XBOX_ELITE) // Elite controller has paddles - names = elite_names; - else - names = xbox_names; - - #undef BASE_XBOX_NAMES - } - else if (G_GamepadTypeIsPlayStation(type)) - { - #define BASE_PS_NAMES \ - [GAMEPAD_BUTTON_A] = DEF_NAME_BUTTON("Cross"), \ - [GAMEPAD_BUTTON_B] = DEF_NAME_BUTTON("Circle"), \ - [GAMEPAD_BUTTON_X] = DEF_NAME_BUTTON("Square"), \ - [GAMEPAD_BUTTON_Y] = DEF_NAME_BUTTON("Triangle"), \ - [GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("Select"), \ - [GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("PS"), \ - [GAMEPAD_BUTTON_LEFTSTICK] = DEF_NAME_BUTTON("L3"), \ - [GAMEPAD_BUTTON_RIGHTSTICK] = DEF_NAME_BUTTON("R3"), \ - [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_BUTTON("L1"), \ - [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_BUTTON("R1") - - static const button_strings_t ps_names[] = { - BASE_PS_NAMES, - PARTIAL_DEF_END }; - - static const button_strings_t ps5_names[] = { - BASE_PS_NAMES, - [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Microphone"), - PARTIAL_DEF_END }; - - names = type == GAMEPAD_TYPE_PS5 ? ps5_names : ps_names; - #undef BASE_PS_NAMES - } - else if (G_GamepadTypeIsNintendoSwitch(type)) - { - static const button_strings_t switch_names[] = { PARTIAL_DEF_START, - [GAMEPAD_BUTTON_BACK] = DEF_NAME_BUTTON("-"), - [GAMEPAD_BUTTON_GUIDE] = DEF_NAME_BUTTON("HOME"), - [GAMEPAD_BUTTON_START] = DEF_NAME_BUTTON("+"), - [GAMEPAD_BUTTON_LEFTSHOULDER] = DEF_NAME_BUTTON("L"), - [GAMEPAD_BUTTON_RIGHTSHOULDER] = DEF_NAME_BUTTON("R"), - [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Capture"), - PARTIAL_DEF_END }; - - names = switch_names; - } - else if (type == GAMEPAD_TYPE_AMAZON_LUNA) - { - static const button_strings_t luna_names[] = { PARTIAL_DEF_START, - [GAMEPAD_BUTTON_MISC1] = DEF_NAME_BUTTON("Microphone"), - PARTIAL_DEF_END }; - - names = luna_names; - } - - const char *str = NULL; - - if (names) - str = GetStringFromButtonList(names, button, strtype); - if (str == NULL) - str = GetStringFromButtonList(base_names, button, strtype); - if (str) - return str; - - return "Unknown"; -} - -#undef DEF_NAME_BUTTON -#undef DEF_NAME_SIMPLE -#undef DEF_NAME_DPAD - -#undef PARTIAL_DEF_START -#undef PARTIAL_DEF_END - -typedef struct -{ - const char *name; - const char *menu1; - const char *menu2; - const char *name_inv; - const char *menu1_inv; - const char *menu2_inv; -} axis_strings_t; - -#define DEF_NAME_AXIS(str, a, inv_a, b, inv_b) {\ - str " " a, str " " b, "the " str " " b, \ - str " " inv_a, str " " inv_b, "the " str " " inv_b} -#define DEF_NAME_TRIGGER(str) {str, NULL, "the " str, NULL, NULL, NULL} -#define DEF_NAME_BUTTON(str) {str, str " Button", "the " str " Button", NULL, NULL, NULL} - -#define PARTIAL_DEF_START [GAMEPAD_AXIS_LEFTX] = { NULL } - -static const char *GetStringFromAxisList(const axis_strings_t *names, gamepad_axis_e axis, gamepad_string_e type, boolean inv) -{ - switch (type) - { - case GAMEPAD_STRING_DEFAULT: - if (inv && names[axis].name_inv) - return names[axis].name_inv; - else - return names[axis].name; - break; - case GAMEPAD_STRING_MENU1: - if (inv && names[axis].menu1_inv) - return names[axis].menu1_inv; - if (names[axis].menu1) - return names[axis].menu1; - else - return names[axis].name; - break; - case GAMEPAD_STRING_MENU2: - if (inv && names[axis].menu2_inv) - return names[axis].menu2_inv; - if (names[axis].menu2) - return names[axis].menu2; - else - return names[axis].name; - break; - } - - return NULL; -} - -const char *G_GetGamepadAxisString(gamepadtype_e type, gamepad_axis_e axis, gamepad_string_e strtype, boolean inv) -{ - static const axis_strings_t base_names[] = { - [GAMEPAD_AXIS_LEFTX] = DEF_NAME_AXIS("Left Stick", "X", "X-", "\x1D", "\x1C"), - [GAMEPAD_AXIS_LEFTY] = DEF_NAME_AXIS("Left Stick", "Y", "Y-", "\x1B", "\x1A"), - [GAMEPAD_AXIS_RIGHTX] = DEF_NAME_AXIS("Right Stick", "X", "X-", "\x1D", "\x1C"), - [GAMEPAD_AXIS_RIGHTY] = DEF_NAME_AXIS("Right Stick", "Y", "Y-", "\x1B", "\x1A"), - [GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_TRIGGER("Left Trigger"), - [GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_TRIGGER("Right Trigger") - }; - - axis_strings_t const *names = NULL; - - if (G_GamepadTypeIsPlayStation(type)) - { - static const axis_strings_t ps_names[] = { PARTIAL_DEF_START, - [GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_BUTTON("L2"), - [GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_BUTTON("R2"), - }; - - names = ps_names; - } - else if (G_GamepadTypeIsNintendoSwitch(type)) - { - static const axis_strings_t switch_names[] = { PARTIAL_DEF_START, - [GAMEPAD_AXIS_TRIGGERLEFT] = DEF_NAME_BUTTON("ZL"), - [GAMEPAD_AXIS_TRIGGERRIGHT] = DEF_NAME_BUTTON("ZR"), - }; - - names = switch_names; - } - - const char *str = NULL; - - if (names) - str = GetStringFromAxisList(names, axis, strtype, inv); - if (str == NULL) - str = GetStringFromAxisList(base_names, axis, strtype, inv); - if (str) - return str; - - return "Unknown"; -} - -#undef DEF_NAME_AXIS -#undef DEF_NAME_TRIGGER -#undef DEF_NAME_BUTTON - -#undef PARTIAL_DEF_START - typedef struct { INT32 keynum; @@ -842,17 +243,20 @@ static keyname_t keynames[] = {KEY_NUMLOCK, "numlock"}, {KEY_SCROLLLOCK, "scrolllock"}, - // satya nadella keys + // bill gates keys {KEY_LEFTWIN, "leftwin"}, {KEY_RIGHTWIN, "rightwin"}, {KEY_MENU, "menu"}, {KEY_LSHIFT, "lshift"}, {KEY_RSHIFT, "rshift"}, + {KEY_LSHIFT, "shift"}, {KEY_LCTRL, "lctrl"}, {KEY_RCTRL, "rctrl"}, + {KEY_LCTRL, "ctrl"}, {KEY_LALT, "lalt"}, {KEY_RALT, "ralt"}, + {KEY_LALT, "alt"}, // keypad keys {KEY_KPADSLASH, "keypad /"}, @@ -900,7 +304,7 @@ static keyname_t keynames[] = {'`', "TILDE"}, {KEY_PAUSE, "pause/break"}, - // virtual keys for mouse buttons and gamepad buttons + // virtual keys for mouse buttons and joystick buttons {KEY_MOUSE1+0,"mouse1"}, {KEY_MOUSE1+1,"mouse2"}, {KEY_MOUSE1+2,"mouse3"}, @@ -909,8 +313,8 @@ static keyname_t keynames[] = {KEY_MOUSE1+5,"mouse6"}, {KEY_MOUSE1+6,"mouse7"}, {KEY_MOUSE1+7,"mouse8"}, - {KEY_2MOUSE1+0,"sec_mouse1"}, - {KEY_2MOUSE1+1,"sec_mouse2"}, + {KEY_2MOUSE1+0,"sec_mouse2"}, // BP: sorry my mouse handler swap button 1 and 2 + {KEY_2MOUSE1+1,"sec_mouse1"}, {KEY_2MOUSE1+2,"sec_mouse3"}, {KEY_2MOUSE1+3,"sec_mouse4"}, {KEY_2MOUSE1+4,"sec_mouse5"}, @@ -922,48 +326,58 @@ static keyname_t keynames[] = {KEY_2MOUSEWHEELUP, "wheel 2 up"}, {KEY_2MOUSEWHEELDOWN, "wheel 2 down"}, -#define DEF_GAMEPAD_NAME(btn, name) {KEY_GAMEPAD+GAMEPAD_BUTTON_##btn, name} -#define DEF_GAMEPAD_AXIS(ax, name) \ - {KEY_AXES+GAMEPAD_AXIS_##ax, name}, \ - {KEY_INV_AXES+GAMEPAD_AXIS_##ax, name "-"} - - DEF_GAMEPAD_NAME(A, "a button"), - DEF_GAMEPAD_NAME(B, "b button"), - DEF_GAMEPAD_NAME(X, "x button"), - DEF_GAMEPAD_NAME(Y, "y button"), - - DEF_GAMEPAD_NAME(BACK, "back button"), - DEF_GAMEPAD_NAME(GUIDE, "guide button"), - DEF_GAMEPAD_NAME(START, "start button"), - DEF_GAMEPAD_NAME(LEFTSTICK, "left stick"), - DEF_GAMEPAD_NAME(RIGHTSTICK, "right stick"), - - DEF_GAMEPAD_NAME(LEFTSHOULDER, "left shoulder"), - DEF_GAMEPAD_NAME(RIGHTSHOULDER, "right shoulder"), - - DEF_GAMEPAD_NAME(DPAD_UP, "d-pad up"), - DEF_GAMEPAD_NAME(DPAD_DOWN, "d-pad down"), - DEF_GAMEPAD_NAME(DPAD_LEFT, "d-pad left"), - DEF_GAMEPAD_NAME(DPAD_RIGHT, "d-pad right"), - - DEF_GAMEPAD_NAME(MISC1, "gamepad misc 1"), - DEF_GAMEPAD_NAME(PADDLE1, "paddle 1"), - DEF_GAMEPAD_NAME(PADDLE2, "paddle 2"), - DEF_GAMEPAD_NAME(PADDLE3, "paddle 3"), - DEF_GAMEPAD_NAME(PADDLE4, "paddle 4"), - DEF_GAMEPAD_NAME(TOUCHPAD, "touchpad"), - - DEF_GAMEPAD_AXIS(LEFTX, "left stick x"), - DEF_GAMEPAD_AXIS(LEFTY, "left stick y"), - - DEF_GAMEPAD_AXIS(RIGHTX, "right stick x"), - DEF_GAMEPAD_AXIS(RIGHTY, "right stick y"), - - DEF_GAMEPAD_AXIS(TRIGGERLEFT, "left trigger"), - DEF_GAMEPAD_AXIS(TRIGGERRIGHT, "right trigger"), - -#undef DEF_GAMEPAD_NAME -#undef DEF_GAMEPAD_AXIS + {KEY_JOY1+0, "joy1"}, + {KEY_JOY1+1, "joy2"}, + {KEY_JOY1+2, "joy3"}, + {KEY_JOY1+3, "joy4"}, + {KEY_JOY1+4, "joy5"}, + {KEY_JOY1+5, "joy6"}, + {KEY_JOY1+6, "joy7"}, + {KEY_JOY1+7, "joy8"}, + {KEY_JOY1+8, "joy9"}, +#if !defined (NOMOREJOYBTN_1S) + // we use up to 32 buttons in DirectInput + {KEY_JOY1+9, "joy10"}, + {KEY_JOY1+10, "joy11"}, + {KEY_JOY1+11, "joy12"}, + {KEY_JOY1+12, "joy13"}, + {KEY_JOY1+13, "joy14"}, + {KEY_JOY1+14, "joy15"}, + {KEY_JOY1+15, "joy16"}, + {KEY_JOY1+16, "joy17"}, + {KEY_JOY1+17, "joy18"}, + {KEY_JOY1+18, "joy19"}, + {KEY_JOY1+19, "joy20"}, + {KEY_JOY1+20, "joy21"}, + {KEY_JOY1+21, "joy22"}, + {KEY_JOY1+22, "joy23"}, + {KEY_JOY1+23, "joy24"}, + {KEY_JOY1+24, "joy25"}, + {KEY_JOY1+25, "joy26"}, + {KEY_JOY1+26, "joy27"}, + {KEY_JOY1+27, "joy28"}, + {KEY_JOY1+28, "joy29"}, + {KEY_JOY1+29, "joy30"}, + {KEY_JOY1+30, "joy31"}, + {KEY_JOY1+31, "joy32"}, +#endif + // the DOS version uses Allegro's joystick support + {KEY_HAT1+0, "hatup"}, + {KEY_HAT1+1, "hatdown"}, + {KEY_HAT1+2, "hatleft"}, + {KEY_HAT1+3, "hatright"}, + {KEY_HAT1+4, "hatup2"}, + {KEY_HAT1+5, "hatdown2"}, + {KEY_HAT1+6, "hatleft2"}, + {KEY_HAT1+7, "hatright2"}, + {KEY_HAT1+8, "hatup3"}, + {KEY_HAT1+9, "hatdown3"}, + {KEY_HAT1+10, "hatleft3"}, + {KEY_HAT1+11, "hatright3"}, + {KEY_HAT1+12, "hatup4"}, + {KEY_HAT1+13, "hatdown4"}, + {KEY_HAT1+14, "hatleft4"}, + {KEY_HAT1+15, "hatright4"}, {KEY_DBLMOUSE1+0, "dblmouse1"}, {KEY_DBLMOUSE1+1, "dblmouse2"}, @@ -973,154 +387,172 @@ static keyname_t keynames[] = {KEY_DBLMOUSE1+5, "dblmouse6"}, {KEY_DBLMOUSE1+6, "dblmouse7"}, {KEY_DBLMOUSE1+7, "dblmouse8"}, - {KEY_DBL2MOUSE1+0, "dblsec_mouse1"}, - {KEY_DBL2MOUSE1+1, "dblsec_mouse2"}, + {KEY_DBL2MOUSE1+0, "dblsec_mouse2"}, // BP: sorry my mouse handler swap button 1 and 2 + {KEY_DBL2MOUSE1+1, "dblsec_mouse1"}, {KEY_DBL2MOUSE1+2, "dblsec_mouse3"}, {KEY_DBL2MOUSE1+3, "dblsec_mouse4"}, {KEY_DBL2MOUSE1+4, "dblsec_mouse5"}, {KEY_DBL2MOUSE1+5, "dblsec_mouse6"}, {KEY_DBL2MOUSE1+6, "dblsec_mouse7"}, - {KEY_DBL2MOUSE1+7, "dblsec_mouse8"} -}; + {KEY_DBL2MOUSE1+7, "dblsec_mouse8"}, -#define NUMKEYNAMES (sizeof(keynames) / sizeof(keyname_t)) - -static keyname_t displaykeynames[] = -{ - {KEY_SPACE, "Space Bar"}, - {KEY_CAPSLOCK, "Caps Lock"}, - {KEY_ENTER, "Enter"}, - {KEY_TAB, "Tab"}, - {KEY_ESCAPE, "Escape"}, - {KEY_BACKSPACE, "Backspace"}, - - {KEY_NUMLOCK, "Num Lock"}, - {KEY_SCROLLLOCK, "Scroll Lock"}, - -#ifdef _WIN32 - {KEY_LEFTWIN, "Left Windows"}, - {KEY_RIGHTWIN, "Right Windows"}, -#else - {KEY_LEFTWIN, "Left Super"}, - {KEY_RIGHTWIN, "Right Super"}, + {KEY_DBLJOY1+0, "dbljoy1"}, + {KEY_DBLJOY1+1, "dbljoy2"}, + {KEY_DBLJOY1+2, "dbljoy3"}, + {KEY_DBLJOY1+3, "dbljoy4"}, + {KEY_DBLJOY1+4, "dbljoy5"}, + {KEY_DBLJOY1+5, "dbljoy6"}, + {KEY_DBLJOY1+6, "dbljoy7"}, + {KEY_DBLJOY1+7, "dbljoy8"}, +#if !defined (NOMOREJOYBTN_1DBL) + {KEY_DBLJOY1+8, "dbljoy9"}, + {KEY_DBLJOY1+9, "dbljoy10"}, + {KEY_DBLJOY1+10, "dbljoy11"}, + {KEY_DBLJOY1+11, "dbljoy12"}, + {KEY_DBLJOY1+12, "dbljoy13"}, + {KEY_DBLJOY1+13, "dbljoy14"}, + {KEY_DBLJOY1+14, "dbljoy15"}, + {KEY_DBLJOY1+15, "dbljoy16"}, + {KEY_DBLJOY1+16, "dbljoy17"}, + {KEY_DBLJOY1+17, "dbljoy18"}, + {KEY_DBLJOY1+18, "dbljoy19"}, + {KEY_DBLJOY1+19, "dbljoy20"}, + {KEY_DBLJOY1+20, "dbljoy21"}, + {KEY_DBLJOY1+21, "dbljoy22"}, + {KEY_DBLJOY1+22, "dbljoy23"}, + {KEY_DBLJOY1+23, "dbljoy24"}, + {KEY_DBLJOY1+24, "dbljoy25"}, + {KEY_DBLJOY1+25, "dbljoy26"}, + {KEY_DBLJOY1+26, "dbljoy27"}, + {KEY_DBLJOY1+27, "dbljoy28"}, + {KEY_DBLJOY1+28, "dbljoy29"}, + {KEY_DBLJOY1+29, "dbljoy30"}, + {KEY_DBLJOY1+30, "dbljoy31"}, + {KEY_DBLJOY1+31, "dbljoy32"}, #endif + {KEY_DBLHAT1+0, "dblhatup"}, + {KEY_DBLHAT1+1, "dblhatdown"}, + {KEY_DBLHAT1+2, "dblhatleft"}, + {KEY_DBLHAT1+3, "dblhatright"}, + {KEY_DBLHAT1+4, "dblhatup2"}, + {KEY_DBLHAT1+5, "dblhatdown2"}, + {KEY_DBLHAT1+6, "dblhatleft2"}, + {KEY_DBLHAT1+7, "dblhatright2"}, + {KEY_DBLHAT1+8, "dblhatup3"}, + {KEY_DBLHAT1+9, "dblhatdown3"}, + {KEY_DBLHAT1+10, "dblhatleft3"}, + {KEY_DBLHAT1+11, "dblhatright3"}, + {KEY_DBLHAT1+12, "dblhatup4"}, + {KEY_DBLHAT1+13, "dblhatdown4"}, + {KEY_DBLHAT1+14, "dblhatleft4"}, + {KEY_DBLHAT1+15, "dblhatright4"}, - {KEY_MENU, "Menu"}, + {KEY_2JOY1+0, "sec_joy1"}, + {KEY_2JOY1+1, "sec_joy2"}, + {KEY_2JOY1+2, "sec_joy3"}, + {KEY_2JOY1+3, "sec_joy4"}, + {KEY_2JOY1+4, "sec_joy5"}, + {KEY_2JOY1+5, "sec_joy6"}, + {KEY_2JOY1+6, "sec_joy7"}, + {KEY_2JOY1+7, "sec_joy8"}, +#if !defined (NOMOREJOYBTN_2S) + // we use up to 32 buttons in DirectInput + {KEY_2JOY1+8, "sec_joy9"}, + {KEY_2JOY1+9, "sec_joy10"}, + {KEY_2JOY1+10, "sec_joy11"}, + {KEY_2JOY1+11, "sec_joy12"}, + {KEY_2JOY1+12, "sec_joy13"}, + {KEY_2JOY1+13, "sec_joy14"}, + {KEY_2JOY1+14, "sec_joy15"}, + {KEY_2JOY1+15, "sec_joy16"}, + {KEY_2JOY1+16, "sec_joy17"}, + {KEY_2JOY1+17, "sec_joy18"}, + {KEY_2JOY1+18, "sec_joy19"}, + {KEY_2JOY1+19, "sec_joy20"}, + {KEY_2JOY1+20, "sec_joy21"}, + {KEY_2JOY1+21, "sec_joy22"}, + {KEY_2JOY1+22, "sec_joy23"}, + {KEY_2JOY1+23, "sec_joy24"}, + {KEY_2JOY1+24, "sec_joy25"}, + {KEY_2JOY1+25, "sec_joy26"}, + {KEY_2JOY1+26, "sec_joy27"}, + {KEY_2JOY1+27, "sec_joy28"}, + {KEY_2JOY1+28, "sec_joy29"}, + {KEY_2JOY1+29, "sec_joy30"}, + {KEY_2JOY1+30, "sec_joy31"}, + {KEY_2JOY1+31, "sec_joy32"}, +#endif + // the DOS version uses Allegro's joystick support + {KEY_2HAT1+0, "sec_hatup"}, + {KEY_2HAT1+1, "sec_hatdown"}, + {KEY_2HAT1+2, "sec_hatleft"}, + {KEY_2HAT1+3, "sec_hatright"}, + {KEY_2HAT1+4, "sec_hatup2"}, + {KEY_2HAT1+5, "sec_hatdown2"}, + {KEY_2HAT1+6, "sec_hatleft2"}, + {KEY_2HAT1+7, "sec_hatright2"}, + {KEY_2HAT1+8, "sec_hatup3"}, + {KEY_2HAT1+9, "sec_hatdown3"}, + {KEY_2HAT1+10, "sec_hatleft3"}, + {KEY_2HAT1+11, "sec_hatright3"}, + {KEY_2HAT1+12, "sec_hatup4"}, + {KEY_2HAT1+13, "sec_hatdown4"}, + {KEY_2HAT1+14, "sec_hatleft4"}, + {KEY_2HAT1+15, "sec_hatright4"}, - {KEY_LSHIFT, "Left Shift"}, - {KEY_RSHIFT, "Right Shift"}, - {KEY_LCTRL, "Left Ctrl"}, - {KEY_RCTRL, "Right Ctrl"}, - {KEY_LALT, "Left Alt"}, - {KEY_RALT, "Right Alt"}, + {KEY_DBL2JOY1+0, "dblsec_joy1"}, + {KEY_DBL2JOY1+1, "dblsec_joy2"}, + {KEY_DBL2JOY1+2, "dblsec_joy3"}, + {KEY_DBL2JOY1+3, "dblsec_joy4"}, + {KEY_DBL2JOY1+4, "dblsec_joy5"}, + {KEY_DBL2JOY1+5, "dblsec_joy6"}, + {KEY_DBL2JOY1+6, "dblsec_joy7"}, + {KEY_DBL2JOY1+7, "dblsec_joy8"}, +#if !defined (NOMOREJOYBTN_2DBL) + {KEY_DBL2JOY1+8, "dblsec_joy9"}, + {KEY_DBL2JOY1+9, "dblsec_joy10"}, + {KEY_DBL2JOY1+10, "dblsec_joy11"}, + {KEY_DBL2JOY1+11, "dblsec_joy12"}, + {KEY_DBL2JOY1+12, "dblsec_joy13"}, + {KEY_DBL2JOY1+13, "dblsec_joy14"}, + {KEY_DBL2JOY1+14, "dblsec_joy15"}, + {KEY_DBL2JOY1+15, "dblsec_joy16"}, + {KEY_DBL2JOY1+16, "dblsec_joy17"}, + {KEY_DBL2JOY1+17, "dblsec_joy18"}, + {KEY_DBL2JOY1+18, "dblsec_joy19"}, + {KEY_DBL2JOY1+19, "dblsec_joy20"}, + {KEY_DBL2JOY1+20, "dblsec_joy21"}, + {KEY_DBL2JOY1+21, "dblsec_joy22"}, + {KEY_DBL2JOY1+22, "dblsec_joy23"}, + {KEY_DBL2JOY1+23, "dblsec_joy24"}, + {KEY_DBL2JOY1+24, "dblsec_joy25"}, + {KEY_DBL2JOY1+25, "dblsec_joy26"}, + {KEY_DBL2JOY1+26, "dblsec_joy27"}, + {KEY_DBL2JOY1+27, "dblsec_joy28"}, + {KEY_DBL2JOY1+28, "dblsec_joy29"}, + {KEY_DBL2JOY1+29, "dblsec_joy30"}, + {KEY_DBL2JOY1+30, "dblsec_joy31"}, + {KEY_DBL2JOY1+31, "dblsec_joy32"}, +#endif + {KEY_DBL2HAT1+0, "dblsec_hatup"}, + {KEY_DBL2HAT1+1, "dblsec_hatdown"}, + {KEY_DBL2HAT1+2, "dblsec_hatleft"}, + {KEY_DBL2HAT1+3, "dblsec_hatright"}, + {KEY_DBL2HAT1+4, "dblsec_hatup2"}, + {KEY_DBL2HAT1+5, "dblsec_hatdown2"}, + {KEY_DBL2HAT1+6, "dblsec_hatleft2"}, + {KEY_DBL2HAT1+7, "dblsec_hatright2"}, + {KEY_DBL2HAT1+8, "dblsec_hatup3"}, + {KEY_DBL2HAT1+9, "dblsec_hatdown3"}, + {KEY_DBL2HAT1+10, "dblsec_hatleft3"}, + {KEY_DBL2HAT1+11, "dblsec_hatright3"}, + {KEY_DBL2HAT1+12, "dblsec_hatup4"}, + {KEY_DBL2HAT1+13, "dblsec_hatdown4"}, + {KEY_DBL2HAT1+14, "dblsec_hatleft4"}, + {KEY_DBL2HAT1+15, "dblsec_hatright4"}, - {KEY_KEYPAD0, "Keypad 0"}, - {KEY_KEYPAD1, "Keypad 1"}, - {KEY_KEYPAD2, "Keypad 2"}, - {KEY_KEYPAD3, "Keypad 3"}, - {KEY_KEYPAD4, "Keypad 4"}, - {KEY_KEYPAD5, "Keypad 5"}, - {KEY_KEYPAD6, "Keypad 6"}, - {KEY_KEYPAD7, "Keypad 7"}, - {KEY_KEYPAD8, "Keypad 8"}, - {KEY_KEYPAD9, "Keypad 9"}, - {KEY_PLUSPAD, "Keypad +"}, - {KEY_MINUSPAD, "Keypad -"}, - {KEY_KPADSLASH, "Keypad /"}, - {KEY_KPADDEL, "Keypad ."}, - - {KEY_UPARROW, "Up Arrow"}, - {KEY_DOWNARROW, "Down Arrow"}, - {KEY_LEFTARROW, "Left Arrow"}, - {KEY_RIGHTARROW, "Right Arrow"}, - - {KEY_HOME, "Home"}, - {KEY_END, "End"}, - {KEY_PGUP, "Page Up"}, - {KEY_PGDN, "Page Down"}, - {KEY_INS, "Insert"}, - {KEY_DEL, "Delete"}, - - {KEY_F1, "F1"}, - {KEY_F2, "F2"}, - {KEY_F3, "F3"}, - {KEY_F4, "F4"}, - {KEY_F5, "F5"}, - {KEY_F6, "F6"}, - {KEY_F7, "F7"}, - {KEY_F8, "F8"}, - {KEY_F9, "F9"}, - {KEY_F10, "F10"}, - {KEY_F11, "F11"}, - {KEY_F12, "F12"}, - - {'`', "Tilde"}, - {KEY_PAUSE, "Pause/Break"}, - - {KEY_MOUSE1+0, "Left Mouse Button"}, - {KEY_MOUSE1+1, "Right Mouse Button"}, - {KEY_MOUSE1+2, "Middle Mouse Button"}, - {KEY_MOUSE1+3, "X1 Mouse Button"}, - {KEY_MOUSE1+4, "X2 Mouse Button"}, - - {KEY_2MOUSE1+0, "Sec. Mouse Left Button"}, - {KEY_2MOUSE1+1, "Sec. Mouse Right Button"}, - {KEY_2MOUSE1+2, "Sec. Mouse Middle Button"}, - {KEY_2MOUSE1+3, "Sec. Mouse X1 Button"}, - {KEY_2MOUSE1+4, "Sec. Mouse X2 Button"}, - - {KEY_MOUSEWHEELUP, "Mouse Wheel Up"}, - {KEY_MOUSEWHEELDOWN, "Mouse Wheel Down"}, - {KEY_2MOUSEWHEELUP, "Sec. Mouse Wheel Up"}, - {KEY_2MOUSEWHEELDOWN, "Sec. Mouse Wheel Down"}, - -#define DEF_GAMEPAD_NAME(btn, name) {KEY_GAMEPAD+GAMEPAD_BUTTON_##btn, name} -#define DEF_GAMEPAD_AXIS(ax, name) {KEY_AXES+GAMEPAD_AXIS_##ax, name} - - DEF_GAMEPAD_NAME(A, "A Button"), - DEF_GAMEPAD_NAME(B, "B Button"), - DEF_GAMEPAD_NAME(X, "X Button"), - DEF_GAMEPAD_NAME(Y, "Y Button"), - - DEF_GAMEPAD_NAME(BACK, "Back Button"), - DEF_GAMEPAD_NAME(GUIDE, "Guide Button"), - DEF_GAMEPAD_NAME(START, "Start Button"), - DEF_GAMEPAD_NAME(LEFTSTICK, "Left Stick Button"), - DEF_GAMEPAD_NAME(RIGHTSTICK, "Right Stick Button"), - - DEF_GAMEPAD_NAME(LEFTSHOULDER, "Left Shoulder"), - DEF_GAMEPAD_NAME(RIGHTSHOULDER, "Right Shoulder"), - - DEF_GAMEPAD_NAME(DPAD_UP, "D-Pad Up"), - DEF_GAMEPAD_NAME(DPAD_DOWN, "D-Pad Down"), - DEF_GAMEPAD_NAME(DPAD_LEFT, "D-Pad Left"), - DEF_GAMEPAD_NAME(DPAD_RIGHT, "D-Pad Right"), - - DEF_GAMEPAD_NAME(MISC1, "Gamepad Misc. 1"), - DEF_GAMEPAD_NAME(PADDLE1, "Paddle 1"), - DEF_GAMEPAD_NAME(PADDLE2, "Paddle 2"), - DEF_GAMEPAD_NAME(PADDLE3, "Paddle 3"), - DEF_GAMEPAD_NAME(PADDLE4, "Paddle 4"), - DEF_GAMEPAD_NAME(TOUCHPAD, "Touchpad"), - - {KEY_INV_AXES + GAMEPAD_AXIS_LEFTX, "Left Stick \x1C"}, - {KEY_AXES + GAMEPAD_AXIS_LEFTX, "Left Stick \x1D"}, - {KEY_INV_AXES + GAMEPAD_AXIS_LEFTY, "Left Stick \x1A"}, - {KEY_AXES + GAMEPAD_AXIS_LEFTY, "Left Stick \x1B"}, - {KEY_INV_AXES + GAMEPAD_AXIS_RIGHTX, "Right Stick \x1C"}, - {KEY_AXES + GAMEPAD_AXIS_RIGHTX, "Right Stick \x1D"}, - {KEY_INV_AXES + GAMEPAD_AXIS_RIGHTY, "Right Stick \x1A"}, - {KEY_AXES + GAMEPAD_AXIS_RIGHTY, "Right Stick \x1B"}, - - DEF_GAMEPAD_AXIS(TRIGGERLEFT, "Left Trigger"), - DEF_GAMEPAD_AXIS(TRIGGERRIGHT, "Right Trigger"), - -#undef DEF_GAMEPAD_NAME -#undef DEF_GAMEPAD_AXIS }; -#define NUMDISPLAYKEYNAMES (sizeof(displaykeynames) / sizeof(keyname_t)) - static const char *gamecontrolname[NUM_GAMECONTROLS] = { "nothing", // a key/button mapped to GC_NULL has no effect @@ -1168,6 +600,8 @@ static const char *gamecontrolname[NUM_GAMECONTROLS] = "custom3", }; +#define NUMKEYNAMES (sizeof (keynames)/sizeof (keyname_t)) + // // Detach any keys associated to the given game control // - pass the pointer to the gamecontrol table for the player being edited @@ -1188,7 +622,7 @@ void G_ClearAllControlKeys(void) } // -// Returns the name of a key (or virtual key for mouse and gamepad) +// Returns the name of a key (or virtual key for mouse and joy) // the input value being an keynum // const char *G_KeyNumToName(INT32 keynum) @@ -1211,44 +645,7 @@ const char *G_KeyNumToName(INT32 keynum) return keynames[j].name; // create a name for unknown keys - snprintf(keynamestr, sizeof keynamestr, "KEY%d", keynum); - return keynamestr; -} - -const char *G_GetDisplayNameForKey(INT32 keynum) -{ - static char keynamestr[32]; - - UINT32 j; - - // find a description for special keys - for (j = 0; j < NUMDISPLAYKEYNAMES; j++) - if (displaykeynames[j].keynum == keynum) - return displaykeynames[j].name; - - // return a string with the ascii char if displayable - if (keynum > ' ' && keynum <= 'z' && keynum != KEY_CONSOLE) - { - snprintf(keynamestr, sizeof keynamestr, "%c Key", toupper((char)keynum)); - return keynamestr; - } - - // unnamed mouse buttons - if (keynum >= KEY_MOUSE1 && keynum <= KEY_MOUSE1+7) - { - j = (keynum - KEY_MOUSE1) + 1; - snprintf(keynamestr, sizeof keynamestr, "Mouse Button #%d", j); - return keynamestr; - } - else if (keynum >= KEY_2MOUSE1 && keynum <= KEY_2MOUSE1+7) - { - j = (keynum - KEY_2MOUSE1) + 1; - snprintf(keynamestr, sizeof keynamestr, "Sec. Mouse Button #%d", j); - return keynamestr; - } - - // create a name for unknown keys - snprintf(keynamestr, sizeof keynamestr, "Unknown Key %d", keynum); + sprintf(keynamestr, "KEY%d", keynum); return keynamestr; } @@ -1275,36 +672,6 @@ INT32 G_KeyNameToNum(const char *keystr) return 0; } -const char *G_GamepadTypeToString(gamepadtype_e type) -{ - static const char *names[] = { - "xbox-360", - "xbox-one", - "xbox-series-xs", - "xbox-elite", - "ps3", - "ps4", - "ps5", - "switch-pro", - "switch-joy-con-grip", - "switch-joy-con-left", - "switch-joy-con-right", - "stadia", - "amazon-luna", - "steam-controller", - "virtual", - "unknown" - }; - - return names[type]; -} - -void G_InitGamepads(void) -{ - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - gamepads[i].num = i; -} - void G_DefineDefaultControls(void) { INT32 i; @@ -1372,36 +739,36 @@ void G_DefineDefaultControls(void) gamecontroldefault[i][GC_VIEWPOINTNEXT][0] = KEY_F12; // Gamepad controls -- same for both schemes - gamecontroldefault[i][GC_JUMP ][1] = GAMEPAD_KEY(A); // A - gamecontroldefault[i][GC_SPIN ][1] = GAMEPAD_KEY(X); // X - gamecontroldefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B - gamecontroldefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y - gamecontroldefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick - gamecontroldefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick - gamecontroldefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB - gamecontroldefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB - gamecontroldefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back - gamecontroldefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start - gamecontroldefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up - gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down - gamecontroldefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left - gamecontroldefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right + gamecontroldefault[i][GC_JUMP ][1] = KEY_JOY1+0; // A + gamecontroldefault[i][GC_SPIN ][1] = KEY_JOY1+2; // X + gamecontroldefault[i][GC_CUSTOM1 ][1] = KEY_JOY1+1; // B + gamecontroldefault[i][GC_CUSTOM2 ][1] = KEY_JOY1+3; // Y + gamecontroldefault[i][GC_CUSTOM3 ][1] = KEY_JOY1+8; // Left Stick + gamecontroldefault[i][GC_CENTERVIEW ][1] = KEY_JOY1+9; // Right Stick + gamecontroldefault[i][GC_WEAPONPREV ][1] = KEY_JOY1+4; // LB + gamecontroldefault[i][GC_WEAPONNEXT ][1] = KEY_JOY1+5; // RB + gamecontroldefault[i][GC_SCREENSHOT ][1] = KEY_JOY1+6; // Back + gamecontroldefault[i][GC_SYSTEMMENU ][0] = KEY_JOY1+7; // Start + gamecontroldefault[i][GC_CAMTOGGLE ][1] = KEY_HAT1+0; // D-Pad Up + gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = KEY_HAT1+1; // D-Pad Down + gamecontroldefault[i][GC_TOSSFLAG ][1] = KEY_HAT1+2; // D-Pad Left + gamecontroldefault[i][GC_SCORES ][1] = KEY_HAT1+3; // D-Pad Right - // Second player only has gamepad defaults - gamecontrolbisdefault[i][GC_JUMP ][1] = GAMEPAD_KEY(A); // A - gamecontrolbisdefault[i][GC_SPIN ][1] = GAMEPAD_KEY(X); // X - gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = GAMEPAD_KEY(B); // B - gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = GAMEPAD_KEY(Y); // Y - gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = GAMEPAD_KEY(LEFTSTICK); // Left Stick - gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = GAMEPAD_KEY(RIGHTSTICK); // Right Stick - gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = GAMEPAD_KEY(LEFTSHOULDER); // LB - gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = GAMEPAD_KEY(RIGHTSHOULDER); // RB - gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = GAMEPAD_KEY(BACK); // Back - //gamecontrolbisdefault[i][GC_SYSTEMMENU ][0] = GAMEPAD_KEY(START); // Start - gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = GAMEPAD_KEY(DPAD_UP); // D-Pad Up - gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = GAMEPAD_KEY(DPAD_DOWN); // D-Pad Down - gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = GAMEPAD_KEY(DPAD_LEFT); // D-Pad Left - //gamecontrolbisdefault[i][GC_SCORES ][1] = GAMEPAD_KEY(DPAD_RIGHT); // D-Pad Right + // Second player controls only have joypad defaults + gamecontrolbisdefault[i][GC_JUMP ][1] = KEY_2JOY1+0; // A + gamecontrolbisdefault[i][GC_SPIN ][1] = KEY_2JOY1+2; // X + gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = KEY_2JOY1+1; // B + gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = KEY_2JOY1+3; // Y + gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = KEY_2JOY1+8; // Left Stick + gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = KEY_2JOY1+9; // Right Stick + gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = KEY_2JOY1+4; // LB + gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = KEY_2JOY1+5; // RB + gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = KEY_2JOY1+6; // Back + //gamecontrolbisdefault[i][GC_SYSTEMMENU ][0] = KEY_2JOY1+7; // Start + gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = KEY_2HAT1+0; // D-Pad Up + gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = KEY_2HAT1+1; // D-Pad Down + gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = KEY_2HAT1+2; // D-Pad Left + //gamecontrolbisdefault[i][GC_SCORES ][1] = KEY_2HAT1+3; // D-Pad Right } } diff --git a/src/g_input.h b/src/g_input.h index fe623034a..400e3fd12 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -8,7 +8,7 @@ // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file g_input.h -/// \brief handle mouse/keyboard/gamepad inputs, +/// \brief handle mouse/keyboard/joystick inputs, /// maps inputs to game controls (forward, spin, jump...) #ifndef __G_INPUT__ @@ -17,178 +17,45 @@ #include "d_event.h" #include "keys.h" #include "command.h" -#include "m_fixed.h" // number of total 'button' inputs, include keyboard keys, plus virtual // keys (mousebuttons and joybuttons becomes keys) #define NUMKEYS 256 -// Max gamepads that can be used by every player -#define NUM_GAMEPADS 2 - -// Max gamepads that can be detected -#define MAX_CONNECTED_GAMEPADS 4 - -// Max mouse buttons #define MOUSEBUTTONS 8 - -typedef enum -{ - GAMEPAD_TYPE_UNKNOWN, - - GAMEPAD_TYPE_XBOX360, - GAMEPAD_TYPE_XBOXONE, - GAMEPAD_TYPE_XBOX_SERIES_XS, - GAMEPAD_TYPE_XBOX_ELITE, - - GAMEPAD_TYPE_PS3, - GAMEPAD_TYPE_PS4, - GAMEPAD_TYPE_PS5, - - GAMEPAD_TYPE_NINTENDO_SWITCH_PRO, - GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_GRIP, - GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_LEFT, - GAMEPAD_TYPE_NINTENDO_SWITCH_JOY_CON_RIGHT, - - GAMEPAD_TYPE_GOOGLE_STADIA, - GAMEPAD_TYPE_AMAZON_LUNA, - GAMEPAD_TYPE_STEAM_CONTROLLER, - - GAMEPAD_TYPE_VIRTUAL -} gamepadtype_e; - -boolean G_GamepadTypeIsXbox(gamepadtype_e type); -boolean G_GamepadTypeIsPlayStation(gamepadtype_e type); -boolean G_GamepadTypeIsNintendoSwitch(gamepadtype_e type); -boolean G_GamepadTypeIsJoyCon(gamepadtype_e type); - -const char *G_GamepadTypeToString(gamepadtype_e type); - -typedef enum -{ - GAMEPAD_BUTTON_A, - GAMEPAD_BUTTON_B, - GAMEPAD_BUTTON_X, - GAMEPAD_BUTTON_Y, - GAMEPAD_BUTTON_BACK, - GAMEPAD_BUTTON_GUIDE, - GAMEPAD_BUTTON_START, - GAMEPAD_BUTTON_LEFTSTICK, - GAMEPAD_BUTTON_RIGHTSTICK, - GAMEPAD_BUTTON_LEFTSHOULDER, - GAMEPAD_BUTTON_RIGHTSHOULDER, - GAMEPAD_BUTTON_DPAD_UP, - GAMEPAD_BUTTON_DPAD_DOWN, - GAMEPAD_BUTTON_DPAD_LEFT, - GAMEPAD_BUTTON_DPAD_RIGHT, - - // According to SDL, this button can be: - // the Xbox Series X|S share button - // the PS5 microphone button - // the Nintendo Switch (Pro or Joy-Con) capture button - // the Amazon Luna microphone button - GAMEPAD_BUTTON_MISC1, - - // Xbox Elite paddles - GAMEPAD_BUTTON_PADDLE1, - GAMEPAD_BUTTON_PADDLE2, - GAMEPAD_BUTTON_PADDLE3, - GAMEPAD_BUTTON_PADDLE4, - - // PS4/PS5 touchpad button - GAMEPAD_BUTTON_TOUCHPAD, - - NUM_GAMEPAD_BUTTONS -} gamepad_button_e; - -typedef enum -{ - GAMEPAD_AXIS_LEFTX, - GAMEPAD_AXIS_LEFTY, - GAMEPAD_AXIS_RIGHTX, - GAMEPAD_AXIS_RIGHTY, - GAMEPAD_AXIS_TRIGGERLEFT, - GAMEPAD_AXIS_TRIGGERRIGHT, - - NUM_GAMEPAD_AXES -} gamepad_axis_e; - -extern const char *const gamepad_button_names[NUM_GAMEPAD_BUTTONS + 1]; -extern const char *const gamepad_axis_names[NUM_GAMEPAD_AXES + 1]; - -// Haptic effects -typedef struct -{ - fixed_t large_magnitude; // Magnitude of the large motor - fixed_t small_magnitude; // Magnitude of the small motor - tic_t duration; // The total duration of the effect, in tics -} haptic_t; - -// Gamepad info for each player on the system -typedef struct -{ - // Gamepad index - UINT8 num; - - // Gamepad is connected and being used by a player - boolean connected; - - // What kind of controller this is (Xbox 360, DualShock, Joy-Con, etc.) - gamepadtype_e type; - - // Treat this gamepad's axes as if it they were buttons - boolean digital; - - struct { - boolean supported; // Gamepad can rumble - boolean active; // Rumble is active - boolean paused; // Rumble is paused - haptic_t data; // Current haptic effect status - } rumble; - - UINT8 buttons[NUM_GAMEPAD_BUTTONS]; // Current state of all buttons - INT16 axes[NUM_GAMEPAD_AXES]; // Current state of all axes -} gamepad_t; - -void G_InitGamepads(void); - -typedef enum -{ - GAMEPAD_STRING_DEFAULT, // A - GAMEPAD_STRING_MENU1, // A Button - GAMEPAD_STRING_MENU2 // the A Button -} gamepad_string_e; - -const char *G_GetGamepadButtonString(gamepadtype_e type, gamepad_button_e button, gamepad_string_e strtype); -const char *G_GetGamepadAxisString(gamepadtype_e type, gamepad_axis_e button, gamepad_string_e strtype, boolean inv); - -extern gamepad_t gamepads[NUM_GAMEPADS]; +#define JOYBUTTONS 32 // 32 buttons +#define JOYHATS 4 // 4 hats +#define JOYAXISSET 4 // 4 Sets of 2 axises // -// mouse and gamepad buttons are handled as 'virtual' keys +// mouse and joystick buttons are handled as 'virtual' keys // typedef enum { KEY_MOUSE1 = NUMKEYS, - KEY_GAMEPAD = KEY_MOUSE1 + MOUSEBUTTONS, - KEY_AXES = KEY_GAMEPAD + NUM_GAMEPAD_BUTTONS, // Sure, why not. - KEY_INV_AXES = KEY_AXES + NUM_GAMEPAD_AXES, + KEY_JOY1 = KEY_MOUSE1 + MOUSEBUTTONS, + KEY_HAT1 = KEY_JOY1 + JOYBUTTONS, - KEY_DBLMOUSE1 = KEY_INV_AXES + NUM_GAMEPAD_AXES, // double clicks + KEY_DBLMOUSE1 =KEY_HAT1 + JOYHATS*4, // double clicks + KEY_DBLJOY1 = KEY_DBLMOUSE1 + MOUSEBUTTONS, + KEY_DBLHAT1 = KEY_DBLJOY1 + JOYBUTTONS, - KEY_2MOUSE1 = KEY_DBLMOUSE1 + MOUSEBUTTONS, - KEY_DBL2MOUSE1 = KEY_2MOUSE1 + MOUSEBUTTONS, + KEY_2MOUSE1 = KEY_DBLHAT1 + JOYHATS*4, + KEY_2JOY1 = KEY_2MOUSE1 + MOUSEBUTTONS, + KEY_2HAT1 = KEY_2JOY1 + JOYBUTTONS, - KEY_MOUSEWHEELUP = KEY_DBL2MOUSE1 + MOUSEBUTTONS, - KEY_MOUSEWHEELDOWN, - KEY_2MOUSEWHEELUP, - KEY_2MOUSEWHEELDOWN, + KEY_DBL2MOUSE1 = KEY_2HAT1 + JOYHATS*4, + KEY_DBL2JOY1 = KEY_DBL2MOUSE1 + MOUSEBUTTONS, + KEY_DBL2HAT1 = KEY_DBL2JOY1 + JOYBUTTONS, - NUMINPUTS + KEY_MOUSEWHEELUP = KEY_DBL2HAT1 + JOYHATS*4, + KEY_MOUSEWHEELDOWN = KEY_MOUSEWHEELUP + 1, + KEY_2MOUSEWHEELUP = KEY_MOUSEWHEELDOWN + 1, + KEY_2MOUSEWHEELDOWN = KEY_2MOUSEWHEELUP + 1, + + NUMINPUTS = KEY_2MOUSEWHEELDOWN + 1, } key_input_e; -#define GAMEPAD_KEY(key) (KEY_GAMEPAD + GAMEPAD_BUTTON_##key) - typedef enum { GC_NULL = 0, // a key/button mapped to GC_NULL has no effect @@ -274,22 +141,19 @@ typedef struct extern mouse_t mouse; extern mouse_t mouse2; +extern INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET]; + // current state of the keys: true if pushed extern UINT8 gamekeydown[NUMINPUTS]; // two key codes (or virtual key) per game control extern INT32 gamecontrol[NUM_GAMECONTROLS][2]; extern INT32 gamecontrolbis[NUM_GAMECONTROLS][2]; // secondary splitscreen player - -// default control storage, use 0 (gcs_custom) for memory retention -extern INT32 gamecontroldefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; +extern INT32 gamecontroldefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; // default control storage, use 0 (gcs_custom) for memory retention extern INT32 gamecontrolbisdefault[num_gamecontrolschemes][NUM_GAMECONTROLS][2]; - -boolean G_PlayerInputDown(UINT8 which, gamecontrols_e gc); -boolean G_CheckDigitalPlayerInput(UINT8 which, gamecontrols_e gc); - -SINT8 G_PlayerInputIsAnalog(UINT8 which, gamecontrols_e gc, UINT8 settings); -INT16 G_GetAnalogPlayerInput(UINT8 which, gamecontrols_e gc, UINT8 settings); +#define PLAYER1INPUTDOWN(gc) (gamekeydown[gamecontrol[gc][0]] || gamekeydown[gamecontrol[gc][1]]) +#define PLAYER2INPUTDOWN(gc) (gamekeydown[gamecontrolbis[gc][0]] || gamekeydown[gamecontrolbis[gc][1]]) +#define PLAYERINPUTDOWN(p, gc) ((p) == 2 ? PLAYER2INPUTDOWN(gc) : PLAYER1INPUTDOWN(gc)) #define num_gcl_tutorial_check 6 #define num_gcl_tutorial_used 8 @@ -317,37 +181,13 @@ extern const INT32 gcl_jump_spin[num_gcl_jump_spin]; // remaps the input event to a game control. void G_MapEventsToControls(event_t *ev); -boolean G_RumbleSupported(UINT8 which); -boolean G_RumbleGamepad(UINT8 which, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration); -void G_StopGamepadRumble(UINT8 which); - -fixed_t G_GetLargeMotorFreq(UINT8 which); -fixed_t G_GetSmallMotorFreq(UINT8 which); -boolean G_GetGamepadRumblePaused(UINT8 which); -boolean G_SetLargeMotorFreq(UINT8 which, fixed_t freq); -boolean G_SetSmallMotorFreq(UINT8 which, fixed_t freq); -void G_SetGamepadRumblePaused(UINT8 which, boolean pause); - -INT16 G_GamepadAxisEventValue(UINT8 which, INT16 value); -INT16 G_GetGamepadAxisValue(UINT8 which, gamepad_axis_e axis); -fixed_t G_GetAdjustedGamepadAxis(UINT8 which, gamepad_axis_e axis, boolean applyDeadzone); - -UINT16 G_GetGamepadDeadZone(UINT8 which); -UINT16 G_GetGamepadDigitalDeadZone(UINT8 which); -INT32 G_BasicDeadZoneCalculation(INT32 magnitude, const UINT16 jdeadzone); - -INT32 G_RemapGamepadEvent(event_t *event, INT32 *type); - // returns the name of a key const char *G_KeyNumToName(INT32 keynum); INT32 G_KeyNameToNum(const char *keystr); -const char *G_GetDisplayNameForKey(INT32 keynum); - // detach any keys associated to the given game control void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control); void G_ClearAllControlKeys(void); - void Command_Setcontrol_f(void); void Command_Setcontrol2_f(void); void G_DefineDefaultControls(void); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 805aa694f..c037abcd7 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -877,7 +877,7 @@ void HU_Ticker(void) hu_tick++; hu_tick &= 7; // currently only to blink chat input cursor - if (G_PlayerInputDown(0, GC_SCORES)) + if (PLAYER1INPUTDOWN(GC_SCORES)) hu_showscores = !chat_on; else hu_showscores = false; @@ -1052,6 +1052,26 @@ boolean HU_Responder(event_t *ev) // only KeyDown events now... + /*// Shoot, to prevent P1 chatting from ruining the game for everyone else, it's either: + // A. completely disallow opening chat entirely in online splitscreen + // or B. iterate through all controls to make sure it's bound to player 1 before eating + // You can see which one I chose. + // (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...) + // (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...) + + if (ev->key >= KEY_MOUSE1) + { + INT32 i; + for (i = 0; i < NUM_GAMECONTROLS; i++) + { + if (gamecontrol[i][0] == ev->key || gamecontrol[i][1] == ev->key) + break; + } + + if (i == NUM_GAMECONTROLS) + return false; + }*/ //We don't actually care about that unless we get splitscreen netgames. :V + #ifndef NONET c = (INT32)ev->key; diff --git a/src/i_gamepad.h b/src/i_gamepad.h deleted file mode 100644 index 730109b54..000000000 --- a/src/i_gamepad.h +++ /dev/null @@ -1,58 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 i_gamepad.h -/// \brief Gamepads - -#ifndef __I_GAMEPAD_H__ -#define __I_GAMEPAD_H__ - -#include "g_input.h" -#include "p_haptic.h" - -// So m_menu knows whether to store cv_usegamepad value or string -#define GAMEPAD_HOTPLUG - -// Value range for axes -#define JOYAXISRANGE INT16_MAX -#define OLDJOYAXISRANGE 1023 - -// Starts all gamepads -void I_InitGamepads(void); - -// Returns the number of gamepads on the system -INT32 I_NumGamepads(void); - -// Changes a gamepad's device -void I_ChangeGamepad(UINT8 which); - -// Toggles a gamepad's digital axis setting -void I_SetGamepadDigital(UINT8 which, boolean enable); - -// Shuts down all gamepads -void I_ShutdownGamepads(void); - -// Returns the name of a gamepad from its index -const char *I_GetGamepadName(INT32 joyindex); - -// Gamepad rumble interface -boolean I_RumbleSupported(void); -boolean I_RumbleGamepad(UINT8 which, const haptic_t *effect); - -boolean I_GetGamepadRumblePaused(UINT8 which); - -boolean I_SetGamepadLargeMotorFreq(UINT8 which, fixed_t freq); -boolean I_SetGamepadSmallMotorFreq(UINT8 which, fixed_t freq); -void I_SetGamepadRumblePaused(UINT8 which, boolean pause); - -boolean I_GetGamepadRumbleSupported(UINT8 which); - -void I_StopGamepadRumble(UINT8 which); - -#endif // __I_GAMEPAD_H__ diff --git a/src/i_joy.h b/src/i_joy.h new file mode 100644 index 000000000..27584cea6 --- /dev/null +++ b/src/i_joy.h @@ -0,0 +1,58 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2022 by Sonic Team Junior. +// +// 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 i_joy.h +/// \brief share joystick information with game control code + +#ifndef __I_JOY_H__ +#define __I_JOY_H__ + +#include "g_input.h" + +/*! + \brief -JOYAXISRANGE to +JOYAXISRANGE for each axis + + (1024-1) so we can do a right shift instead of division + (doesnt matter anyway, just give enough precision) + a gamepad will return -1, 0, or 1 in the event data + an analog type joystick will return a value + from -JOYAXISRANGE to +JOYAXISRANGE for each axis +*/ + +#define JOYAXISRANGE 1023 + +// detect a bug if we increase JOYBUTTONS above DIJOYSTATE's number of buttons +#if (JOYBUTTONS > 64) +"JOYBUTTONS is greater than INT64 bits can hold" +#endif + +/** \brief The struct JoyType_s + + share some joystick information (maybe 2 for splitscreen), to the game input code, + actually, we need to know if it is a gamepad or analog controls +*/ + +struct JoyType_s +{ + /*! if true, we MUST Poll() to get new joystick data, + that is: we NEED the DIRECTINPUTDEVICE2 ! (watchout NT compatibility) */ + INT32 bJoyNeedPoll; + /*! this joystick is a gamepad, read: digital axes + if FALSE, interpret the joystick event data as JOYAXISRANGE (see above) */ + INT32 bGamepadStyle; + +}; +typedef struct JoyType_s JoyType_t; +/** \brief Joystick info + for palyer 1 and 2's joystick/gamepad +*/ + +extern JoyType_t Joystick, Joystick2; + +#endif // __I_JOY_H__ diff --git a/src/i_system.h b/src/i_system.h index f3ca5aac5..7153aa735 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -101,6 +101,90 @@ ticcmd_t *I_BaseTiccmd2(void); */ void I_Quit(void) FUNCNORETURN; +typedef enum +{ + EvilForce = -1, + //Constant + ConstantForce = 0, + //Ramp + RampForce, + //Periodics + SquareForce, + SineForce, + TriangleForce, + SawtoothUpForce, + SawtoothDownForce, + //MAX + NumberofForces, +} FFType; + +typedef struct JoyFF_s +{ + INT32 ForceX; ///< The X of the Force's Vel + INT32 ForceY; ///< The Y of the Force's Vel + //All + UINT32 Duration; ///< The total duration of the effect, in microseconds + INT32 Gain; //< /The gain to be applied to the effect, in the range from 0 through 10,000. + //All, CONSTANTFORCE -10,000 to 10,000 + INT32 Magnitude; ///< Magnitude of the effect, in the range from 0 through 10,000. + //RAMPFORCE + INT32 Start; ///< Magnitude at the start of the effect, in the range from -10,000 through 10,000. + INT32 End; ///< Magnitude at the end of the effect, in the range from -10,000 through 10,000. + //PERIODIC + INT32 Offset; ///< Offset of the effect. + UINT32 Phase; ///< Position in the cycle of the periodic effect at which playback begins, in the range from 0 through 35,999 + UINT32 Period; ///< Period of the effect, in microseconds. +} JoyFF_t; + +/** \brief Forcefeedback for the first joystick + + \param Type what kind of Effect + \param Effect Effect Info + + \return void +*/ + +void I_Tactile(FFType Type, const JoyFF_t *Effect); + +/** \brief Forcefeedback for the second joystick + + \param Type what kind of Effect + \param Effect Effect Info + + \return void +*/ +void I_Tactile2(FFType Type, const JoyFF_t *Effect); + +/** \brief to set up the first joystick scale +*/ +void I_JoyScale(void); + +/** \brief to set up the second joystick scale +*/ +void I_JoyScale2(void); + +// Called by D_SRB2Main. + +/** \brief to startup the first joystick +*/ +void I_InitJoystick(void); + +/** \brief to startup the second joystick +*/ +void I_InitJoystick2(void); + +/** \brief return the number of joystick on the system +*/ +INT32 I_NumJoys(void); + +/** \brief The *I_GetJoyName function + + \param joyindex which joystick + + \return joystick name +*/ +const char *I_GetJoyName(INT32 joyindex); + #ifndef NOMUMBLE #include "p_mobj.h" // mobj_t #include "s_sound.h" // listener_t @@ -209,7 +293,15 @@ const CPUInfoFlags *I_CPUInfo(void); */ const char *I_LocateWad(void); -/** \brief Mice events +/** \brief First Joystick's events +*/ +void I_GetJoystickEvents(void); + +/** \brief Second Joystick's events +*/ +void I_GetJoystick2Events(void); + +/** \brief Mouses events */ void I_GetMouseEvents(void); diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c index 88efc3490..1710b0355 100644 --- a/src/lua_inputlib.c +++ b/src/lua_inputlib.c @@ -15,7 +15,6 @@ #include "g_game.h" #include "hu_stuff.h" #include "i_system.h" -#include "i_gamepad.h" #include "lua_script.h" #include "lua_libs.h" @@ -31,7 +30,7 @@ static int lib_gameControlDown(lua_State *L) int i = luaL_checkinteger(L, 1); if (i < 0 || i >= NUM_GAMECONTROLS) return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); - lua_pushinteger(L, G_PlayerInputDown(0, i)); + lua_pushinteger(L, PLAYER1INPUTDOWN(i)); return 1; } @@ -40,7 +39,7 @@ static int lib_gameControl2Down(lua_State *L) int i = luaL_checkinteger(L, 1); if (i < 0 || i >= NUM_GAMECONTROLS) return luaL_error(L, "GC_* constant %d out of range (0 - %d)", i, NUM_GAMECONTROLS-1); - lua_pushinteger(L, G_PlayerInputDown(1, i)); + lua_pushinteger(L, PLAYER2INPUTDOWN(i)); return 1; } @@ -67,14 +66,14 @@ static int lib_gameControl2ToKeyNum(lua_State *L) static int lib_joyAxis(lua_State *L) { int i = luaL_checkinteger(L, 1); - lua_pushinteger(L, G_JoyAxis(0, i) / 32); + lua_pushinteger(L, JoyAxis(i)); return 1; } static int lib_joy2Axis(lua_State *L) { int i = luaL_checkinteger(L, 1); - lua_pushinteger(L, G_JoyAxis(1, i) / 32); + lua_pushinteger(L, Joy2Axis(i)); return 1; } diff --git a/src/m_cheat.c b/src/m_cheat.c index f94377450..89c8009ae 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -199,41 +199,39 @@ static UINT8 cht_CheckCheat(cheatseq_t *cht, char key) boolean cht_Responder(event_t *ev) { - UINT8 ch = 0; + UINT8 ret = 0, ch = 0; + if (ev->type != ev_keydown) + return false; - if (ev->type == ev_gamepad_down) + if (ev->key > 0xFF) { + // map some fake (joy) inputs into keys + // map joy inputs into keys switch (ev->key) { - case GAMEPAD_BUTTON_DPAD_UP: + case KEY_JOY1: + case KEY_JOY1 + 2: + ch = KEY_ENTER; + break; + case KEY_HAT1: ch = KEY_UPARROW; break; - case GAMEPAD_BUTTON_DPAD_DOWN: + case KEY_HAT1 + 1: ch = KEY_DOWNARROW; break; - case GAMEPAD_BUTTON_DPAD_LEFT: + case KEY_HAT1 + 2: ch = KEY_LEFTARROW; break; - case GAMEPAD_BUTTON_DPAD_RIGHT: + case KEY_HAT1 + 3: ch = KEY_RIGHTARROW; break; - case GAMEPAD_BUTTON_START: - ch = KEY_ENTER; - break; default: // no mapping return false; } } - else if (ev->type == ev_keydown) - { - if (ev->key > 0xFF) - return false; - + else ch = (UINT8)ev->key; - } - - UINT8 ret = 0; ret += cht_CheckCheat(&cheat_ultimate, (char)ch); ret += cht_CheckCheat(&cheat_ultimate_joy, (char)ch); diff --git a/src/m_menu.c b/src/m_menu.c index 51d13df98..82d078062 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -41,8 +41,6 @@ #include "v_video.h" #include "i_video.h" -#include "i_gamepad.h" -#include "g_input.h" #include "keys.h" #include "z_zone.h" #include "w_wad.h" @@ -64,6 +62,8 @@ #include "i_sound.h" #include "fastcmp.h" +#include "i_joy.h" // for joystick menu controls + #include "p_saveg.h" // Only for NEWSKINSAVES // Condition Sets @@ -72,6 +72,13 @@ // And just some randomness for the exits. #include "m_random.h" +#if defined(HAVE_SDL) +#include "SDL.h" +#if SDL_VERSION_ATLEAST(2,0,0) +#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG +#endif +#endif + #if defined (__GNUC__) && (__GNUC__ >= 4) #define FIXUPO0 #endif @@ -141,12 +148,7 @@ typedef enum levellist_mode_t levellistmode = LLM_CREATESERVER; UINT8 maplistoption = 0; -static struct -{ - char name[29]; - INT32 index; -} gamepadInfo[MAX_CONNECTED_GAMEPADS + 1]; - +static char joystickInfo[MAX_JOYSTICKS+1][29]; #ifndef NONET static UINT32 serverlistpage; #endif @@ -161,7 +163,6 @@ static INT16 itemOn = 1; // menu item skull is on, Hack by Tails 09-18-2002 static INT16 skullAnimCounter = 10; // skull animation counter static boolean setupcontrols_secondaryplayer; -static consvar_t *setupcontrols_joycvar = NULL; static INT32 (*setupcontrols)[2]; // pointer to the gamecontrols of the player being edited // shhh... what am I doing... nooooo! @@ -310,17 +311,17 @@ menu_t MP_MainDef; menu_t OP_ChangeControlsDef; menu_t OP_MPControlsDef, OP_MiscControlsDef; menu_t OP_P1ControlsDef, OP_P2ControlsDef, OP_MouseOptionsDef; -menu_t OP_Mouse2OptionsDef, OP_Gamepad1Def, OP_Gamepad2Def; +menu_t OP_Mouse2OptionsDef, OP_Joystick1Def, OP_Joystick2Def; menu_t OP_CameraOptionsDef, OP_Camera2OptionsDef; menu_t OP_PlaystyleDef; static void M_VideoModeMenu(INT32 choice); static void M_Setup1PControlsMenu(INT32 choice); static void M_Setup2PControlsMenu(INT32 choice); -static void M_Setup1PGamepadMenu(INT32 choice); -static void M_Setup2PGamepadMenu(INT32 choice); +static void M_Setup1PJoystickMenu(INT32 choice); +static void M_Setup2PJoystickMenu(INT32 choice); static void M_Setup1PPlaystyleMenu(INT32 choice); static void M_Setup2PPlaystyleMenu(INT32 choice); -static void M_AssignGamepad(INT32 choice); +static void M_AssignJoystick(INT32 choice); static void M_ChangeControl(INT32 choice); // Video & Sound @@ -374,8 +375,7 @@ static void M_DrawSetupChoosePlayerMenu(void); static void M_DrawControlsDefMenu(void); static void M_DrawCameraOptionsMenu(void); static void M_DrawPlaystyleMenu(void); -static void M_DrawGamepadMenu(void); -static void M_DrawControlConfigMenu(void); +static void M_DrawControl(void); static void M_DrawMainVideoMenu(void); static void M_DrawVideoMode(void); static void M_DrawColorMenu(void); @@ -386,7 +386,7 @@ static void M_DrawConnectMenu(void); static void M_DrawMPMainMenu(void); static void M_DrawRoomMenu(void); #endif -static void M_DrawGamepadList(void); +static void M_DrawJoystick(void); static void M_DrawSetupMultiPlayerMenu(void); // Handling functions @@ -1084,7 +1084,7 @@ static menuitem_t OP_P1ControlsMenu[] = { {IT_CALL | IT_STRING, NULL, "Control Configuration...", M_Setup1PControlsMenu, 10}, {IT_SUBMENU | IT_STRING, NULL, "Mouse Options...", &OP_MouseOptionsDef, 20}, - {IT_SUBMENU | IT_STRING, NULL, "Gamepad Options...", &OP_Gamepad1Def , 30}, + {IT_SUBMENU | IT_STRING, NULL, "Gamepad Options...", &OP_Joystick1Def , 30}, {IT_SUBMENU | IT_STRING, NULL, "Camera Options...", &OP_CameraOptionsDef, 50}, @@ -1096,7 +1096,7 @@ static menuitem_t OP_P2ControlsMenu[] = { {IT_CALL | IT_STRING, NULL, "Control Configuration...", M_Setup2PControlsMenu, 10}, {IT_SUBMENU | IT_STRING, NULL, "Second Mouse Options...", &OP_Mouse2OptionsDef, 20}, - {IT_SUBMENU | IT_STRING, NULL, "Second Gamepad Options...", &OP_Gamepad2Def , 30}, + {IT_SUBMENU | IT_STRING, NULL, "Second Gamepad Options...", &OP_Joystick2Def , 30}, {IT_SUBMENU | IT_STRING, NULL, "Camera Options...", &OP_Camera2OptionsDef, 50}, @@ -1160,43 +1160,43 @@ static menuitem_t OP_ChangeControlsMenu[] = {IT_CALL | IT_STRING2, NULL, "Custom Action 3", M_ChangeControl, GC_CUSTOM3 }, }; -static menuitem_t OP_Gamepad1Menu[] = +static menuitem_t OP_Joystick1Menu[] = { - {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup1PGamepadMenu, 10}, - {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis[0] , 30}, - {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis[0] , 40}, - {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis[0] , 50}, - {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis[0] , 60}, - {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis[0] , 70}, - {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis[0] , 80}, - {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis[0] , 90}, - {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis[0] ,100}, + {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup1PJoystickMenu, 10}, + {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis , 30}, + {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis , 40}, + {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis , 50}, + {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis , 60}, + {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis , 70}, + {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis , 80}, + {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis , 90}, + {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis ,100}, {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook, 120}, {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook, 130}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[0], 140}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[0], 150}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone, 140}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone, 150}, }; -static menuitem_t OP_Gamepad2Menu[] = +static menuitem_t OP_Joystick2Menu[] = { - {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup2PGamepadMenu, 10}, - {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis[1] , 30}, - {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis[1] , 40}, - {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis[1] , 50}, - {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis[1] , 60}, - {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis[1] , 70}, - {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis[1] , 80}, - {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis[1] , 90}, - {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis[1] ,100}, + {IT_STRING | IT_CALL, NULL, "Select Gamepad...", M_Setup2PJoystickMenu, 10}, + {IT_STRING | IT_CVAR, NULL, "Move \x17 Axis" , &cv_moveaxis2 , 30}, + {IT_STRING | IT_CVAR, NULL, "Move \x18 Axis" , &cv_sideaxis2 , 40}, + {IT_STRING | IT_CVAR, NULL, "Camera \x17 Axis" , &cv_lookaxis2 , 50}, + {IT_STRING | IT_CVAR, NULL, "Camera \x18 Axis" , &cv_turnaxis2 , 60}, + {IT_STRING | IT_CVAR, NULL, "Jump Axis" , &cv_jumpaxis2 , 70}, + {IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis2 , 80}, + {IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis2 , 90}, + {IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis2 ,100}, {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook2,120}, {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone[1],140}, - {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone[1],150}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Analog Deadzone", &cv_deadzone2,140}, + {IT_STRING | IT_CVAR | IT_CV_FLOATSLIDER, NULL, "Digital Deadzone", &cv_digitaldeadzone2,150}, }; -static menuitem_t OP_GamepadSetMenu[MAX_CONNECTED_GAMEPADS + 1]; +static menuitem_t OP_JoystickSetMenu[1+MAX_JOYSTICKS]; static menuitem_t OP_MouseOptionsMenu[] = { @@ -2093,21 +2093,21 @@ menu_t OP_Mouse2OptionsDef = DEFAULTMENUSTYLE( MTREE3(MN_OP_MAIN, MN_OP_P2CONTROLS, MN_OP_P2MOUSE), "M_CONTRO", OP_Mouse2OptionsMenu, &OP_P2ControlsDef, 35, 30); -menu_t OP_Gamepad1Def = GAMEPADMENUSTYLE( +menu_t OP_Joystick1Def = DEFAULTMENUSTYLE( MTREE3(MN_OP_MAIN, MN_OP_P1CONTROLS, MN_OP_P1JOYSTICK), - "M_CONTRO", OP_Gamepad1Menu, &OP_P1ControlsDef, 50, 30); -menu_t OP_Gamepad2Def = GAMEPADMENUSTYLE( + "M_CONTRO", OP_Joystick1Menu, &OP_P1ControlsDef, 50, 30); +menu_t OP_Joystick2Def = DEFAULTMENUSTYLE( MTREE3(MN_OP_MAIN, MN_OP_P2CONTROLS, MN_OP_P2JOYSTICK), - "M_CONTRO", OP_Gamepad2Menu, &OP_P2ControlsDef, 50, 30); + "M_CONTRO", OP_Joystick2Menu, &OP_P2ControlsDef, 50, 30); -menu_t OP_GamepadSetDef = +menu_t OP_JoystickSetDef = { MTREE4(MN_OP_MAIN, 0, 0, MN_OP_JOYSTICKSET), // second and third level set on runtime "M_CONTRO", - sizeof (OP_GamepadSetMenu)/sizeof (menuitem_t), - &OP_Gamepad1Def, - OP_GamepadSetMenu, - M_DrawGamepadList, + sizeof (OP_JoystickSetMenu)/sizeof (menuitem_t), + &OP_Joystick1Def, + OP_JoystickSetMenu, + M_DrawJoystick, 60, 40, 0, NULL @@ -3198,28 +3198,13 @@ static void Command_Manual_f(void) itemOn = 0; } -static INT32 RemapGamepadButton(event_t *ev) -{ - switch (ev->key) - { - case GAMEPAD_BUTTON_A: return KEY_ENTER; - case GAMEPAD_BUTTON_B: return KEY_ESCAPE; - case GAMEPAD_BUTTON_X: return KEY_BACKSPACE; - case GAMEPAD_BUTTON_DPAD_UP: return KEY_UPARROW; - case GAMEPAD_BUTTON_DPAD_DOWN: return KEY_DOWNARROW; - case GAMEPAD_BUTTON_DPAD_LEFT: return KEY_LEFTARROW; - case GAMEPAD_BUTTON_DPAD_RIGHT: return KEY_RIGHTARROW; - } - - return KEY_GAMEPAD + ev->key; -} - // // M_Responder // boolean M_Responder(event_t *ev) { INT32 ch = -1; +// INT32 i; static tic_t joywait = 0, mousewait = 0; static INT32 pjoyx = 0, pjoyy = 0; static INT32 pmousex = 0, pmousey = 0; @@ -3237,8 +3222,6 @@ boolean M_Responder(event_t *ev) if (CON_Ready() && gamestate != GS_WAITINGPLAYERS) return false; - boolean useEventHandler = false; - if (noFurtherInput) { // Ignore input after enter/escape/other buttons @@ -3247,69 +3230,80 @@ boolean M_Responder(event_t *ev) } else if (menuactive) { - if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) - useEventHandler = currentMenu->menuitems[itemOn].alphaKey == MM_EVENTHANDLER; - if (ev->type == ev_keydown) { keydown++; ch = ev->key; - // added 5-2-98 remap virtual keys (mouse buttons) + // added 5-2-98 remap virtual keys (mouse & joystick buttons) switch (ch) { case KEY_MOUSE1: + case KEY_JOY1: ch = KEY_ENTER; break; + case KEY_JOY1 + 3: + ch = 'n'; + break; case KEY_MOUSE1 + 1: + case KEY_JOY1 + 1: ch = KEY_ESCAPE; break; + case KEY_JOY1 + 2: + ch = KEY_BACKSPACE; + break; + case KEY_HAT1: + ch = KEY_UPARROW; + break; + case KEY_HAT1 + 1: + ch = KEY_DOWNARROW; + break; + case KEY_HAT1 + 2: + ch = KEY_LEFTARROW; + break; + case KEY_HAT1 + 3: + ch = KEY_RIGHTARROW; + break; } } - else if (ev->type == ev_gamepad_down) + else if (ev->type == ev_joystick && ev->key == 0 && joywait < I_GetTime()) { - keydown++; - ch = RemapGamepadButton(ev); - } - else if (ev->type == ev_gamepad_axis && ev->which == 0 && joywait < I_GetTime()) - { - const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(0); - const INT16 value = G_GamepadAxisEventValue(0, ev->x); - - if (ev->key == GAMEPAD_AXIS_LEFTY) + const INT32 jdeadzone = (JOYAXISRANGE * cv_digitaldeadzone.value) / FRACUNIT; + if (ev->y != INT32_MAX) { - if (abs(value) > jdeadzone) + if (Joystick.bGamepadStyle || abs(ev->y) > jdeadzone) { - if (value < 0 && pjoyy >= 0) + if (ev->y < 0 && pjoyy >= 0) { ch = KEY_UPARROW; joywait = I_GetTime() + NEWTICRATE/7; } - else if (value > 0 && pjoyy <= 0) + else if (ev->y > 0 && pjoyy <= 0) { ch = KEY_DOWNARROW; joywait = I_GetTime() + NEWTICRATE/7; } - pjoyy = value; + pjoyy = ev->y; } else pjoyy = 0; } - else if (ev->key == GAMEPAD_AXIS_LEFTX) + + if (ev->x != INT32_MAX) { - if (abs(value) > jdeadzone) + if (Joystick.bGamepadStyle || abs(ev->x) > jdeadzone) { - if (value < 0 && pjoyx >= 0) + if (ev->x < 0 && pjoyx >= 0) { ch = KEY_LEFTARROW; joywait = I_GetTime() + NEWTICRATE/17; } - else if (value > 0 && pjoyx <= 0) + else if (ev->x > 0 && pjoyx <= 0) { ch = KEY_RIGHTARROW; joywait = I_GetTime() + NEWTICRATE/17; } - pjoyx = value; + pjoyx = ev->x; } else pjoyx = 0; @@ -3345,29 +3339,13 @@ boolean M_Responder(event_t *ev) pmousex = lastx += 30; } } - else if (ev->type == ev_keyup || ev->type == ev_gamepad_up) // Preserve event for other responders + else if (ev->type == ev_keyup) // Preserve event for other responders keydown = 0; } - // Preserve event for other responders - else if (ev->type == ev_keydown || ev->type == ev_gamepad_down) - { + else if (ev->type == ev_keydown) // Preserve event for other responders ch = ev->key; - if (ev->type == ev_gamepad_down) - ch += KEY_GAMEPAD; - } - else if (ev->type == ev_gamepad_axis) - { - const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(0); - const INT16 value = G_GamepadAxisEventValue(0, ev->x); - - if (value > jdeadzone) - ch = KEY_AXES + ev->key; - else if (value < -jdeadzone) - ch = KEY_INV_AXES + ev->key; - } - - if (!useEventHandler && ch == -1) + if (ch == -1) return false; else if (ch == gamecontrol[GC_SYSTEMMENU][0] || ch == gamecontrol[GC_SYSTEMMENU][1]) // allow remappable ESC key ch = KEY_ESCAPE; @@ -3455,25 +3433,27 @@ boolean M_Responder(event_t *ev) if (currentMenu->menuitems[itemOn].status == IT_MSGHANDLER) { - if (!useEventHandler) + if (currentMenu->menuitems[itemOn].alphaKey != MM_EVENTHANDLER) { - UINT16 type = currentMenu->menuitems[itemOn].alphaKey; - if (type == MM_YESNO && !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER || ch == KEY_DEL)) - return true; - if (routine) - routine(ch); - if (type == MM_YESNO) + if (ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE || ch == KEY_ENTER || ch == KEY_DEL) { + if (routine) + routine(ch); if (stopstopmessage) stopstopmessage = false; else M_StopMessage(0); noFurtherInput = true; + return true; } return true; } else { + // dirty hack: for customising controls, I want only buttons/keys, not moves + if (ev->type == ev_mouse || ev->type == ev_mouse2 || ev->type == ev_joystick + || ev->type == ev_joystick2) + return true; if (routine) { void (*otherroutine)(event_t *sev) = currentMenu->menuitems[itemOn].itemaction; @@ -3739,7 +3719,6 @@ void M_StartControlPanel(void) currentMenu = &SPauseDef; itemOn = spause_continue; - } else // multiplayer { @@ -3784,9 +3763,6 @@ void M_StartControlPanel(void) } CON_ToggleOff(); // move away console - - if (P_AutoPause()) - P_PauseRumble(NULL); } void M_EndModeAttackRun(void) @@ -3815,7 +3791,6 @@ void M_ClearMenus(boolean callexitmenufunc) hidetitlemap = false; I_UpdateMouseGrab(); - P_UnpauseRumble(NULL); } // @@ -3985,10 +3960,10 @@ void M_Init(void) at all if every item just calls the same function, and nothing more. Now just automate the definition. */ - for (i = 0; i < MAX_CONNECTED_GAMEPADS + 1; ++i) + for (i = 0; i <= MAX_JOYSTICKS; ++i) { - OP_GamepadSetMenu[i].status = ( IT_NOTHING|IT_CALL ); - OP_GamepadSetMenu[i].itemaction = M_AssignGamepad; + OP_JoystickSetMenu[i].status = ( IT_NOTHING|IT_CALL ); + OP_JoystickSetMenu[i].itemaction = M_AssignJoystick; } #ifndef NONET @@ -4218,6 +4193,26 @@ static void M_DrawStaticBox(fixed_t x, fixed_t y, INT32 flags, fixed_t w, fixed_ W_UnlockCachedPatch(patch); } +// +// Draw border for the savegame description +// +#if 0 // once used for joysticks and savegames, now no longer +static void M_DrawSaveLoadBorder(INT32 x,INT32 y) +{ + INT32 i; + + V_DrawScaledPatch (x-8,y+7,0,W_CachePatchName("M_LSLEFT",PU_PATCH)); + + for (i = 0;i < 24;i++) + { + V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSCNTR",PU_PATCH)); + x += 8; + } + + V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSRGHT",PU_PATCH)); +} +#endif + // horizontally centered text static void M_CentreText(INT32 y, const char *string) { @@ -6156,10 +6151,7 @@ void M_StartMessage(const char *string, void *routine, MessageDef.menuitems[0].text = message; MessageDef.menuitems[0].alphaKey = (UINT8)itemtype; - - if (routine == NULL && itemtype != MM_NOTHING) - itemtype = MM_NOTHING; - + if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING; switch (itemtype) { case MM_NOTHING: @@ -6167,7 +6159,9 @@ void M_StartMessage(const char *string, void *routine, MessageDef.menuitems[0].itemaction = M_StopMessage; break; case MM_YESNO: - case MM_KEYHANDLER: + MessageDef.menuitems[0].status = IT_MSGHANDLER; + MessageDef.menuitems[0].itemaction = routine; + break; case MM_EVENTHANDLER: MessageDef.menuitems[0].status = IT_MSGHANDLER; MessageDef.menuitems[0].itemaction = routine; @@ -6199,6 +6193,7 @@ void M_StartMessage(const char *string, void *routine, MessageDef.lastOn = (INT16)((strlines<<8)+max); + //M_SetupNextMenu(); currentMenu = &MessageDef; itemOn = 0; } @@ -11409,7 +11404,7 @@ static void M_ConnectMenuModChecks(INT32 choice) if (modifiedgame) { - M_StartMessage(M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2 will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_KEYHANDLER); + M_StartMessage(M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\nSRB2 will automatically add\neverything you need when you join.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER); return; } @@ -12602,147 +12597,183 @@ static void M_SetupScreenshotMenu(void) item->status = (IT_STRING | IT_CVAR); } -// ============ -// GAMEPAD MENU -// ============ +// ============= +// JOYSTICK MENU +// ============= // Start the controls menu, setting it up for either the console player, // or the secondary splitscreen player -static void M_DrawGamepadList(void) +static void M_DrawJoystick(void) { - INT32 i; - - INT32 compareval = G_GetGamepadDeviceIndex(0); - INT32 compareval2 = G_GetGamepadDeviceIndex(1); + INT32 i, compareval2, compareval; // draw title (or big pic) M_DrawMenuTitle(); - for (i = 0; i < MAX_CONNECTED_GAMEPADS + 1; i++) + for (i = 0; i <= MAX_JOYSTICKS; i++) // See MAX_JOYSTICKS { - M_DrawTextBox(OP_GamepadSetDef.x-8, OP_GamepadSetDef.y+LINEHEIGHT*i-12, 28, 1); + M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1); + //M_DrawSaveLoadBorder(OP_JoystickSetDef.x+4, OP_JoystickSetDef.y+1+LINEHEIGHT*i); + +#ifdef JOYSTICK_HOTPLUG + if (atoi(cv_usejoystick2.string) > I_NumJoys()) + compareval2 = atoi(cv_usejoystick2.string); + else + compareval2 = cv_usejoystick2.value; + + if (atoi(cv_usejoystick.string) > I_NumJoys()) + compareval = atoi(cv_usejoystick.string); + else + compareval = cv_usejoystick.value; +#else + compareval2 = cv_usejoystick2.value; + compareval = cv_usejoystick.value; +#endif if ((setupcontrols_secondaryplayer && (i == compareval2)) || (!setupcontrols_secondaryplayer && (i == compareval))) - V_DrawString(OP_GamepadSetDef.x, OP_GamepadSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,gamepadInfo[i].name); + V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,joystickInfo[i]); else - V_DrawString(OP_GamepadSetDef.x, OP_GamepadSetDef.y+LINEHEIGHT*i-4,0,gamepadInfo[i].name); + V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,0,joystickInfo[i]); if (i == itemOn) { - V_DrawScaledPatch(currentMenu->x - 24, OP_GamepadSetDef.y+LINEHEIGHT*i-4, 0, + V_DrawScaledPatch(currentMenu->x - 24, OP_JoystickSetDef.y+LINEHEIGHT*i-4, 0, W_CachePatchName("M_CURSOR", PU_PATCH)); } } } -boolean M_OnGamepadMenu(void) +void M_SetupJoystickMenu(INT32 choice) { - return currentMenu == &OP_GamepadSetDef; -} + INT32 i = 0; + const char *joyNA = "Unavailable"; + INT32 n = I_NumJoys(); + (void)choice; -void M_UpdateGamepadMenu(void) -{ - INT32 i = 0, j = 1; - INT32 n = I_NumGamepads(); + strcpy(joystickInfo[i], "None"); - strcpy(gamepadInfo[i].name, "None"); - gamepadInfo[i].index = 0; - - for (i = 1; i < MAX_CONNECTED_GAMEPADS + 1; i++) + for (i = 1; i <= MAX_JOYSTICKS; i++) { - if (i <= n && (I_GetGamepadName(i)) != NULL) - strlcpy(gamepadInfo[j].name, I_GetGamepadName(i), sizeof gamepadInfo[j].name); + if (i <= n && (I_GetJoyName(i)) != NULL) + strncpy(joystickInfo[i], I_GetJoyName(i), 28); else - strlcpy(gamepadInfo[j].name, "Unavailable", sizeof gamepadInfo[j].name); + strcpy(joystickInfo[i], joyNA); - gamepadInfo[j].index = j; - -#ifdef GAMEPAD_HOTPLUG - // Update cv_usegamepad.string here so that the user can +#ifdef JOYSTICK_HOTPLUG + // We use cv_usejoystick.string as the USER-SET var + // and cv_usejoystick.value as the INTERNAL var + // + // In practice, if cv_usejoystick.string == 0, this overrides + // cv_usejoystick.value and always disables + // + // Update cv_usejoystick.string here so that the user can // properly change this value. - for (INT32 jn = 0; jn < NUM_GAMEPADS; jn++) - { - if (i == cv_usegamepad[jn].value) - CV_SetValue(&cv_usegamepad[jn], i); - } + if (i == cv_usejoystick.value) + CV_SetValue(&cv_usejoystick, i); + if (i == cv_usejoystick2.value) + CV_SetValue(&cv_usejoystick2, i); #endif - - j++; } + + M_SetupNextMenu(&OP_JoystickSetDef); } -static void M_SetupGamepadMenu(void) -{ - M_UpdateGamepadMenu(); - M_SetupNextMenu(&OP_GamepadSetDef); - - if (setupcontrols_secondaryplayer) - itemOn = G_GetGamepadDeviceIndex(1); - else - itemOn = G_GetGamepadDeviceIndex(0); -} - -static void M_Setup1PGamepadMenu(INT32 choice) +static void M_Setup1PJoystickMenu(INT32 choice) { setupcontrols_secondaryplayer = false; - setupcontrols_joycvar = &cv_usegamepad[0]; - OP_GamepadSetDef.prevMenu = &OP_Gamepad1Def; - OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); - OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); - OP_GamepadSetDef.menuid |= MN_OP_P1CONTROLS << MENUBITS; - OP_GamepadSetDef.menuid |= MN_OP_P1JOYSTICK << (MENUBITS*2); - - M_SetupGamepadMenu(); - (void)choice; + OP_JoystickSetDef.prevMenu = &OP_Joystick1Def; + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); + OP_JoystickSetDef.menuid |= MN_OP_P1CONTROLS << MENUBITS; + OP_JoystickSetDef.menuid |= MN_OP_P1JOYSTICK << (MENUBITS*2); + M_SetupJoystickMenu(choice); } -static void M_Setup2PGamepadMenu(INT32 choice) +static void M_Setup2PJoystickMenu(INT32 choice) { setupcontrols_secondaryplayer = true; - setupcontrols_joycvar = &cv_usegamepad[1]; - OP_GamepadSetDef.prevMenu = &OP_Gamepad2Def; - OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); - OP_GamepadSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); - OP_GamepadSetDef.menuid |= MN_OP_P2CONTROLS << MENUBITS; - OP_GamepadSetDef.menuid |= MN_OP_P2JOYSTICK << (MENUBITS*2); - - M_SetupGamepadMenu(); - (void)choice; + OP_JoystickSetDef.prevMenu = &OP_Joystick2Def; + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); + OP_JoystickSetDef.menuid &= ~(((1 << MENUBITS) - 1) << (MENUBITS*2)); + OP_JoystickSetDef.menuid |= MN_OP_P2CONTROLS << MENUBITS; + OP_JoystickSetDef.menuid |= MN_OP_P2JOYSTICK << (MENUBITS*2); + M_SetupJoystickMenu(choice); } -static void M_AssignGamepad(INT32 choice) +static void M_AssignJoystick(INT32 choice) { -#ifdef GAMEPAD_HOTPLUG - INT32 this = gamepadInfo[choice].index; +#ifdef JOYSTICK_HOTPLUG + INT32 oldchoice, oldstringchoice; + INT32 numjoys = I_NumJoys(); - // Detect if other players are using this gamepad index - for (INT32 i = 0; this && i < NUM_GAMEPADS; i++) + if (setupcontrols_secondaryplayer) { - // Ignore yourself - if (i == (INT32)setupcontrols_secondaryplayer) - continue; + oldchoice = oldstringchoice = atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value; + CV_SetValue(&cv_usejoystick2, choice); - INT32 other = G_GetGamepadDeviceIndex(i); - - // Ignore gamepads that are disconnected - // (the game will deal with it when they are connected) - if (other > I_NumGamepads()) - continue; - - if (other == this) + // Just in case last-minute changes were made to cv_usejoystick.value, + // update the string too + // But don't do this if we're intentionally setting higher than numjoys + if (choice <= numjoys) { - M_StartMessage("This gamepad is used by another\n" - "player. Reset the gamepad\n" - "for that player first.\n\n" - "(Press a key)\n", NULL, MM_NOTHING); - return; + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); + + // reset this so the comparison is valid + if (oldchoice > numjoys) + oldchoice = cv_usejoystick2.value; + + if (oldchoice != choice) + { + if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick2, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); + + if (oldstringchoice == + (atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value)) + M_StartMessage("This gamepad is used by another\n" + "player. Reset the gamepad\n" + "for that player first.\n\n" + "(Press a key)\n", NULL, MM_NOTHING); + } } } -#endif + else + { + oldchoice = oldstringchoice = atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value; + CV_SetValue(&cv_usejoystick, choice); - CV_SetValue(setupcontrols_joycvar, this); + // Just in case last-minute changes were made to cv_usejoystick.value, + // update the string too + // But don't do this if we're intentionally setting higher than numjoys + if (choice <= numjoys) + { + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); + + // reset this so the comparison is valid + if (oldchoice > numjoys) + oldchoice = cv_usejoystick.value; + + if (oldchoice != choice) + { + if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device + CV_SetValue(&cv_usejoystick, (oldstringchoice > numjoys ? oldstringchoice : oldchoice)); + + if (oldstringchoice == + (atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value)) + M_StartMessage("This gamepad is used by another\n" + "player. Reset the gamepad\n" + "for that player first.\n\n" + "(Press a key)\n", NULL, MM_NOTHING); + } + } + } +#else + if (setupcontrols_secondaryplayer) + CV_SetValue(&cv_usejoystick2, choice); + else + CV_SetValue(&cv_usejoystick, choice); +#endif } // ============= @@ -12813,26 +12844,10 @@ static void M_Setup2PControlsMenu(INT32 choice) M_SetupNextMenu(&OP_ChangeControlsDef); } -static const char *M_GetKeyName(UINT8 player, INT32 key) -{ - gamepadtype_e type = GAMEPAD_TYPE_UNKNOWN; - if (cv_usegamepad[player].value) - type = gamepads[player].type; - - if (key >= KEY_GAMEPAD && key < KEY_GAMEPAD + NUM_GAMEPAD_BUTTONS) - return G_GetGamepadButtonString(type, key - KEY_GAMEPAD, GAMEPAD_STRING_MENU1); - else if (key >= KEY_AXES && key < KEY_AXES + NUM_GAMEPAD_AXES) - return G_GetGamepadAxisString(type, key - KEY_AXES, GAMEPAD_STRING_MENU1, false); - else if (key >= KEY_INV_AXES && key < KEY_INV_AXES + NUM_GAMEPAD_AXES) - return G_GetGamepadAxisString(type, key - KEY_INV_AXES, GAMEPAD_STRING_MENU1, true); - - return G_GetDisplayNameForKey(key); -} - #define controlheight 18 // Draws the Customise Controls menu -static void M_DrawControlConfigMenu(void) +static void M_DrawControl(void) { char tmp[50]; INT32 x, y, i, max, cursory = 0, iter; @@ -12912,38 +12927,40 @@ static void M_DrawControlConfigMenu(void) if (currentMenu->menuitems[i].status == IT_CONTROL) { - INT32 right = x + V_StringWidth(currentMenu->menuitems[i].text, 0); V_DrawString(x, y, ((i == itemOn) ? V_YELLOWMAP : 0), currentMenu->menuitems[i].text); - keys[0] = setupcontrols[currentMenu->menuitems[i].alphaKey][0]; keys[1] = setupcontrols[currentMenu->menuitems[i].alphaKey][1]; tmp[0] ='\0'; if (keys[0] == KEY_NULL && keys[1] == KEY_NULL) + { strcpy(tmp, "---"); + } else { if (keys[0] != KEY_NULL) - strcat(tmp, M_GetKeyName(setupcontrols_secondaryplayer, keys[0])); - if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) - strcat(tmp, " or "); - if (keys[1] != KEY_NULL) - strcat(tmp, M_GetKeyName(setupcontrols_secondaryplayer, keys[1])); - } + strcat (tmp, G_KeyNumToName (keys[0])); - INT32 left = BASEVIDWIDTH-currentMenu->x-V_StringWidth(tmp, V_ALLOWLOWERCASE); - if (left - 8 <= right) - V_DrawRightAlignedThinString(BASEVIDWIDTH-currentMenu->x, y+1, V_ALLOWLOWERCASE | V_YELLOWMAP, tmp); - else - V_DrawRightAlignedString(BASEVIDWIDTH-currentMenu->x, y, V_ALLOWLOWERCASE | V_YELLOWMAP, tmp); + if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) + strcat(tmp," or "); + + if (keys[1] != KEY_NULL) + strcat (tmp, G_KeyNumToName (keys[1])); + + + } + V_DrawRightAlignedString(BASEVIDWIDTH-currentMenu->x, y, V_YELLOWMAP, tmp); } + /*else if (currentMenu->menuitems[i].status == IT_GRAYEDOUT2) + V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);*/ else if ((currentMenu->menuitems[i].status == IT_HEADER) && (i != max-1)) M_DrawLevelPlatterHeader(y, currentMenu->menuitems[i].text, true, false); y += SMALLLINEHEIGHT; } - V_DrawScaledPatch(currentMenu->x - 20, cursory, 0, W_CachePatchName("M_CURSOR", PU_PATCH)); + V_DrawScaledPatch(currentMenu->x - 20, cursory, 0, + W_CachePatchName("M_CURSOR", PU_PATCH)); } #undef controlbuffer @@ -12951,55 +12968,55 @@ static void M_DrawControlConfigMenu(void) static INT32 controltochange; static char controltochangetext[33]; -static void M_ChangeControlResponse(event_t *ev) +static void M_ChangecontrolResponse(event_t *ev) { - // dirty hack: for customising controls, I want only buttons/keys, not moves - if (ev->type == ev_mouse || ev->type == ev_mouse2) - return; - - INT32 ch = ev->key; - - // Remap gamepad events - if (ev->type == ev_gamepad_down) - ch += KEY_GAMEPAD; - else if (ev->type == ev_gamepad_axis) - { - const UINT16 jdeadzone = G_GetGamepadDigitalDeadZone(ev->which); - const INT16 value = G_GamepadAxisEventValue(ev->which, ev->x); - - if (value > jdeadzone) - ch += KEY_AXES; - else if (value < -jdeadzone) - ch += KEY_INV_AXES; - else - return; - } - else if (ev->type != ev_keydown) - return; + INT32 control; + INT32 found; + INT32 ch = ev->key; // ESCAPE cancels; dummy out PAUSE if (ch != KEY_ESCAPE && ch != KEY_PAUSE) { - INT32 control = controltochange; + + switch (ev->type) + { + // ignore mouse/joy movements, just get buttons + case ev_mouse: + case ev_mouse2: + case ev_joystick: + case ev_joystick2: + ch = KEY_NULL; // no key + break; + + // keypad arrows are converted for the menu in cursor arrows + // so use the event instead of ch + case ev_keydown: + ch = ev->key; + break; + + default: + break; + } + + control = controltochange; // check if we already entered this key - INT32 found = -1; - if (setupcontrols[control][0] == ch) + found = -1; + if (setupcontrols[control][0] ==ch) found = 0; - else if (setupcontrols[control][1] == ch) + else if (setupcontrols[control][1] ==ch) found = 1; - if (found >= 0) { -#define CHECK_DBL(key, length) (ch >= key && ch <= key+length) -#define SET_DBL(key, dblkey) ch-key+dblkey - // replace mouse clicks by double clicks - if (CHECK_DBL(KEY_MOUSE1, MOUSEBUTTONS)) - setupcontrols[control][found] = SET_DBL(KEY_MOUSE1, KEY_DBLMOUSE1); - else if (CHECK_DBL(KEY_2MOUSE1, MOUSEBUTTONS)) - setupcontrols[control][found] = SET_DBL(KEY_2MOUSE1, KEY_DBL2MOUSE1); -#undef CHECK_DBL -#undef SET_DBL + // replace mouse and joy clicks by double clicks + if (ch >= KEY_MOUSE1 && ch <= KEY_MOUSE1+MOUSEBUTTONS) + setupcontrols[control][found] = ch-KEY_MOUSE1+KEY_DBLMOUSE1; + else if (ch >= KEY_JOY1 && ch <= KEY_JOY1+JOYBUTTONS) + setupcontrols[control][found] = ch-KEY_JOY1+KEY_DBLJOY1; + else if (ch >= KEY_2MOUSE1 && ch <= KEY_2MOUSE1+MOUSEBUTTONS) + setupcontrols[control][found] = ch-KEY_2MOUSE1+KEY_DBL2MOUSE1; + else if (ch >= KEY_2JOY1 && ch <= KEY_2JOY1+JOYBUTTONS) + setupcontrols[control][found] = ch-KEY_2JOY1+KEY_DBL2JOY1; } else { @@ -13014,11 +13031,9 @@ static void M_ChangeControlResponse(event_t *ev) found = 0; setupcontrols[control][1] = KEY_NULL; //replace key 1,clear key2 } - - G_CheckDoubleUsage(ch, true); + (void)G_CheckDoubleUsage(ch, true); setupcontrols[control][found] = ch; } - S_StartSound(NULL, sfx_strpst); } else if (ch == KEY_PAUSE) @@ -13034,7 +13049,7 @@ static void M_ChangeControlResponse(event_t *ev) sprintf(tmp, M_GetText("The \x82Pause Key \x80is enabled, but \nit is not configurable. \n\nHit another key for\n%s\nESC for Cancel"), controltochangetext); - M_StartMessage(tmp, M_ChangeControlResponse, MM_EVENTHANDLER); + M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER); currentMenu->prevMenu = prev; S_StartSound(NULL, sfx_s3k42); @@ -13060,150 +13075,7 @@ static void M_ChangeControl(INT32 choice) currentMenu->menuitems[choice].text); strlcpy(controltochangetext, currentMenu->menuitems[choice].text, 33); - M_StartMessage(tmp, M_ChangeControlResponse, MM_EVENTHANDLER); -} - -static const char *M_GetGamepadAxisName(consvar_t *cv) -{ - switch (cv->value) - { - case 0: - return "None"; - - case 1: // X - return "L. Stick X"; - case 2: // Y - return "L. Stick Y"; - case 3: // X - return "R. Stick X"; - case 4: // Y - return "R. Stick Y"; - - case -1: // X- - return "L. Stick X (inv.)"; - case -2: // Y- - return "L. Stick Y (inv.)"; - case -3: // X- - return "R. Stick X (inv.)"; - case -4: // Y- - return "R. Stick Y (inv.)"; - - case 5: - return "L. Trigger"; - case 6: - return "R. Trigger"; - - default: - return cv->string; - } -} - -static void M_DrawGamepadMenu(void) -{ - INT32 x, y, i, cursory = 0; - INT32 right, left; - - // DRAW MENU - x = currentMenu->x; - y = currentMenu->y; - - // draw title (or big pic) - M_DrawMenuTitle(); - - for (i = 0; i < currentMenu->numitems; i++) - { - if (i == itemOn) - cursory = y; - switch (currentMenu->menuitems[i].status & IT_DISPLAY) - { - case IT_NOTHING: - case IT_DYBIGSPACE: - y += LINEHEIGHT; - break; - case IT_STRING: - case IT_WHITESTRING: - if (currentMenu->menuitems[i].alphaKey) - y = currentMenu->y+currentMenu->menuitems[i].alphaKey; - if (i == itemOn) - cursory = y; - - if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) - V_DrawString(x, y, 0, currentMenu->menuitems[i].text); - else - V_DrawString(x, y, V_YELLOWMAP, currentMenu->menuitems[i].text); - - right = x + V_StringWidth(currentMenu->menuitems[i].text, 0); - - // Cvar specific handling - switch (currentMenu->menuitems[i].status & IT_TYPE) - case IT_CVAR: - { - consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction; - switch (currentMenu->menuitems[i].status & IT_CVARTYPE) - { - case IT_CV_SLIDER: - M_DrawSlider(x, y, cv, (i == itemOn)); - break; - default: - { - const char *str = cv->string; - INT32 flags = V_YELLOWMAP; - INT32 width = V_StringWidth(str, flags); - - if (cv->PossibleValue == joyaxis_cons_t) - { - str = M_GetGamepadAxisName(cv); - flags |= V_ALLOWLOWERCASE; - - width = V_StringWidth(str, flags); - left = BASEVIDWIDTH - x - width; - - if (left - 16 <= right) - { - width = V_ThinStringWidth(str, flags); - V_DrawRightAlignedThinString(BASEVIDWIDTH - x, y + 1, flags, str); - } - else - V_DrawRightAlignedString(BASEVIDWIDTH - x, y, flags, str); - } - else - V_DrawRightAlignedString(BASEVIDWIDTH - x, y, flags, str); - - if (i == itemOn) - { - V_DrawCharacter(BASEVIDWIDTH - x - 10 - width - (skullAnimCounter/5), y, - '\x1C' | V_YELLOWMAP, false); - V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y, - '\x1D' | V_YELLOWMAP, false); - } - break; - } - } - break; - } - y += STRINGHEIGHT; - break; - case IT_TRANSTEXT: - if (currentMenu->menuitems[i].alphaKey) - y = currentMenu->y+currentMenu->menuitems[i].alphaKey; - V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text); - y += SMALLLINEHEIGHT; - break; - case IT_HEADERTEXT: // draws 16 pixels to the left, in yellow text - if (currentMenu->menuitems[i].alphaKey) - y = currentMenu->y+currentMenu->menuitems[i].alphaKey; - - //V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text); - M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, true, false); - y += SMALLLINEHEIGHT; - break; - } - } - - // DRAW THE SKULL CURSOR - V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, - W_CachePatchName("M_CURSOR", PU_PATCH)); - V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text); + M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER); } static void M_Setup1PPlaystyleMenu(INT32 choice) diff --git a/src/m_menu.h b/src/m_menu.h index 8d023811d..a7072b0c1 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -223,9 +223,8 @@ typedef enum { MM_NOTHING = 0, // is just displayed until the user do someting MM_YESNO, // routine is called with only 'y' or 'n' in param - MM_KEYHANDLER, // the same of above but without 'y' or 'n' restriction - MM_EVENTHANDLER // the same of above but routine is void routine(event_t *) - // (ex: set control) + MM_EVENTHANDLER // the same of above but without 'y' or 'n' restriction + // and routine is void routine(event_t *) (ex: set control) } menumessagetype_t; void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtype); @@ -362,11 +361,9 @@ extern menu_t *currentMenu; extern menu_t MainDef; extern menu_t SP_LoadDef; -// Call when a gamepad is connected or disconnected -void M_UpdateGamepadMenu(void); - -// Returns true if the player is on the gamepad selection menu -boolean M_OnGamepadMenu(void); +// Call upon joystick hotplug +void M_SetupJoystickMenu(INT32 choice); +extern menu_t OP_JoystickSetDef; // Stuff for customizing the player select screen typedef struct @@ -541,19 +538,6 @@ void M_FreePlayerSetupColors(void); NULL\ } -#define GAMEPADMENUSTYLE(id, header, source, prev, x, y)\ -{\ - id,\ - header,\ - sizeof(source)/sizeof(menuitem_t),\ - prev,\ - source,\ - M_DrawGamepadMenu,\ - x, y,\ - 0,\ - NULL\ -} - #define MAPPLATTERMENUSTYLE(id, header, source)\ {\ id,\ @@ -574,7 +558,7 @@ void M_FreePlayerSetupColors(void); sizeof (source)/sizeof (menuitem_t),\ prev,\ source,\ - M_DrawControlConfigMenu,\ + M_DrawControl,\ 24, 40,\ 0,\ NULL\ diff --git a/src/p_haptic.c b/src/p_haptic.c deleted file mode 100644 index dbfa58737..000000000 --- a/src/p_haptic.c +++ /dev/null @@ -1,115 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 2021-2022 by Jaime "Lactozilla" Passos. -// -// 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 p_haptic.c -/// \brief Haptic feedback - -#include "p_haptic.h" -#include "g_game.h" -#include "d_netcmd.h" -#include "i_gamepad.h" -#include "doomstat.h" - -// Helper function: Returns the gamepad index for a player if it's enabled -static INT16 GetGamepadIndex(player_t *player) -{ - INT16 index = G_GetGamepadForPlayer(player); - - if (index >= 0 && cv_usegamepad[index].value) - return index; - - return -1; -} - -// Rumbles a player's gamepad, or all gamepads -boolean P_DoRumble(player_t *player, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration) -{ - if (!I_RumbleSupported()) - return false; - - // Rumble every gamepad - if (player == NULL) - { - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - if (cv_gamepad_rumble[i].value) - G_RumbleGamepad(i, large_magnitude, small_magnitude, duration); - } - - return true; - } - - INT16 which = GetGamepadIndex(player); - if (which < 0 || !cv_gamepad_rumble[which].value) - return false; - - return G_RumbleGamepad((UINT8)which, large_magnitude, small_magnitude, duration); -} - -// Pauses or unpauses gamepad rumble for a player (or all of them) -// Rumble is paused or unpaused regardless if it's enabled or not -static void SetRumblePaused(player_t *player, boolean pause) -{ - INT16 which = GetGamepadIndex(player); - - if (which >= 0) - G_SetGamepadRumblePaused((UINT8)which, pause); - else if (player == NULL) - { - // Pause or unpause every gamepad - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - G_SetGamepadRumblePaused(i, pause); - } -} - -void P_PauseRumble(player_t *player) -{ - SetRumblePaused(player, true); -} - -void P_UnpauseRumble(player_t *player) -{ - SetRumblePaused(player, false); -} - -boolean P_IsRumbleEnabled(player_t *player) -{ - INT16 which = GetGamepadIndex(player); - if (which < 0 || !cv_gamepad_rumble[which].value) - return false; - - return G_RumbleSupported((UINT8)which); -} - -boolean P_IsRumblePaused(player_t *player) -{ - INT16 which = GetGamepadIndex(player); - if (which < 0 || !cv_gamepad_rumble[which].value) - return false; - - return G_GetGamepadRumblePaused((UINT8)which); -} - -// Stops gamepad rumble for a player (or all of them) -void P_StopRumble(player_t *player) -{ - if (!I_RumbleSupported()) - return; - - if (player) - { - INT16 which = GetGamepadIndex(player); - if (which >= 0) - G_StopGamepadRumble((UINT8)which); - return; - } - - // Stop every gamepad instead - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - G_StopGamepadRumble(i); -} diff --git a/src/p_haptic.h b/src/p_haptic.h deleted file mode 100644 index 1bd4f9199..000000000 --- a/src/p_haptic.h +++ /dev/null @@ -1,27 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 2021-2022 by Jaime "Lactozilla" Passos. -// -// 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 p_haptic.h -/// \brief Haptic feedback - -#ifndef __P_HAPTIC__ -#define __P_HAPTIC__ - -#include "doomdef.h" -#include "p_local.h" - -boolean P_DoRumble(player_t *player, fixed_t large_magnitude, fixed_t small_magnitude, tic_t duration); -void P_PauseRumble(player_t *player); -void P_UnpauseRumble(player_t *player); -boolean P_IsRumbleEnabled(player_t *player); -boolean P_IsRumblePaused(player_t *player); -void P_StopRumble(player_t *player); - -#define P_DoRumbleCombined(player, magnitude, dur) P_DoRumble(player, magnitude, magnitude, dur); - -#endif // __P_HAPTIC__ diff --git a/src/p_inter.c b/src/p_inter.c index f3c13e315..dd3e0f9c2 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -13,7 +13,6 @@ #include "doomdef.h" #include "i_system.h" -#include "i_gamepad.h" #include "am_map.h" #include "g_game.h" #include "m_random.h" @@ -25,7 +24,6 @@ #include "lua_hook.h" #include "m_cond.h" // unlockables, emblems, etc #include "p_setup.h" -#include "p_haptic.h" #include "m_cheat.h" // objectplace #include "m_misc.h" #include "v_video.h" // video flags for CEchos @@ -35,6 +33,54 @@ #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" #define CTFTEAMENDCODE(pl) pl->ctfteam ? "\x80" : "" +void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period) +{ + BasicFF_t Basicfeed; + if (!player) + return; + Basicfeed.Duration = (UINT32)(duration * (100L/TICRATE)); + Basicfeed.ForceX = Basicfeed.ForceY = 1; + Basicfeed.Gain = 25000; + Basicfeed.Magnitude = period*10; + Basicfeed.player = player; + /// \todo test FFB + P_RampConstant(&Basicfeed, attack, fade); +} + +void P_ForceConstant(const BasicFF_t *FFInfo) +{ + JoyFF_t ConstantQuake; + if (!FFInfo || !FFInfo->player) + return; + ConstantQuake.ForceX = FFInfo->ForceX; + ConstantQuake.ForceY = FFInfo->ForceY; + ConstantQuake.Duration = FFInfo->Duration; + ConstantQuake.Gain = FFInfo->Gain; + ConstantQuake.Magnitude = FFInfo->Magnitude; + if (FFInfo->player == &players[consoleplayer]) + I_Tactile(ConstantForce, &ConstantQuake); + else if (splitscreen && FFInfo->player == &players[secondarydisplayplayer]) + I_Tactile2(ConstantForce, &ConstantQuake); +} +void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End) +{ + JoyFF_t RampQuake; + if (!FFInfo || !FFInfo->player) + return; + RampQuake.ForceX = FFInfo->ForceX; + RampQuake.ForceY = FFInfo->ForceY; + RampQuake.Duration = FFInfo->Duration; + RampQuake.Gain = FFInfo->Gain; + RampQuake.Magnitude = FFInfo->Magnitude; + RampQuake.Start = Start; + RampQuake.End = End; + if (FFInfo->player == &players[consoleplayer]) + I_Tactile(ConstantForce, &RampQuake); + else if (splitscreen && FFInfo->player == &players[secondarydisplayplayer]) + I_Tactile2(ConstantForce, &RampQuake); +} + + // // GET STUFF // @@ -3011,8 +3057,6 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN player_t *player = target->player; (void)damage; //unused parm - P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); - // If flashing or invulnerable, ignore the tag, if (player->powers[pw_flashing] || player->powers[pw_invulnerability]) return false; @@ -3116,8 +3160,6 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou { player_t *player = target->player; - (void)damage; - if (!(damagetype & DMG_CANHURTSELF)) { // You can't kill yourself, idiot... @@ -3180,8 +3222,6 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) { - (void)damage; - player->pflags &= ~PF_SLIDING; player->powers[pw_carry] = CR_NONE; @@ -3202,7 +3242,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) // Get rid of emeralds player->powers[pw_emeralds] = 0; - P_DoRumbleCombined(player, FRACUNIT, TICRATE / 3); + P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); P_ResetPlayer(player); @@ -3242,9 +3282,7 @@ static void P_SuperDamage(player_t *player, mobj_t *inflictor, mobj_t *source, I fixed_t fallbackspeed; angle_t ang; - (void)damage; - - P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); + P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); if (player->mo->eflags & MFE_VERTICALFLIP) player->mo->z--; @@ -3325,14 +3363,12 @@ void P_RemoveShield(player_t *player) static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) { - (void)damage; - // Must do pain first to set flashing -- P_RemoveShield can cause damage P_DoPlayerPain(player, source, inflictor); P_RemoveShield(player); - P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); + P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); if (damagetype == DMG_SPIKE) // spikes S_StartSound(player->mo, sfx_spkdth); @@ -3361,7 +3397,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN { P_DoPlayerPain(player, source, inflictor); - P_DoRumbleCombined(player, FRACUNIT, TICRATE / 6); + P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); if (damagetype == DMG_SPIKE) // spikes S_StartSound(player->mo, sfx_spkdth); @@ -3692,6 +3728,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da damage = 1; P_KillPlayer(player, source, damage); } + + P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2); } // Killing dead. Just for kicks. diff --git a/src/p_local.h b/src/p_local.h index 31a9e6c9d..2b3020997 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -453,6 +453,18 @@ extern mobj_t **blocklinks; // for thing chains // // P_INTER // +typedef struct BasicFF_s +{ + INT32 ForceX; ///< The X of the Force's Vel + INT32 ForceY; ///< The Y of the Force's Vel + const player_t *player; ///< Player of Rumble + //All + UINT32 Duration; ///< The total duration of the effect, in microseconds + INT32 Gain; ///< /The gain to be applied to the effect, in the range from 0 through 10,000. + //All, CONSTANTFORCE �10,000 to 10,000 + INT32 Magnitude; ///< Magnitude of the effect, in the range from 0 through 10,000. +} BasicFF_t; + /* Damage/death types, for P_DamageMobj and related */ //// Damage types //#define DMG_NORMAL 0 (unneeded?) @@ -473,6 +485,9 @@ extern mobj_t **blocklinks; // for thing chains #define DMG_CANHURTSELF 0x40 // Flag - can hurt self/team indirectly, such as through mines #define DMG_DEATHMASK DMG_INSTAKILL // if bit 7 is set, this is a death type instead of a damage type +void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period); +void P_ForceConstant(const BasicFF_t *FFInfo); +void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End); void P_RemoveShield(player_t *player); void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source); boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); diff --git a/src/p_user.c b/src/p_user.c index 4ca4e6c8a..c3f47f70c 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -5331,9 +5331,9 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) // disabled because it seemed to disorient people and Z-targeting exists now /*if (!demoplayback) { - if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(G_PlayerInputDown(0, GC_TURNLEFT) || G_PlayerInputDown(0, GC_TURNRIGHT))) + if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(PLAYER1INPUTDOWN(GC_TURNLEFT) || PLAYER1INPUTDOWN(GC_TURNRIGHT))) P_SetPlayerAngle(player, player->mo->angle);; - else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(G_PlayerInputDown(1, GC_TURNLEFT) || G_PlayerInputDown(1, GC_TURNRIGHT))) + else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(PLAYER2INPUTDOWN(GC_TURNLEFT) || PLAYER2INPUTDOWN(GC_TURNRIGHT))) P_SetPlayerAngle(player, player->mo->angle); }*/ } @@ -7342,7 +7342,7 @@ static void P_NiGHTSMovement(player_t *player) else if (cmd->forwardmove < 0) newangle = 270; } - else // AngleFixed(R_PointToAngle2()) results in slight inaccuracy! Don't use it unless movement is on both axes. + else // AngleFixed(R_PointToAngle2()) results in slight inaccuracy! Don't use it unless movement is on both axises. newangle = (INT16)FixedInt(AngleFixed(R_PointToAngle2(0,0, cmd->sidemove*FRACUNIT, cmd->forwardmove*FRACUNIT))); newangle -= player->viewrollangle / ANG1; diff --git a/src/sdl/Sourcefile b/src/sdl/Sourcefile index ef6a8b0dc..82d5ce073 100644 --- a/src/sdl/Sourcefile +++ b/src/sdl/Sourcefile @@ -2,7 +2,6 @@ i_net.c i_system.c i_main.c i_video.c -i_gamepad.c dosstr.c endtxt.c hwsym_sdl.c diff --git a/src/sdl/i_gamepad.c b/src/sdl/i_gamepad.c deleted file mode 100644 index 69f03d662..000000000 --- a/src/sdl/i_gamepad.c +++ /dev/null @@ -1,914 +0,0 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2022 by Sonic Team Junior. -// -// 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 i_gamepad.c -/// \brief Gamepads - -#ifdef HAVE_SDL -#include "../i_gamepad.h" -#include "../i_system.h" -#include "../doomdef.h" -#include "../d_main.h" -#include "../d_netcmd.h" -#include "../g_game.h" -#include "../m_argv.h" -#include "../m_menu.h" -#include "../z_zone.h" - -#include "SDL.h" -#include "SDL_joystick.h" -#include "sdlmain.h" - -static void Controller_ChangeDevice(UINT8 num); -static void Controller_Close(UINT8 num); -static void Controller_StopRumble(UINT8 num); - -static ControllerInfo controllers[NUM_GAMEPADS]; - -static boolean rumble_supported = false; -static boolean rumble_paused = false; - -// This attempts to initialize the gamepad subsystems -static boolean InitGamepadSubsystems(void) -{ - if (M_CheckParm("-noxinput")) - SDL_SetHintWithPriority(SDL_HINT_XINPUT_ENABLED, "0", SDL_HINT_OVERRIDE); - if (M_CheckParm("-nohidapi")) - SDL_SetHintWithPriority(SDL_HINT_JOYSTICK_HIDAPI, "0", SDL_HINT_OVERRIDE); - - if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == 0) - { - if (SDL_InitSubSystem(GAMEPAD_INIT_FLAGS) == -1) - { - CONS_Printf(M_GetText("Couldn't initialize game controller subsystems: %s\n"), SDL_GetError()); - return false; - } - } - - return true; -} - -void I_InitGamepads(void) -{ - if (M_CheckParm("-nojoy")) - return; - - CONS_Printf("I_InitGamepads()...\n"); - - if (!InitGamepadSubsystems()) - return; - - rumble_supported = !M_CheckParm("-norumble"); - - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - controllers[i].info = &gamepads[i]; - - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - Controller_ChangeDevice(i); -} - -INT32 I_NumGamepads(void) -{ - if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == GAMEPAD_INIT_FLAGS) - return SDL_NumJoysticks(); - else - return 0; -} - -// From the SDL source code -#define USB_VENDOR_MICROSOFT 0x045e -#define USB_VENDOR_PDP 0x0e6f -#define USB_VENDOR_POWERA_ALT 0x20d6 - -#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 0x02e3 -#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 0x0b00 -#define USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH 0x0b05 -#define USB_PRODUCT_XBOX_SERIES_X 0x0b12 -#define USB_PRODUCT_XBOX_SERIES_X_BLE 0x0b13 -#define USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT 0x02d6 -#define USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE 0x02d9 -#define USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW 0x02da -#define USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 0x4001 -#define USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA 0x4002 - -static boolean IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id) -{ - if (vendor_id == USB_VENDOR_MICROSOFT) { - if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 || - product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 || - product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) { - return true; - } - } - - return false; -} - -static boolean IsJoystickXboxSeriesXS(Uint16 vendor_id, Uint16 product_id) -{ - if (vendor_id == USB_VENDOR_MICROSOFT) { - if (product_id == USB_PRODUCT_XBOX_SERIES_X || - product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) { - return true; - } - } - else if (vendor_id == USB_VENDOR_PDP) { - if (product_id == USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT || - product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE || - product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW) { - return true; - } - } - else if (vendor_id == USB_VENDOR_POWERA_ALT) { - if ((product_id >= 0x2001 && product_id <= 0x201a) || - product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 || - product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA) { - return true; - } - } - - return false; -} - -// Opens a controller device -static boolean Controller_OpenDevice(UINT8 which, INT32 devindex) -{ - if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == 0) - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Game controller subsystems not started\n")); - return false; - } - - if (devindex <= 0) - return false; - - if (SDL_NumJoysticks() == 0) - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Found no controllers on this system\n")); - return false; - } - - devindex--; - - if (!SDL_IsGameController(devindex)) - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Device index %d isn't a game controller\n"), devindex); - return false; - } - - ControllerInfo *controller = &controllers[which]; - SDL_GameController *newdev = SDL_GameControllerOpen(devindex); - - // Handle the edge case where the device <-> controller index assignment can change due to hotplugging - // This indexing is SDL's responsibility and there's not much we can do about it. - // - // Example: - // 1. Plug Controller A -> Index 0 opened - // 2. Plug Controller B -> Index 1 opened - // 3. Unplug Controller A -> Index 0 closed, Index 1 active - // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed - // 5. Plug Controller B -> Index 0 opened - // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B - if (controller->dev) - { - if (controller->dev == newdev // same device, nothing to do - || (newdev == NULL && SDL_GameControllerGetAttached(controller->dev))) // we failed, but already have a working device - return true; - - // Else, we're changing devices, so close the controller - CONS_Debug(DBG_GAMELOGIC, M_GetText("Controller %d device is changing; closing controller...\n"), which); - Controller_Close(which); - } - - if (newdev == NULL) - { - CONS_Debug(DBG_GAMELOGIC, M_GetText("Controller %d: Couldn't open device - %s\n"), which, SDL_GetError()); - controller->started = false; - } - else - { - controller->dev = newdev; - controller->joydev = SDL_GameControllerGetJoystick(controller->dev); - controller->started = true; - - CONS_Debug(DBG_GAMELOGIC, M_GetText("Controller %d: %s\n"), which, SDL_GameControllerName(controller->dev)); - - #define GAMEPAD_TYPE_CASE(ctrl) \ - case SDL_CONTROLLER_TYPE_##ctrl: \ - controller->info->type = GAMEPAD_TYPE_##ctrl; \ - break - - switch (SDL_GameControllerGetType(newdev)) - { - GAMEPAD_TYPE_CASE(UNKNOWN); - GAMEPAD_TYPE_CASE(XBOX360); - GAMEPAD_TYPE_CASE(XBOXONE); - GAMEPAD_TYPE_CASE(PS3); - GAMEPAD_TYPE_CASE(PS4); - GAMEPAD_TYPE_CASE(PS5); - GAMEPAD_TYPE_CASE(NINTENDO_SWITCH_PRO); - GAMEPAD_TYPE_CASE(GOOGLE_STADIA); - GAMEPAD_TYPE_CASE(AMAZON_LUNA); - GAMEPAD_TYPE_CASE(VIRTUAL); - default: break; - } - - #undef GAMEPAD_BUTTON_CASE - - // Check the device vendor and product to find out what controller this actually is - Uint16 vendor = SDL_JoystickGetDeviceVendor(devindex); - Uint16 product = SDL_JoystickGetDeviceProduct(devindex); - - if (IsJoystickXboxSeriesXS(vendor, product)) - controller->info->type = GAMEPAD_TYPE_XBOX_SERIES_XS; - else if (IsJoystickXboxOneElite(vendor, product)) - controller->info->type = GAMEPAD_TYPE_XBOX_ELITE; - - CONS_Debug(DBG_GAMELOGIC, M_GetText(" Type: %s\n"), G_GamepadTypeToString(controller->info->type)); - - // Change the ring LEDs on Xbox 360 controllers - // TODO: Doesn't seem to work? - SDL_GameControllerSetPlayerIndex(controller->dev, which); - - // Check if rumble is supported - if (SDL_GameControllerHasRumble(controller->dev) == SDL_TRUE) - { - controller->info->rumble.supported = true; - CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: Yes\n")); - } - else - { - controller->info->rumble.supported = false; - CONS_Debug(DBG_GAMELOGIC, M_GetText(" Rumble supported: No\n"));; - } - - controller->info->connected = true; - } - - return controller->started; -} - -// Initializes a controller -static INT32 Controller_Init(SDL_GameController **newcontroller, UINT8 which, INT32 *index) -{ - ControllerInfo *info = &controllers[which]; - SDL_GameController *controller = NULL; - INT32 device = (*index); - - if (device && SDL_IsGameController(device - 1)) - controller = SDL_GameControllerOpen(device - 1); - if (newcontroller) - (*newcontroller) = controller; - - if (controller && info->dev == controller) // don't override an active device - (*index) = I_GetControllerIndex(info->dev) + 1; - else if (controller && Controller_OpenDevice(which, device)) - { - // SDL's device indexes are unstable, so cv_usegamepad may not match - // the actual device index. So let's cheat a bit and find the device's current index. - info->lastindex = I_GetControllerIndex(info->dev) + 1; - return 1; - } - else - { - (*index) = 0; - return 0; - } - - return -1; -} - -// Changes a controller's device -static void Controller_ChangeDevice(UINT8 num) -{ - SDL_GameController *newjoy = NULL; - - if (!Controller_Init(&newjoy, num, &cv_usegamepad[num].value) && controllers[num].lastindex) - Controller_Close(num); - - I_CloseInactiveController(newjoy); -} - -static boolean Controller_IsAnyUsingDevice(SDL_GameController *dev) -{ - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - if (controllers[i].dev == dev) - return true; - } - - return false; -} - -static boolean Controller_IsAnyOtherUsingDevice(SDL_GameController *dev, UINT8 thisjoy) -{ - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - if (i == thisjoy) - continue; - else if (controllers[i].dev == dev) - return true; - } - - return false; -} - -void I_ControllerDeviceAdded(INT32 which) -{ - if (!SDL_IsGameController(which)) - return; - - SDL_GameController *newjoy = SDL_GameControllerOpen(which); - - CONS_Debug(DBG_GAMELOGIC, "Gamepad device index %d added\n", which + 1); - - // Because SDL's device index is unstable, we're going to cheat here a bit: - // For the first controller setting that is NOT active: - // 1. Set cv_usegamepadX.value to the new device index (this does not change what is written to config.cfg) - // 2. Set OTHERS' cv_usegamepadX.value to THEIR new device index, because it likely changed - // * If device doesn't exist, switch cv_usegamepad back to default value (.string) - // * BUT: If that default index is being occupied, use ANOTHER cv_usegamepad's default value! - for (UINT8 this = 0; this < NUM_GAMEPADS && newjoy; this++) - { - if ((!controllers[this].dev || !SDL_GameControllerGetAttached(controllers[this].dev)) - && !Controller_IsAnyOtherUsingDevice(newjoy, this)) // don't override a currently active device - { - cv_usegamepad[this].value = which + 1; - - // Go through every other device - for (UINT8 other = 0; other < NUM_GAMEPADS; other++) - { - if (other == this) - { - // Don't change this controller's index - continue; - } - else if (controllers[other].dev) - { - // Update this controller's index if the device is open - cv_usegamepad[other].value = I_GetControllerIndex(controllers[other].dev) + 1; - } - else if (atoi(cv_usegamepad[other].string) != controllers[this].lastindex - && atoi(cv_usegamepad[other].string) != cv_usegamepad[this].value) - { - // If the user-set index for the other controller doesn't - // match this controller's current or former internal index, - // then use the other controller's internal index - cv_usegamepad[other].value = atoi(cv_usegamepad[other].string); - } - else if (atoi(cv_usegamepad[this].string) != controllers[this].lastindex - && atoi(cv_usegamepad[this].string) != cv_usegamepad[this].value) - { - // If the user-set index for this controller doesn't match - // its current or former internal index, then use this - // controller's internal index - cv_usegamepad[other].value = atoi(cv_usegamepad[this].string); - } - else - { - // Try again - cv_usegamepad[other].value = 0; - continue; - } - - break; - } - - break; - } - } - - // Was cv_usegamepad disabled in settings? - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - if (!strcmp(cv_usegamepad[i].string, "0") || !cv_usegamepad[i].value) - cv_usegamepad[i].value = 0; - else if (atoi(cv_usegamepad[i].string) <= I_NumGamepads() // don't mess if we intentionally set higher than NumJoys - && cv_usegamepad[i].value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usegamepad[i], cv_usegamepad[i].value); - } - - // Update all gamepads' init states - // This is a little wasteful since cv_usegamepad already calls this, but - // we need to do this in case CV_SetValue did nothing because the string was already same. - // if the device is already active, this should do nothing, effectively. - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - Controller_ChangeDevice(i); - CONS_Debug(DBG_GAMELOGIC, "Controller %d device index: %d\n", i, controllers[i].lastindex); - } - - if (M_OnGamepadMenu()) - M_UpdateGamepadMenu(); - - I_CloseInactiveController(newjoy); -} - -void I_ControllerDeviceRemoved(void) -{ - for (UINT8 this = 0; this < NUM_GAMEPADS; this++) - { - if (controllers[this].dev && !SDL_GameControllerGetAttached(controllers[this].dev)) - { - CONS_Debug(DBG_GAMELOGIC, "Controller %d removed, device index: %d\n", this, controllers[this].lastindex); - G_OnGamepadDisconnect(this); - Controller_Close(this); - } - - // Update the device indexes, because they likely changed - // * If device doesn't exist, switch cv_usegamepad back to default value (.string) - // * BUT: If that default index is being occupied, use ANOTHER cv_usegamepad's default value! - if (controllers[this].dev) - cv_usegamepad[this].value = controllers[this].lastindex = I_GetControllerIndex(controllers[this].dev) + 1; - else - { - for (UINT8 other = 0; other < NUM_GAMEPADS; other++) - { - if (other == this) - continue; - - if (atoi(cv_usegamepad[this].string) != controllers[other].lastindex) - { - // Update this internal index if this user-set index - // doesn't match the other's former internal index - cv_usegamepad[this].value = atoi(cv_usegamepad[this].string); - } - else if (atoi(cv_usegamepad[other].string) != controllers[other].lastindex) - { - // Otherwise, set this internal index to the other's - // user-set index, if the other user-set index is not the - // same as the other's former internal index - cv_usegamepad[this].value = atoi(cv_usegamepad[other].string); - } - else - { - // Try again - cv_usegamepad[this].value = 0; - continue; - } - - break; - } - } - - // Was cv_usegamepad disabled in settings? - if (!strcmp(cv_usegamepad[this].string, "0")) - cv_usegamepad[this].value = 0; - else if (atoi(cv_usegamepad[this].string) <= I_NumGamepads() // don't mess if we intentionally set higher than NumJoys - && cv_usegamepad[this].value) // update the cvar ONLY if a device exists - CV_SetValue(&cv_usegamepad[this], cv_usegamepad[this].value); - - CONS_Debug(DBG_GAMELOGIC, "Controller %d device index: %d\n", this, controllers[this].lastindex); - } - - if (M_OnGamepadMenu()) - M_UpdateGamepadMenu(); -} - -// Close the controller device if there isn't any controller using it -void I_CloseInactiveController(SDL_GameController *dev) -{ - if (!Controller_IsAnyUsingDevice(dev)) - SDL_GameControllerClose(dev); -} - -// Cheat to get the device index for a game controller handle -INT32 I_GetControllerIndex(SDL_GameController *dev) -{ - INT32 i, count = SDL_NumJoysticks(); - - for (i = 0; dev && i < count; i++) - { - SDL_GameController *test = SDL_GameControllerOpen(i); - if (test && test == dev) - return i; - else - I_CloseInactiveController(test); - } - - return -1; -} - -// Changes a gamepad's device -void I_ChangeGamepad(UINT8 which) -{ - if (which >= NUM_GAMEPADS) - return; - - if (controllers[which].started) - Controller_StopRumble(which); - - Controller_ChangeDevice(which); -} - -// Returns the name of a controller from its index -const char *I_GetGamepadName(INT32 joyindex) -{ - static char joyname[256]; - joyname[0] = '\0'; - - if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == GAMEPAD_INIT_FLAGS) - { - const char *tempname = SDL_GameControllerNameForIndex(joyindex - 1); - if (tempname) - strlcpy(joyname, tempname, sizeof joyname); - } - - return joyname; -} - -// Toggles a gamepad's digital axis setting -void I_SetGamepadDigital(UINT8 which, boolean enable) -{ - if (which >= NUM_GAMEPADS) - return; - - gamepads[which].digital = enable; -} - -static gamepad_t *Controller_GetFromID(SDL_JoystickID which, UINT8 *found) -{ - // Determine the joystick IDs for each current open controller - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - if (which == SDL_JoystickInstanceID(controllers[i].joydev)) - { - (*found) = i; - return &gamepads[i]; - } - } - - (*found) = UINT8_MAX; - - return NULL; -} - -void I_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint32 type) -{ - event_t event; - - gamepad_t *gamepad = Controller_GetFromID(evt.which, &event.which); - if (gamepad == NULL) - return; - - if (type == SDL_CONTROLLERBUTTONUP) - event.type = ev_gamepad_up; - else if (type == SDL_CONTROLLERBUTTONDOWN) - event.type = ev_gamepad_down; - else - return; - -#define GAMEPAD_BUTTON_CASE(btn) \ - case SDL_CONTROLLER_BUTTON_##btn: \ - event.key = GAMEPAD_BUTTON_##btn; \ - break - - switch (evt.button) - { - GAMEPAD_BUTTON_CASE(A); - GAMEPAD_BUTTON_CASE(B); - GAMEPAD_BUTTON_CASE(X); - GAMEPAD_BUTTON_CASE(Y); - GAMEPAD_BUTTON_CASE(BACK); - GAMEPAD_BUTTON_CASE(GUIDE); - GAMEPAD_BUTTON_CASE(START); - GAMEPAD_BUTTON_CASE(LEFTSTICK); - GAMEPAD_BUTTON_CASE(RIGHTSTICK); - GAMEPAD_BUTTON_CASE(LEFTSHOULDER); - GAMEPAD_BUTTON_CASE(RIGHTSHOULDER); - GAMEPAD_BUTTON_CASE(DPAD_UP); - GAMEPAD_BUTTON_CASE(DPAD_DOWN); - GAMEPAD_BUTTON_CASE(DPAD_LEFT); - GAMEPAD_BUTTON_CASE(DPAD_RIGHT); - GAMEPAD_BUTTON_CASE(MISC1); - GAMEPAD_BUTTON_CASE(PADDLE1); - GAMEPAD_BUTTON_CASE(PADDLE2); - GAMEPAD_BUTTON_CASE(PADDLE3); - GAMEPAD_BUTTON_CASE(PADDLE4); - GAMEPAD_BUTTON_CASE(TOUCHPAD); - default: return; - } - -#undef GAMEPAD_BUTTON_CASE - - D_PostEvent(&event); -} - -void I_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt) -{ - event_t event; - - gamepad_t *gamepad = Controller_GetFromID(evt.which, &event.which); - if (gamepad == NULL) - return; - -#define GAMEPAD_AXIS_CASE(btn) \ - case SDL_CONTROLLER_AXIS_##btn: \ - event.key = GAMEPAD_AXIS_##btn; \ - break - - switch (evt.axis) - { - GAMEPAD_AXIS_CASE(LEFTX); - GAMEPAD_AXIS_CASE(LEFTY); - GAMEPAD_AXIS_CASE(RIGHTX); - GAMEPAD_AXIS_CASE(RIGHTY); - GAMEPAD_AXIS_CASE(TRIGGERLEFT); - GAMEPAD_AXIS_CASE(TRIGGERRIGHT); - default: return; - } - -#undef GAMEPAD_AXIS_CASE - - event.type = ev_gamepad_axis; - event.x = evt.value; - - D_PostEvent(&event); -} - -static void Controller_StopRumble(UINT8 num) -{ - ControllerInfo *controller = &controllers[num]; - - controller->rumble.large_magnitude = 0; - controller->rumble.small_magnitude = 0; - controller->rumble.time_left = 0; - controller->rumble.expiration = 0; - - gamepad_t *gamepad = controller->info; - - gamepad->rumble.active = false; - gamepad->rumble.paused = false; - gamepad->rumble.data.large_magnitude = 0; - gamepad->rumble.data.small_magnitude = 0; - gamepad->rumble.data.duration = 0; - - if (gamepad->rumble.supported) - SDL_GameControllerRumble(controller->dev, 0, 0, 0); -} - -static void Controller_Close(UINT8 num) -{ - ControllerInfo *controller = &controllers[num]; - - // Close the game controller device - if (controller->dev) - { - Controller_StopRumble(num); - SDL_GameControllerClose(controller->dev); - } - - controller->dev = NULL; - controller->joydev = NULL; - controller->lastindex = -1; - controller->started = false; - - // Reset gamepad info - gamepad_t *gamepad = controller->info; - - if (gamepad) - { - gamepad->type = GAMEPAD_TYPE_UNKNOWN; - gamepad->connected = false; - gamepad->digital = false; - gamepad->rumble.supported = false; - - for (UINT8 i = 0; i < NUM_GAMEPAD_BUTTONS; i++) - gamepad->buttons[i] = 0; - - for (UINT8 i = 0; i < NUM_GAMEPAD_AXES; i++) - gamepad->axes[i] = 0; - } -} - -void I_ShutdownGamepads(void) -{ - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - Controller_Close(i); -} - -boolean I_RumbleSupported(void) -{ - return rumble_supported; -} - -static boolean Controller_Rumble(ControllerInfo *c) -{ - if (SDL_GameControllerRumble(c->dev, c->rumble.large_magnitude, c->rumble.small_magnitude, 0) == -1) - return false; - - return true; -} - -void I_ToggleControllerRumble(boolean unpause) -{ - if (!I_RumbleSupported() || rumble_paused == !unpause) - return; - - rumble_paused = !unpause; - - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - ControllerInfo *controller = &controllers[i]; - if (!controller->started || !controller->info->rumble.supported) - continue; - - if (rumble_paused) - SDL_GameControllerRumble(controller->dev, 0, 0, 0); - else if (!controller->info->rumble.paused) - { - if (!Controller_Rumble(controller)) - controller->rumble.expiration = controller->rumble.time_left = 0; - } - } -} - -void I_UpdateControllers(void) -{ - if (SDL_WasInit(GAMEPAD_INIT_FLAGS) != GAMEPAD_INIT_FLAGS) - return; - - for (UINT8 i = 0; i < NUM_GAMEPADS; i++) - { - ControllerInfo *controller = &controllers[i]; - if (!controller->started || !controller->info->rumble.supported || controller->info->rumble.paused) - continue; - - if (controller->rumble.expiration && - SDL_TICKS_PASSED(SDL_GetTicks(), controller->rumble.expiration)) - { - // Enough time has passed, so stop the effect - Controller_StopRumble(i); - } - } - - SDL_JoystickUpdate(); -} - -// Converts duration in tics to milliseconds -#define TICS_TO_MS(tics) ((INT32)(tics * (1000.0f/TICRATE))) - -boolean I_RumbleGamepad(UINT8 which, const haptic_t *effect) -{ - if (!I_RumbleSupported() || which >= NUM_GAMEPADS) - return false; - - ControllerInfo *controller = &controllers[which]; - if (!controller->started || !controller->info->rumble.supported) - return false; - - UINT16 duration = min(TICS_TO_MS(effect->duration), UINT16_MAX); - UINT16 large_magnitude = max(0, min(effect->large_magnitude, UINT16_MAX)); - UINT16 small_magnitude = max(0, min(effect->small_magnitude, UINT16_MAX)); - - CONS_Debug(DBG_GAMELOGIC, "Starting rumble effect for controller %d:\n", which); - CONS_Debug(DBG_GAMELOGIC, " Large motor magnitude: %f\n", large_magnitude / 65535.0f); - CONS_Debug(DBG_GAMELOGIC, " Small motor magnitude: %f\n", small_magnitude / 65535.0f); - - if (!duration) - CONS_Debug(DBG_GAMELOGIC, " Duration: forever\n"); - else - CONS_Debug(DBG_GAMELOGIC, " Duration: %dms\n", duration); - - controller->rumble.large_magnitude = large_magnitude; - controller->rumble.small_magnitude = small_magnitude; - - if (!rumble_paused && !Controller_Rumble(controller)) - { - Controller_StopRumble(which); - return false; - } - - controller->rumble.time_left = 0; - - if (duration) - controller->rumble.expiration = SDL_GetTicks() + duration; - else - controller->rumble.expiration = 0; - - // Update gamepad rumble info - gamepad_t *gamepad = controller->info; - - gamepad->rumble.active = true; - gamepad->rumble.paused = false; - gamepad->rumble.data.large_magnitude = effect->large_magnitude; - gamepad->rumble.data.small_magnitude = effect->small_magnitude; - gamepad->rumble.data.duration = effect->duration; - - return true; -} - -#undef TICS_TO_MS - -#define SET_MOTOR_FREQ(type) \ - if (!I_RumbleSupported() || which >= NUM_GAMEPADS) \ - return false; \ - \ - ControllerInfo *controller = &controllers[which]; \ - if (!controller->started || !controller->info->rumble.supported) \ - return false; \ - \ - gamepad_t *gamepad = controller->info; \ - if (gamepad->rumble.data.type##_magnitude == freq) \ - return true; \ - \ - UINT16 frequency = max(0, min(freq, UINT16_MAX)); \ - \ - controller->rumble.type##_magnitude = frequency; \ - \ - if (!rumble_paused && !gamepad->rumble.paused && !Controller_Rumble(controller)) \ - { \ - Controller_StopRumble(which); \ - return false; \ - } \ - \ - gamepad->rumble.data.type##_magnitude = freq; \ - gamepad->rumble.active = true; \ - return true - -boolean I_SetGamepadLargeMotorFreq(UINT8 which, fixed_t freq) -{ - SET_MOTOR_FREQ(large); -} - -boolean I_SetGamepadSmallMotorFreq(UINT8 which, fixed_t freq) -{ - SET_MOTOR_FREQ(small); -} - -void I_SetGamepadRumblePaused(UINT8 which, boolean pause) -{ - if (!I_RumbleSupported() || which >= NUM_GAMEPADS) - return; - - ControllerInfo *controller = &controllers[which]; - if (!controller->started || !controller->info->rumble.supported) - return; - - if (pause == controller->info->rumble.paused) - return; - else if (pause) - { - if (!rumble_paused) - SDL_GameControllerRumble(controller->dev, 0, 0, 0); - - if (controller->rumble.expiration) - { - controller->rumble.time_left = controller->rumble.expiration - SDL_GetTicks(); - controller->rumble.expiration = 0; - } - } - else - { - if (!rumble_paused) - SDL_GameControllerRumble(controller->dev, controller->rumble.large_magnitude, controller->rumble.small_magnitude, 0); - - if (controller->rumble.time_left) - controller->rumble.expiration = SDL_GetTicks() + controller->rumble.time_left; - } - - controller->info->rumble.paused = pause; -} - -boolean I_GetGamepadRumbleSupported(UINT8 which) -{ - if (!I_RumbleSupported() || which >= NUM_GAMEPADS) - return false; - - ControllerInfo *controller = &controllers[which]; - if (!controller->started) - return false; - - return controller->info->rumble.supported; -} - -boolean I_GetGamepadRumblePaused(UINT8 which) -{ - if (!I_RumbleSupported() || which >= NUM_GAMEPADS) - return false; - - ControllerInfo *controller = &controllers[which]; - if (!controller->started || !controller->info->rumble.supported) - return false; - - return controller->info->rumble.paused; -} - -void I_StopGamepadRumble(UINT8 which) -{ - if (!I_RumbleSupported() || which >= NUM_GAMEPADS) - return; - - ControllerInfo *controller = &controllers[which]; - if (!controller->started || !controller->info->rumble.supported) - return; - - Controller_StopRumble(which); -} -#endif diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index ee082fd5d..818d0f0c4 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -185,7 +185,6 @@ static char returnWadPath[256]; #include "../i_video.h" #include "../i_sound.h" #include "../i_system.h" -#include "../i_gamepad.h" #include "../i_threads.h" #include "../screen.h" //vid.WndParent #include "../d_net.h" @@ -194,6 +193,8 @@ static char returnWadPath[256]; #include "endtxt.h" #include "sdlmain.h" +#include "../i_joy.h" + #include "../m_argv.h" #include "../r_main.h" // Frame interpolation/uncapped @@ -211,6 +212,41 @@ static char returnWadPath[256]; #include "../byteptr.h" #endif +/** \brief The JoyReset function + + \param JoySet Joystick info to reset + + \return void +*/ +static void JoyReset(SDLJoyInfo_t *JoySet) +{ + if (JoySet->dev) + { + SDL_JoystickClose(JoySet->dev); + } + JoySet->dev = NULL; + JoySet->oldjoy = -1; + JoySet->axises = JoySet->buttons = JoySet->hats = JoySet->balls = 0; + //JoySet->scale +} + +/** \brief First joystick up and running +*/ +static INT32 joystick_started = 0; + +/** \brief SDL info about joystick 1 +*/ +SDLJoyInfo_t JoyInfo; + + +/** \brief Second joystick up and running +*/ +static INT32 joystick2_started = 0; + +/** \brief SDL inof about joystick 2 +*/ +SDLJoyInfo_t JoyInfo2; + #ifdef HAVE_TERMIOS static INT32 fdmouse2 = -1; static INT32 mouse2_started = 0; @@ -903,17 +939,721 @@ INT32 I_GetKey (void) return rc; } +// +// I_JoyScale +// +void I_JoyScale(void) +{ + Joystick.bGamepadStyle = cv_joyscale.value==0; + JoyInfo.scale = Joystick.bGamepadStyle?1:cv_joyscale.value; +} + +void I_JoyScale2(void) +{ + Joystick2.bGamepadStyle = cv_joyscale2.value==0; + JoyInfo2.scale = Joystick2.bGamepadStyle?1:cv_joyscale2.value; +} + +// Cheat to get the device index for a joystick handle +INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev) +{ + INT32 i, count = SDL_NumJoysticks(); + + for (i = 0; dev && i < count; i++) + { + SDL_Joystick *test = SDL_JoystickOpen(i); + if (test && test == dev) + return i; + else if (JoyInfo.dev != test && JoyInfo2.dev != test) + SDL_JoystickClose(test); + } + + return -1; +} + +/** \brief Joystick 1 buttons states +*/ +static UINT64 lastjoybuttons = 0; + +/** \brief Joystick 1 hats state +*/ +static UINT64 lastjoyhats = 0; + +/** \brief Shuts down joystick 1 + + + \return void + + +*/ +void I_ShutdownJoystick(void) +{ + INT32 i; + event_t event; + event.type=ev_keyup; + event.x = 0; + event.y = 0; + + lastjoybuttons = lastjoyhats = 0; + + // emulate the up of all joystick buttons + for (i=0;i= 0; i--) + { + joybuttons <<= 1; + if (SDL_JoystickGetButton(JoyInfo.dev,i)) + joybuttons |= 1; + } + + if (joybuttons != lastjoybuttons) + { + INT64 j = 1; // keep only bits that changed since last time + INT64 newbuttons = joybuttons ^ lastjoybuttons; + lastjoybuttons = joybuttons; + + for (i = 0; i < JOYBUTTONS; i++, j <<= 1) + { + if (newbuttons & j) // button changed state? + { + if (joybuttons & j) + event.type = ev_keydown; + else + event.type = ev_keyup; + event.key = KEY_JOY1 + i; + D_PostEvent(&event); + } + } + } +#endif + + for (i = JoyInfo.hats - 1; i >= 0; i--) + { + Uint8 hat = SDL_JoystickGetHat(JoyInfo.dev, i); + + if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i); + if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i); + if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i); + if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i); + } + + if (joyhats != lastjoyhats) + { + INT64 j = 1; // keep only bits that changed since last time + INT64 newhats = joyhats ^ lastjoyhats; + lastjoyhats = joyhats; + + for (i = 0; i < JOYHATS*4; i++, j <<= 1) + { + if (newhats & j) // hat changed state? + { + if (joyhats & j) + event.type = ev_keydown; + else + event.type = ev_keyup; + event.key = KEY_HAT1 + i; + D_PostEvent(&event); + } + } + } + +#if 0 + // send joystick axis positions + event.type = ev_joystick; + + for (i = JOYAXISSET - 1; i >= 0; i--) + { + event.key = i; + if (i*2 + 1 <= JoyInfo.axises) + axisx = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 0); + else axisx = 0; + if (i*2 + 2 <= JoyInfo.axises) + axisy = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 1); + else axisy = 0; + + + // -32768 to 32767 + axisx = axisx/32; + axisy = axisy/32; + + + if (Joystick.bGamepadStyle) + { + // gamepad control type, on or off, live or die + if (axisx < -(JOYAXISRANGE/2)) + event.x = -1; + else if (axisx > (JOYAXISRANGE/2)) + event.x = 1; + else event.x = 0; + if (axisy < -(JOYAXISRANGE/2)) + event.y = -1; + else if (axisy > (JOYAXISRANGE/2)) + event.y = 1; + else event.y = 0; + } + else + { + + axisx = JoyInfo.scale?((axisx/JoyInfo.scale)*JoyInfo.scale):axisx; + axisy = JoyInfo.scale?((axisy/JoyInfo.scale)*JoyInfo.scale):axisy; + +#ifdef SDL_JDEADZONE + if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0; + if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0; +#endif + + // analog control style , just send the raw data + event.x = axisx; // x axis + event.y = axisy; // y axis + } + D_PostEvent(&event); + } +#endif +} + +/** \brief Open joystick handle + + \param fname name of joystick + + \return axises + + +*/ +static int joy_open(int joyindex) +{ + SDL_Joystick *newdev = NULL; + int num_joy = 0; + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf(M_GetText("Joystick subsystem not started\n")); + return -1; + } + + if (joyindex <= 0) + return -1; + + num_joy = SDL_NumJoysticks(); + + if (num_joy == 0) + { + CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); + return -1; + } + + newdev = SDL_JoystickOpen(joyindex-1); + + // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging + // This indexing is SDL's responsibility and there's not much we can do about it. + // + // Example: + // 1. Plug Controller A -> Index 0 opened + // 2. Plug Controller B -> Index 1 opened + // 3. Unplug Controller A -> Index 0 closed, Index 1 active + // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed + // 5. Plug Controller B -> Index 0 opened + // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B + if (JoyInfo.dev) + { + if (JoyInfo.dev == newdev // same device, nothing to do + || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device + return JoyInfo.axises; + // Else, we're changing devices, so send neutral joy events + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device is changing; resetting events...\n"); + I_ShutdownJoystick(); + } + + JoyInfo.dev = newdev; + + if (JoyInfo.dev == NULL) + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: Couldn't open device - %s\n"), SDL_GetError()); + return -1; + } + else + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: %s\n"), SDL_JoystickName(JoyInfo.dev)); + JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev); + if (JoyInfo.axises > JOYAXISSET*2) + JoyInfo.axises = JOYAXISSET*2; + /* if (joyaxes<2) + { + I_OutputMsg("Not enought axes?\n"); + return 0; + }*/ + + JoyInfo.buttons = SDL_JoystickNumButtons(JoyInfo.dev); + if (JoyInfo.buttons > JOYBUTTONS) + JoyInfo.buttons = JOYBUTTONS; + + JoyInfo.hats = SDL_JoystickNumHats(JoyInfo.dev); + if (JoyInfo.hats > JOYHATS) + JoyInfo.hats = JOYHATS; + + JoyInfo.balls = SDL_JoystickNumBalls(JoyInfo.dev); + + //Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo.dev), "pad"); + + return JoyInfo.axises; + } +} + +//Joystick2 + +/** \brief Joystick 2 buttons states +*/ +static UINT64 lastjoy2buttons = 0; + +/** \brief Joystick 2 hats state +*/ +static UINT64 lastjoy2hats = 0; + +/** \brief Shuts down joystick 2 + + + \return void +*/ +void I_ShutdownJoystick2(void) +{ + INT32 i; + event_t event; + event.type = ev_keyup; + event.x = 0; + event.y = 0; + + lastjoy2buttons = lastjoy2hats = 0; + + // emulate the up of all joystick buttons + for (i = 0; i < JOYBUTTONS; i++) + { + event.key = KEY_2JOY1 + i; + D_PostEvent(&event); + } + + // emulate the up of all joystick hats + for (i = 0; i < JOYHATS*4; i++) + { + event.key = KEY_2HAT1 + i; + D_PostEvent(&event); + } + + // reset joystick position + event.type = ev_joystick2; + for (i = 0; i < JOYAXISSET; i++) + { + event.key = i; + D_PostEvent(&event); + } + + joystick2_started = 0; + JoyReset(&JoyInfo2); + + // don't shut down the subsystem here, because hotplugging +} + +void I_GetJoystick2Events(void) +{ + static event_t event = {0,0,0,0,false}; + INT32 i = 0; + UINT64 joyhats = 0; +#if 0 + INT64 joybuttons = 0; + INT32 axisx, axisy; +#endif + + if (!joystick2_started) + return; + + if (!JoyInfo2.dev) //I_ShutdownJoystick2(); + return; + + +#if 0 + //faB: look for as much buttons as g_input code supports, + // we don't use the others + for (i = JoyInfo2.buttons - 1; i >= 0; i--) + { + joybuttons <<= 1; + if (SDL_JoystickGetButton(JoyInfo2.dev,i)) + joybuttons |= 1; + } + + if (joybuttons != lastjoy2buttons) + { + INT64 j = 1; // keep only bits that changed since last time + INT64 newbuttons = joybuttons ^ lastjoy2buttons; + lastjoy2buttons = joybuttons; + + for (i = 0; i < JOYBUTTONS; i++, j <<= 1) + { + if (newbuttons & j) // button changed state? + { + if (joybuttons & j) + event.type = ev_keydown; + else + event.type = ev_keyup; + event.key = KEY_2JOY1 + i; + D_PostEvent(&event); + } + } + } +#endif + + for (i = JoyInfo2.hats - 1; i >= 0; i--) + { + Uint8 hat = SDL_JoystickGetHat(JoyInfo2.dev, i); + + if (hat & SDL_HAT_UP ) joyhats|=(UINT64)0x1<<(0 + 4*i); + if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i); + if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i); + if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i); + } + + if (joyhats != lastjoy2hats) + { + INT64 j = 1; // keep only bits that changed since last time + INT64 newhats = joyhats ^ lastjoy2hats; + lastjoy2hats = joyhats; + + for (i = 0; i < JOYHATS*4; i++, j <<= 1) + { + if (newhats & j) // hat changed state? + { + if (joyhats & j) + event.type = ev_keydown; + else + event.type = ev_keyup; + event.key = KEY_2HAT1 + i; + D_PostEvent(&event); + } + } + } + +#if 0 + // send joystick axis positions + event.type = ev_joystick2; + + for (i = JOYAXISSET - 1; i >= 0; i--) + { + event.key = i; + if (i*2 + 1 <= JoyInfo2.axises) + axisx = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 0); + else axisx = 0; + if (i*2 + 2 <= JoyInfo2.axises) + axisy = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 1); + else axisy = 0; + + // -32768 to 32767 + axisx = axisx/32; + axisy = axisy/32; + + if (Joystick2.bGamepadStyle) + { + // gamepad control type, on or off, live or die + if (axisx < -(JOYAXISRANGE/2)) + event.x = -1; + else if (axisx > (JOYAXISRANGE/2)) + event.x = 1; + else + event.x = 0; + if (axisy < -(JOYAXISRANGE/2)) + event.y = -1; + else if (axisy > (JOYAXISRANGE/2)) + event.y = 1; + else + event.y = 0; + } + else + { + + axisx = JoyInfo2.scale?((axisx/JoyInfo2.scale)*JoyInfo2.scale):axisx; + axisy = JoyInfo2.scale?((axisy/JoyInfo2.scale)*JoyInfo2.scale):axisy; + +#ifdef SDL_JDEADZONE + if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0; + if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0; +#endif + + // analog control style , just send the raw data + event.x = axisx; // x axis + event.y = axisy; // y axis + } + D_PostEvent(&event); + } +#endif +} + +/** \brief Open joystick handle + + \param fname name of joystick + + \return axises + + +*/ +static int joy_open2(int joyindex) +{ + SDL_Joystick *newdev = NULL; + int num_joy = 0; + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf(M_GetText("Joystick subsystem not started\n")); + return -1; + } + + if (joyindex <= 0) + return -1; + + num_joy = SDL_NumJoysticks(); + + if (num_joy == 0) + { + CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); + return -1; + } + + newdev = SDL_JoystickOpen(joyindex-1); + + // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging + // This indexing is SDL's responsibility and there's not much we can do about it. + // + // Example: + // 1. Plug Controller A -> Index 0 opened + // 2. Plug Controller B -> Index 1 opened + // 3. Unplug Controller A -> Index 0 closed, Index 1 active + // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed + // 5. Plug Controller B -> Index 0 opened + // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B + if (JoyInfo2.dev) + { + if (JoyInfo2.dev == newdev // same device, nothing to do + || (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device + return JoyInfo.axises; + // Else, we're changing devices, so send neutral joy events + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device is changing; resetting events...\n"); + I_ShutdownJoystick2(); + } + + JoyInfo2.dev = newdev; + + if (JoyInfo2.dev == NULL) + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: couldn't open device - %s\n"), SDL_GetError()); + return -1; + } + else + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev)); + JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev); + if (JoyInfo2.axises > JOYAXISSET*2) + JoyInfo2.axises = JOYAXISSET*2; +/* if (joyaxes<2) + { + I_OutputMsg("Not enought axes?\n"); + return 0; + }*/ + + JoyInfo2.buttons = SDL_JoystickNumButtons(JoyInfo2.dev); + if (JoyInfo2.buttons > JOYBUTTONS) + JoyInfo2.buttons = JOYBUTTONS; + + JoyInfo2.hats = SDL_JoystickNumHats(JoyInfo2.dev); + if (JoyInfo2.hats > JOYHATS) + JoyInfo2.hats = JOYHATS; + + JoyInfo2.balls = SDL_JoystickNumBalls(JoyInfo2.dev); + + //Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo2.dev), "pad"); + + return JoyInfo2.axises; + } +} + +// +// I_InitJoystick +// +void I_InitJoystick(void) +{ + SDL_Joystick *newjoy = NULL; + + //I_ShutdownJoystick(); + if (M_CheckParm("-nojoy")) + return; + + if (M_CheckParm("-noxinput")) + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + + if (M_CheckParm("-nohidapi")) + SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf("I_InitJoystick()...\n"); + + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); + return; + } + } + + if (cv_usejoystick.value) + newjoy = SDL_JoystickOpen(cv_usejoystick.value-1); + + if (newjoy && JoyInfo2.dev == newjoy) // don't override an active device + cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + else if (newjoy && joy_open(cv_usejoystick.value) != -1) + { + // SDL's device indexes are unstable, so cv_usejoystick may not match + // the actual device index. So let's cheat a bit and find the device's current index. + JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + joystick_started = 1; + } + else + { + if (JoyInfo.oldjoy) + I_ShutdownJoystick(); + cv_usejoystick.value = 0; + joystick_started = 0; + } + + if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) + SDL_JoystickClose(newjoy); +} + +void I_InitJoystick2(void) +{ + SDL_Joystick *newjoy = NULL; + + //I_ShutdownJoystick2(); + if (M_CheckParm("-nojoy")) + return; + + if (M_CheckParm("-noxinput")) + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + + if (M_CheckParm("-nohidapi")) + SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf("I_InitJoystick2()...\n"); + + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); + return; + } + } + + if (cv_usejoystick2.value) + newjoy = SDL_JoystickOpen(cv_usejoystick2.value-1); + + if (newjoy && JoyInfo.dev == newjoy) // don't override an active device + cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + else if (newjoy && joy_open2(cv_usejoystick2.value) != -1) + { + // SDL's device indexes are unstable, so cv_usejoystick may not match + // the actual device index. So let's cheat a bit and find the device's current index. + JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + joystick2_started = 1; + } + else + { + if (JoyInfo2.oldjoy) + I_ShutdownJoystick2(); + cv_usejoystick2.value = 0; + joystick2_started = 0; + } + + if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) + SDL_JoystickClose(newjoy); +} + static void I_ShutdownInput(void) { - I_ShutdownGamepads(); + // Yes, the name is misleading: these send neutral events to + // clean up the unplugged joystick's input + // Note these methods are internal to this file, not called elsewhere. + I_ShutdownJoystick(); + I_ShutdownJoystick2(); - if (SDL_WasInit(GAMEPAD_INIT_FLAGS) == GAMEPAD_INIT_FLAGS) + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) { - CONS_Printf("Shutting down game controller subsystems\n"); - SDL_QuitSubSystem(GAMEPAD_INIT_FLAGS); + CONS_Printf("Shutting down joy system\n"); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n"); } } +INT32 I_NumJoys(void) +{ + INT32 numjoy = 0; + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + numjoy = SDL_NumJoysticks(); + return numjoy; +} + +static char joyname[255]; // joystick name is straight from the driver + +const char *I_GetJoyName(INT32 joyindex) +{ + const char *tempname = NULL; + joyname[0] = 0; + joyindex--; //SDL's Joystick System starts at 0, not 1 + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + { + tempname = SDL_JoystickNameForIndex(joyindex); + if (tempname) + strncpy(joyname, tempname, 255); + } + return joyname; +} + #ifndef NOMUMBLE #ifdef HAVE_MUMBLE // Best Mumble positional audio settings: @@ -1373,6 +2113,23 @@ void I_StartupMouse2(void) #endif } +// +// I_Tactile +// +void I_Tactile(FFType pFFType, const JoyFF_t *FFEffect) +{ + // UNUSED. + (void)pFFType; + (void)FFEffect; +} + +void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect) +{ + // UNUSED. + (void)pFFType; + (void)FFEffect; +} + /** \brief empty ticcmd for player 1 */ static ticcmd_t emptycmd; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 81b20b51e..6e971a5d8 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -66,7 +66,7 @@ #include "../m_menu.h" #include "../d_main.h" #include "../s_sound.h" -#include "../i_gamepad.h" +#include "../i_joy.h" #include "../st_stuff.h" #include "../hu_stuff.h" #include "../g_game.h" @@ -449,10 +449,51 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText) static void VID_Command_Info_f (void) { +#if 0 + SDL2STUB(); +#else +#if 0 + const SDL_VideoInfo *videoInfo; + videoInfo = SDL_GetVideoInfo(); //Alam: Double-Check + if (videoInfo) + { + CONS_Printf("%s", M_GetText("Video Interface Capabilities:\n")); + if (videoInfo->hw_available) + CONS_Printf("%s", M_GetText(" Hardware surfaces\n")); + if (videoInfo->wm_available) + CONS_Printf("%s", M_GetText(" Window manager\n")); + //UnusedBits1 :6 + //UnusedBits2 :1 + if (videoInfo->blit_hw) + CONS_Printf("%s", M_GetText(" Accelerated blits HW-2-HW\n")); + if (videoInfo->blit_hw_CC) + CONS_Printf("%s", M_GetText(" Accelerated blits HW-2-HW with Colorkey\n")); + if (videoInfo->wm_available) + CONS_Printf("%s", M_GetText(" Accelerated blits HW-2-HW with Alpha\n")); + if (videoInfo->blit_sw) + { + CONS_Printf("%s", M_GetText(" Accelerated blits SW-2-HW\n")); + if (!M_CheckParm("-noblit")) videoblitok = SDL_TRUE; + } + if (videoInfo->blit_sw_CC) + CONS_Printf("%s", M_GetText(" Accelerated blits SW-2-HW with Colorkey\n")); + if (videoInfo->blit_sw_A) + CONS_Printf("%s", M_GetText(" Accelerated blits SW-2-HW with Alpha\n")); + if (videoInfo->blit_fill) + CONS_Printf("%s", M_GetText(" Accelerated Color filling\n")); + //UnusedBits3 :16 + if (videoInfo->video_mem) + CONS_Printf(M_GetText(" There is %i KB of video memory\n"), videoInfo->video_mem); + else + CONS_Printf("%s", M_GetText(" There no video memory for SDL\n")); + //*vfmt + } +#else if (!M_CheckParm("-noblit")) videoblitok = SDL_TRUE; - +#endif SurfaceInfo(bufSurface, M_GetText("Current Engine Mode")); SurfaceInfo(vidSurface, M_GetText("Current Video Mode")); +#endif } static void VID_Command_ModeList_f(void) @@ -487,6 +528,61 @@ static void VID_Command_Mode_f (void) setmodeneeded = modenum+1; // request vid mode change } +static inline void SDLJoyRemap(event_t *event) +{ + (void)event; +} + +static INT32 SDLJoyAxis(const Sint16 axis, evtype_t which) +{ + // -32768 to 32767 + INT32 raxis = axis/32; + if (which == ev_joystick) + { + if (Joystick.bGamepadStyle) + { + // gamepad control type, on or off, live or die + if (raxis < -(JOYAXISRANGE/2)) + raxis = -1; + else if (raxis > (JOYAXISRANGE/2)) + raxis = 1; + else + raxis = 0; + } + else + { + raxis = JoyInfo.scale!=1?((raxis/JoyInfo.scale)*JoyInfo.scale):raxis; + +#ifdef SDL_JDEADZONE + if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) + raxis = 0; +#endif + } + } + else if (which == ev_joystick2) + { + if (Joystick2.bGamepadStyle) + { + // gamepad control type, on or off, live or die + if (raxis < -(JOYAXISRANGE/2)) + raxis = -1; + else if (raxis > (JOYAXISRANGE/2)) + raxis = 1; + else raxis = 0; + } + else + { + raxis = JoyInfo2.scale!=1?((raxis/JoyInfo2.scale)*JoyInfo2.scale):raxis; + +#ifdef SDL_JDEADZONE + if (-SDL_JDEADZONE <= raxis && raxis <= SDL_JDEADZONE) + raxis = 0; +#endif + } + } + return raxis; +} + static void Impl_HandleWindowEvent(SDL_WindowEvent evt) { static SDL_bool firsttimeonmouse = SDL_TRUE; @@ -518,13 +614,13 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) // Tell game we got focus back, resume music if necessary window_notinfocus = false; if (!paused) - S_ResumeAudio(); + S_ResumeAudio(); //resume it - I_ToggleControllerRumble(true); - P_UnpauseRumble(NULL); - - if (!firsttimeonmouse && cv_usemouse.value) - I_StartupMouse(); + if (!firsttimeonmouse) + { + if (cv_usemouse.value) I_StartupMouse(); + } + //else firsttimeonmouse = SDL_FALSE; if (USE_MOUSEINPUT && !IgnoreMouse()) SDLdoGrabMouse(); @@ -533,45 +629,43 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) { // Tell game we lost focus, pause music window_notinfocus = true; - - if (!cv_playmusicifunfocused.value) + if (! cv_playmusicifunfocused.value) S_PauseAudio(); - if (!cv_playsoundsifunfocused.value) + if (! cv_playsoundsifunfocused.value) S_StopSounds(); if (!disable_mouse) + { SDLforceUngrabMouse(); - + } memset(gamekeydown, 0, NUMKEYS); // TODO this is a scary memset - I_ToggleControllerRumble(false); - if (P_AutoPause()) - P_PauseRumble(NULL); - if (MOUSE_MENU) + { SDLdoUngrabMouse(); + } } + } static void Impl_HandleKeyboardEvent(SDL_KeyboardEvent evt, Uint32 type) { event_t event; - if (type == SDL_KEYUP) + { event.type = ev_keyup; + } else if (type == SDL_KEYDOWN) + { event.type = ev_keydown; + } else + { return; - + } event.key = Impl_SDL_Scancode_To_Keycode(evt.keysym.scancode); - if (!event.key) - return; - event.repeated = (evt.repeat != 0); - event.which = 0; - - D_PostEvent(&event); + if (event.key) D_PostEvent(&event); } static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt) @@ -636,35 +730,32 @@ static void Impl_HandleMouseButtonEvent(SDL_MouseButtonEvent evt, Uint32 type) if (SDL_GetMouseFocus() != window || IgnoreMouse()) return; + /// \todo inputEvent.button.which if (USE_MOUSEINPUT) { if (type == SDL_MOUSEBUTTONUP) - event.type = ev_keyup; - else if (type == SDL_MOUSEBUTTONDOWN) - event.type = ev_keydown; - else - return; - - switch (evt.button) { - case SDL_BUTTON_LEFT: - event.key = KEY_MOUSE1+0; - break; - case SDL_BUTTON_RIGHT: - event.key = KEY_MOUSE1+1; - break; - case SDL_BUTTON_MIDDLE: - event.key = KEY_MOUSE1+2; - break; - case SDL_BUTTON_X1: - event.key = KEY_MOUSE1+3; - break; - case SDL_BUTTON_X2: - event.key = KEY_MOUSE1+4; - break; + event.type = ev_keyup; + } + else if (type == SDL_MOUSEBUTTONDOWN) + { + event.type = ev_keydown; + } + else return; + if (evt.button == SDL_BUTTON_MIDDLE) + event.key = KEY_MOUSE1+2; + else if (evt.button == SDL_BUTTON_RIGHT) + event.key = KEY_MOUSE1+1; + else if (evt.button == SDL_BUTTON_LEFT) + event.key = KEY_MOUSE1; + else if (evt.button == SDL_BUTTON_X1) + event.key = KEY_MOUSE1+3; + else if (evt.button == SDL_BUTTON_X2) + event.key = KEY_MOUSE1+4; + if (event.type == ev_keyup || event.type == ev_keydown) + { + D_PostEvent(&event); } - - D_PostEvent(&event); } } @@ -695,6 +786,111 @@ static void Impl_HandleMouseWheelEvent(SDL_MouseWheelEvent evt) } } +static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt) +{ + event_t event; + SDL_JoystickID joyid[2]; + + // Determine the Joystick IDs for each current open joystick + joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev); + joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev); + + evt.axis++; + event.key = event.x = event.y = INT32_MAX; + + if (evt.which == joyid[0]) + { + event.type = ev_joystick; + } + else if (evt.which == joyid[1]) + { + event.type = ev_joystick2; + } + else return; + //axis + if (evt.axis > JOYAXISSET*2) + return; + //vaule + if (evt.axis%2) + { + event.key = evt.axis / 2; + event.x = SDLJoyAxis(evt.value, event.type); + } + else + { + evt.axis--; + event.key = evt.axis / 2; + event.y = SDLJoyAxis(evt.value, event.type); + } + D_PostEvent(&event); +} + +#if 0 +static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt) +{ + event_t event; + SDL_JoystickID joyid[2]; + + // Determine the Joystick IDs for each current open joystick + joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev); + joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev); + + if (evt.hat >= JOYHATS) + return; // ignore hats with too high an index + + if (evt.which == joyid[0]) + { + event.key = KEY_HAT1 + (evt.hat*4); + } + else if (evt.which == joyid[1]) + { + event.key = KEY_2HAT1 + (evt.hat*4); + } + else return; + + // NOTE: UNFINISHED +} +#endif + +static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type) +{ + event_t event; + SDL_JoystickID joyid[2]; + + // Determine the Joystick IDs for each current open joystick + joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev); + joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev); + + if (evt.which == joyid[0]) + { + event.key = KEY_JOY1; + } + else if (evt.which == joyid[1]) + { + event.key = KEY_2JOY1; + } + else return; + if (type == SDL_JOYBUTTONUP) + { + event.type = ev_keyup; + } + else if (type == SDL_JOYBUTTONDOWN) + { + event.type = ev_keydown; + } + else return; + if (evt.button < JOYBUTTONS) + { + event.key += evt.button; + } + else return; + + SDLJoyRemap(&event); + if (event.type != ev_console) D_PostEvent(&event); +} + + + void I_GetEvent(void) { SDL_Event evt; @@ -732,18 +928,147 @@ void I_GetEvent(void) case SDL_MOUSEWHEEL: Impl_HandleMouseWheelEvent(evt.wheel); break; - case SDL_CONTROLLERAXISMOTION: - I_HandleControllerAxisEvent(evt.caxis); + case SDL_JOYAXISMOTION: + Impl_HandleJoystickAxisEvent(evt.jaxis); break; - case SDL_CONTROLLERBUTTONUP: - case SDL_CONTROLLERBUTTONDOWN: - I_HandleControllerButtonEvent(evt.cbutton, evt.type); +#if 0 + case SDL_JOYHATMOTION: + Impl_HandleJoystickHatEvent(evt.jhat) break; - case SDL_CONTROLLERDEVICEADDED: - I_ControllerDeviceAdded(evt.cdevice.which); +#endif + case SDL_JOYBUTTONUP: + case SDL_JOYBUTTONDOWN: + Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); break; - case SDL_CONTROLLERDEVICEREMOVED: - I_ControllerDeviceRemoved(); + case SDL_JOYDEVICEADDED: + { + SDL_Joystick *newjoy = SDL_JoystickOpen(evt.jdevice.which); + + CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1); + + // Because SDL's device index is unstable, we're going to cheat here a bit: + // For the first joystick setting that is NOT active: + // 1. Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg) + // 2. Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed + // * If device doesn't exist, switch cv_usejoystick back to default value (.string) + // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! + if (newjoy && (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev)) + && JoyInfo2.dev != newjoy) // don't override a currently active device + { + cv_usejoystick.value = evt.jdevice.which + 1; + + if (JoyInfo2.dev) + cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy + && atoi(cv_usejoystick2.string) != cv_usejoystick.value) + cv_usejoystick2.value = atoi(cv_usejoystick2.string); + else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy + && atoi(cv_usejoystick.string) != cv_usejoystick.value) + cv_usejoystick2.value = atoi(cv_usejoystick.string); + else // we tried... + cv_usejoystick2.value = 0; + } + else if (newjoy && (!JoyInfo2.dev || !SDL_JoystickGetAttached(JoyInfo2.dev)) + && JoyInfo.dev != newjoy) // don't override a currently active device + { + cv_usejoystick2.value = evt.jdevice.which + 1; + + if (JoyInfo.dev) + cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy + && atoi(cv_usejoystick.string) != cv_usejoystick2.value) + cv_usejoystick.value = atoi(cv_usejoystick.string); + else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy + && atoi(cv_usejoystick2.string) != cv_usejoystick2.value) + cv_usejoystick.value = atoi(cv_usejoystick2.string); + else // we tried... + cv_usejoystick.value = 0; + } + + // Was cv_usejoystick disabled in settings? + if (!strcmp(cv_usejoystick.string, "0") || !cv_usejoystick.value) + cv_usejoystick.value = 0; + else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); + + if (!strcmp(cv_usejoystick2.string, "0") || !cv_usejoystick2.value) + cv_usejoystick2.value = 0; + else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick2.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); + + // Update all joysticks' init states + // This is a little wasteful since cv_usejoystick already calls this, but + // we need to do this in case CV_SetValue did nothing because the string was already same. + // if the device is already active, this should do nothing, effectively. + I_InitJoystick(); + I_InitJoystick2(); + + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); + + // update the menu + if (currentMenu == &OP_JoystickSetDef) + M_SetupJoystickMenu(0); + + if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy) + SDL_JoystickClose(newjoy); + } + break; + case SDL_JOYDEVICEREMOVED: + if (JoyInfo.dev && !SDL_JoystickGetAttached(JoyInfo.dev)) + { + CONS_Debug(DBG_GAMELOGIC, "Joystick1 removed, device index: %d\n", JoyInfo.oldjoy); + I_ShutdownJoystick(); + } + + if (JoyInfo2.dev && !SDL_JoystickGetAttached(JoyInfo2.dev)) + { + CONS_Debug(DBG_GAMELOGIC, "Joystick2 removed, device index: %d\n", JoyInfo2.oldjoy); + I_ShutdownJoystick2(); + } + + // Update the device indexes, because they likely changed + // * If device doesn't exist, switch cv_usejoystick back to default value (.string) + // * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value! + if (JoyInfo.dev) + cv_usejoystick.value = JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1; + else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy) + cv_usejoystick.value = atoi(cv_usejoystick.string); + else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy) + cv_usejoystick.value = atoi(cv_usejoystick2.string); + else // we tried... + cv_usejoystick.value = 0; + + if (JoyInfo2.dev) + cv_usejoystick2.value = JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1; + else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy) + cv_usejoystick2.value = atoi(cv_usejoystick2.string); + else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy) + cv_usejoystick2.value = atoi(cv_usejoystick.string); + else // we tried... + cv_usejoystick2.value = 0; + + // Was cv_usejoystick disabled in settings? + if (!strcmp(cv_usejoystick.string, "0")) + cv_usejoystick.value = 0; + else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick, cv_usejoystick.value); + + if (!strcmp(cv_usejoystick2.string, "0")) + cv_usejoystick2.value = 0; + else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys + && cv_usejoystick2.value) // update the cvar ONLY if a device exists + CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value); + + CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy); + CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy); + + // update the menu + if (currentMenu == &OP_JoystickSetDef) + M_SetupJoystickMenu(0); break; case SDL_QUIT: LUA_HookBool(true, HOOK(GameQuit)); @@ -761,7 +1086,6 @@ void I_GetEvent(void) //SDL_memset(&event, 0, sizeof(event_t)); event.type = ev_mouse; event.key = 0; - event.which = 0; event.x = (INT32)lround(mousemovex * ((float)wwidth / (float)realwidth)); event.y = (INT32)lround(mousemovey * ((float)wheight / (float)realheight)); D_PostEvent(&event); @@ -800,9 +1124,15 @@ void I_OsPolling(void) if (consolevent) I_GetConsoleEvents(); + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + { + SDL_JoystickUpdate(); + I_GetJoystickEvents(); + I_GetJoystick2Events(); + } - I_UpdateControllers(); I_GetMouseEvents(); + I_GetEvent(); mod = SDL_GetModState(); diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index bb178b233..6b6e79d97 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -23,41 +23,59 @@ extern SDL_bool consolevent; extern SDL_bool framebuffer; #include "../m_fixed.h" -#include "../i_gamepad.h" -// SDL info about all controllers -typedef struct +// SDL2 stub macro +#ifdef _MSC_VER +#define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __FUNCTION__, __LINE__) +#else +#define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __func__, __LINE__) +#endif + +// So m_menu knows whether to store cv_usejoystick value or string +#define JOYSTICK_HOTPLUG + +/** \brief The JoyInfo_s struct + + info about joystick +*/ +typedef struct SDLJoyInfo_s { - boolean started; // started - int lastindex; // last gamepad ID + /// Joystick handle + SDL_Joystick *dev; + /// number of old joystick + int oldjoy; + /// number of axies + int axises; + /// scale of axises + INT32 scale; + /// number of buttons + int buttons; + /// number of hats + int hats; + /// number of balls + int balls; - SDL_GameController *dev; - SDL_Joystick *joydev; +} SDLJoyInfo_t; - gamepad_t *info; // pointer to gamepad info +/** \brief SDL info about joystick 1 +*/ +extern SDLJoyInfo_t JoyInfo; - struct { - Uint16 large_magnitude; - Uint16 small_magnitude; - Uint32 expiration, time_left; - } rumble; -} ControllerInfo; +/** \brief joystick axis deadzone +*/ +#define SDL_JDEADZONE 153 +#undef SDL_JDEADZONE -#define GAMEPAD_INIT_FLAGS (SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) +/** \brief SDL inof about joystick 2 +*/ +extern SDLJoyInfo_t JoyInfo2; -void I_UpdateControllers(void); +// So we can call this from i_video event loop +void I_ShutdownJoystick(void); +void I_ShutdownJoystick2(void); -void I_ControllerDeviceAdded(INT32 which); -void I_ControllerDeviceRemoved(void); - -void I_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint32 type); -void I_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt); - -INT32 I_GetControllerIndex(SDL_GameController *dev); -void I_CloseInactiveController(SDL_GameController *dev); -void I_CloseInactiveHapticDevice(SDL_Haptic *dev); - -void I_ToggleControllerRumble(boolean unpause); +// Cheat to get the device index for a joystick handle +INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev); void I_GetConsoleEvents(void); From 20724ad70bc44eee8d8c10c32cfbf88be94ef2f9 Mon Sep 17 00:00:00 2001 From: Arthur Date: Fri, 27 Jan 2023 09:46:02 -0500 Subject: [PATCH 79/89] Also retain PF_CANCARRY --- src/p_map.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_map.c b/src/p_map.c index f738cb97d..54e2003ba 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -262,7 +262,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) } else { - INT32 pflags = object->player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING); // Not identical to below... + INT32 pflags = object->player->pflags & (PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_THOKKED|PF_BOUNCING|PF_CANCARRY); // Not identical to below... UINT8 secondjump = object->player->secondjump; UINT16 tailsfly = object->player->powers[pw_tailsfly]; if (object->player->pflags & PF_GLIDING) From a6599c176d66f0c80af5fda458d4cdfe0a9296ae Mon Sep 17 00:00:00 2001 From: spherallic Date: Fri, 27 Jan 2023 16:49:53 +0100 Subject: [PATCH 80/89] Change gamepad defaults for 2.2.11 --- src/g_input.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/g_input.c b/src/g_input.c index 79bd2a4a2..262e68c6a 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -744,31 +744,31 @@ void G_DefineDefaultControls(void) gamecontroldefault[i][GC_CUSTOM1 ][1] = KEY_JOY1+1; // B gamecontroldefault[i][GC_CUSTOM2 ][1] = KEY_JOY1+3; // Y gamecontroldefault[i][GC_CUSTOM3 ][1] = KEY_JOY1+8; // Left Stick - gamecontroldefault[i][GC_CENTERVIEW ][1] = KEY_JOY1+9; // Right Stick - gamecontroldefault[i][GC_WEAPONPREV ][1] = KEY_JOY1+4; // LB - gamecontroldefault[i][GC_WEAPONNEXT ][1] = KEY_JOY1+5; // RB + gamecontroldefault[i][GC_CAMTOGGLE ][1] = KEY_JOY1+4; // LB + gamecontroldefault[i][GC_CENTERVIEW ][1] = KEY_JOY1+5; // RB gamecontroldefault[i][GC_SCREENSHOT ][1] = KEY_JOY1+6; // Back gamecontroldefault[i][GC_SYSTEMMENU ][0] = KEY_JOY1+7; // Start - gamecontroldefault[i][GC_CAMTOGGLE ][1] = KEY_HAT1+0; // D-Pad Up - gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = KEY_HAT1+1; // D-Pad Down - gamecontroldefault[i][GC_TOSSFLAG ][1] = KEY_HAT1+2; // D-Pad Left - gamecontroldefault[i][GC_SCORES ][1] = KEY_HAT1+3; // D-Pad Right + gamecontroldefault[i][GC_WEAPONPREV ][1] = KEY_HAT1+2; // D-Pad Left + gamecontroldefault[i][GC_WEAPONNEXT ][1] = KEY_HAT1+3; // D-Pad Right + gamecontroldefault[i][GC_VIEWPOINTNEXT][1] = KEY_JOY1+9; // Right Stick + gamecontroldefault[i][GC_TOSSFLAG ][1] = KEY_HAT1+0; // D-Pad Up + gamecontroldefault[i][GC_SCORES ][1] = KEY_HAT1+1; // D-Pad Down // Second player controls only have joypad defaults - gamecontrolbisdefault[i][GC_JUMP ][1] = KEY_2JOY1+0; // A - gamecontrolbisdefault[i][GC_SPIN ][1] = KEY_2JOY1+2; // X - gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = KEY_2JOY1+1; // B - gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = KEY_2JOY1+3; // Y - gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = KEY_2JOY1+8; // Left Stick - gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = KEY_2JOY1+9; // Right Stick - gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = KEY_2JOY1+4; // LB - gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = KEY_2JOY1+5; // RB - gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = KEY_2JOY1+6; // Back + gamecontrolbisdefault[i][GC_JUMP ][1] = KEY_2JOY1+0; // A + gamecontrolbisdefault[i][GC_SPIN ][1] = KEY_2JOY1+2; // X + gamecontrolbisdefault[i][GC_CUSTOM1 ][1] = KEY_2JOY1+1; // B + gamecontrolbisdefault[i][GC_CUSTOM2 ][1] = KEY_2JOY1+3; // Y + gamecontrolbisdefault[i][GC_CUSTOM3 ][1] = KEY_2JOY1+8; // Left Stick + gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = KEY_2JOY1+4; // LB + gamecontrolbisdefault[i][GC_CENTERVIEW ][1] = KEY_2JOY1+5; // RB + gamecontrolbisdefault[i][GC_SCREENSHOT ][1] = KEY_2JOY1+6; // Back //gamecontrolbisdefault[i][GC_SYSTEMMENU ][0] = KEY_2JOY1+7; // Start - gamecontrolbisdefault[i][GC_CAMTOGGLE ][1] = KEY_2HAT1+0; // D-Pad Up - gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = KEY_2HAT1+1; // D-Pad Down - gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = KEY_2HAT1+2; // D-Pad Left - //gamecontrolbisdefault[i][GC_SCORES ][1] = KEY_2HAT1+3; // D-Pad Right + gamecontrolbisdefault[i][GC_WEAPONPREV ][1] = KEY_2HAT1+2; // D-Pad Left + gamecontrolbisdefault[i][GC_WEAPONNEXT ][1] = KEY_2HAT1+3; // D-Pad Right + gamecontrolbisdefault[i][GC_VIEWPOINTNEXT][1] = KEY_2JOY1+9; // Right Stick + gamecontrolbisdefault[i][GC_TOSSFLAG ][1] = KEY_2HAT1+0; // D-Pad Up + //gamecontrolbisdefault[i][GC_SCORES ][1] = KEY_2HAT1+1; // D-Pad Down } } From b64dac714f39dadf9f44e474558c81d6420032d0 Mon Sep 17 00:00:00 2001 From: Jaime Ita Passos Date: Sat, 28 Jan 2023 00:12:29 -0300 Subject: [PATCH 81/89] Fix -Wnon-literal-null-conversion and -Wconstant-conversion warnings --- src/p_spec.c | 2 +- src/taglist.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 5c9caa82f..c104ce6c5 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4375,7 +4375,7 @@ sector_t *P_FindPlayerTrigger(player_t *player, line_t *sourceline) return loopsector; } - return false; + return NULL; } boolean P_IsPlayerValid(size_t playernum) diff --git a/src/taglist.c b/src/taglist.c index 305b05f04..405007614 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -472,5 +472,5 @@ mtag_t Tag_NextUnused(mtag_t start) start++; } - return MAXTAGS; + return (mtag_t)MAXTAGS; } From 4f8d2fcdc840b760e9d8c577b76498e4925b49a1 Mon Sep 17 00:00:00 2001 From: cobalt2727 <60624944+cobalt2727@users.noreply.github.com> Date: Wed, 8 Feb 2023 14:46:09 -0500 Subject: [PATCH 82/89] fix ARM builds being completely broken If merged, this is the quickest approach to resolve https://github.com/STJr/SRB2/issues/513 ...alternatively, we could just remove the option altogether but I don't have the time right now to test whether it's needed or not To the best of my knowledge, this covers all possible amd64 `CMAKE_SYSTEM_PROCESSOR` values based on what I'm seeing at https://stackoverflow.com/questions/70475665/what-are-the-possible-values-of-cmake-system-processor --- src/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c98019e8..b47706db0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -127,7 +127,9 @@ endif() # Compatibility flag with later versions of GCC # We should really fix our code to not need this if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - target_compile_options(SRB2SDL2 PRIVATE -mno-ms-bitfields) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|x64|amd64|AMD64|em64t|EM64T)") + target_compile_options(SRB2SDL2 PRIVATE -mno-ms-bitfields) + endif() target_compile_options(SRB2SDL2 PRIVATE -Wno-trigraphs) endif() From aba57612d57de590b1b9db8891d1332fb1857460 Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Sun, 19 Feb 2023 07:27:16 +0100 Subject: [PATCH 83/89] -Only print deprecated map effect warnings once on map load -Add missing unsupported effect warnings to P_WriteTextmap -Apply spikes MSF_TRIGGERSPECIAL_TOUCH hack in UDMF too --- src/p_setup.c | 20 ++++++++++++++++++++ src/p_spec.c | 29 +++++++++-------------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index b0054119b..0330ea1e5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2270,6 +2270,9 @@ static void P_WriteTextmap(void) case 10: CONS_Alert(CONS_WARNING, M_GetText("Sector %s has ring drainer effect, which is not supported in UDMF. Use linedef type 462 instead.\n"), sizeu1(i)); break; + case 15: + CONS_Alert(CONS_WARNING, M_GetText("Sector %s has bouncy FOF effect, which is not supported in UDMF. Use linedef type 76 instead.\n"), sizeu1(i)); + break; default: break; } @@ -2285,6 +2288,12 @@ static void P_WriteTextmap(void) case 9: CONS_Alert(CONS_WARNING, M_GetText("Sector %s has Egg Capsule type, which is not supported in UDMF. Use linedef type 464 instead.\n"), sizeu1(i)); break; + case 10: + CONS_Alert(CONS_WARNING, M_GetText("Sector %s has special stage time/spheres requirements effect, which is not supported in UDMF. Use the SpecialStageTime and SpecialStageSpheres level header options instead.\n"), sizeu1(i)); + break; + case 11: + CONS_Alert(CONS_WARNING, M_GetText("Sector %s has custom global gravity effect, which is not supported in UDMF. Use the Gravity level header option instead.\n"), sizeu1(i)); + break; default: break; } @@ -6008,6 +6017,9 @@ static void P_ConvertBinarySectorTypes(void) case 14: //Non-ramp sector sectors[i].specialflags |= SSF_NOSTEPDOWN; break; + case 15: //Bouncy FOF + CONS_Alert(CONS_WARNING, M_GetText("Deprecated bouncy FOF sector type detected. Please use linedef type 76 instead.\n")); + break; default: break; } @@ -6040,11 +6052,13 @@ static void P_ConvertBinarySectorTypes(void) sectors[i].triggerer = TO_PLAYER; break; case 6: //Trigger linedef executor (Emerald check) + CONS_Alert(CONS_WARNING, M_GetText("Deprecated emerald check sector type detected. Please use linedef types 337-339 instead.\n")); sectors[i].triggertag = tag; sectors[i].flags &= ~MSF_TRIGGERLINE_PLANE; sectors[i].triggerer = TO_PLAYEREMERALDS; break; case 7: //Trigger linedef executor (NiGHTS mare) + CONS_Alert(CONS_WARNING, M_GetText("Deprecated NiGHTS mare sector type detected. Please use linedef types 340-342 instead.\n")); sectors[i].triggertag = tag; sectors[i].flags &= ~MSF_TRIGGERLINE_PLANE; sectors[i].triggerer = TO_PLAYERNIGHTS; @@ -6052,6 +6066,12 @@ static void P_ConvertBinarySectorTypes(void) case 8: //Check for linedef executor on FOFs sectors[i].flags |= MSF_TRIGGERLINE_MOBJ; break; + case 10: //Special stage time/spheres requirements + CONS_Alert(CONS_WARNING, M_GetText("Deprecated sector type for special stage requirements detected. Please use the SpecialStageTime and SpecialStageSpheres level header options instead.\n")); + break; + case 11: //Custom global gravity + CONS_Alert(CONS_WARNING, M_GetText("Deprecated sector type for global gravity detected. Please use the Gravity level header option instead.\n")); + break; default: break; } diff --git a/src/p_spec.c b/src/p_spec.c index 6a52f19e8..2aec1ae36 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1760,13 +1760,11 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller { if (caller->triggerer == TO_PLAYEREMERALDS) { - CONS_Alert(CONS_WARNING, M_GetText("Deprecated emerald check sector type detected. Please use linedef types 337-339 instead.\n")); if (!(ALL7EMERALDS(emeralds))) return false; } else if (caller->triggerer == TO_PLAYERNIGHTS) { - CONS_Alert(CONS_WARNING, M_GetText("Deprecated NiGHTS mare sector type detected. Please use linedef types 340-342 instead.\n")); if (!P_CheckPlayerMareOld(triggerline)) return false; } @@ -5970,8 +5968,6 @@ static inline void P_AddCameraScanner(sector_t *sourcesec, sector_t *actionsecto { elevator_t *elevator; // Why not? LOL - CONS_Alert(CONS_WARNING, M_GetText("Detected a camera scanner effect (linedef type 5). This effect is deprecated and will be removed in the future!\n")); - // create and initialize new elevator thinker elevator = Z_Calloc(sizeof (*elevator), PU_LEVSPEC, NULL); P_AddThinker(THINK_MAIN, &elevator->thinker); @@ -6226,22 +6222,21 @@ void P_SpawnSpecials(boolean fromnetsave) circuitmap = true; } - if (!sector->special) + if (sector->damagetype == SD_SPIKE) { + //Terrible hack to replace an even worse hack: + //Spike damage automatically sets MSF_TRIGGERSPECIAL_TOUCH. + //Yes, this also affects other specials on the same sector. Sorry. + sector->flags |= MSF_TRIGGERSPECIAL_TOUCH; + } + + // Process deprecated binary sector specials + if (udmf || !sector->special) continue; // Process Section 1 switch(GETSECSPECIAL(sector->special, 1)) { - case 5: // Spikes - //Terrible hack to replace an even worse hack: - //Spike damage automatically sets MSF_TRIGGERSPECIAL_TOUCH. - //Yes, this also affects other specials on the same sector. Sorry. - sector->flags |= MSF_TRIGGERSPECIAL_TOUCH; - break; case 15: // Bouncy FOF - if (udmf) - break; - CONS_Alert(CONS_WARNING, M_GetText("Deprecated bouncy FOF sector type detected. Please use linedef type 76 instead.\n")); CheckForBouncySector = true; break; } @@ -6250,17 +6245,11 @@ void P_SpawnSpecials(boolean fromnetsave) switch(GETSECSPECIAL(sector->special, 2)) { case 10: // Time for special stage - if (udmf) - break; - CONS_Alert(CONS_WARNING, M_GetText("Deprecated sector type for special stage requirements detected. Please use the SpecialStageTime and SpecialStageSpheres level header options instead.\n")); sstimer = (sector->floorheight>>FRACBITS) * TICRATE + 6; // Time to finish ssspheres = sector->ceilingheight>>FRACBITS; // Ring count for special stage break; case 11: // Custom global gravity! - if (udmf) - break; - CONS_Alert(CONS_WARNING, M_GetText("Deprecated sector type for global gravity detected. Please use the Gravity level header option instead.\n")); gravity = sector->floorheight/1000; break; } From d58dad7f982e9486afd6945df2f223ed79abf721 Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Sat, 25 Feb 2023 11:58:12 +0100 Subject: [PATCH 84/89] Move emblem float option to args[1], since args[0] is already occupied for NiGHTS emblems --- extras/conf/udb/Includes/SRB222_things.cfg | 2 +- src/p_mobj.c | 5 ++++- src/p_setup.c | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/extras/conf/udb/Includes/SRB222_things.cfg b/extras/conf/udb/Includes/SRB222_things.cfg index 1de661e29..b4508c91e 100644 --- a/extras/conf/udb/Includes/SRB222_things.cfg +++ b/extras/conf/udb/Includes/SRB222_things.cfg @@ -4546,7 +4546,7 @@ udmf sprite = "EMBMA0"; width = 16; height = 30; - arg0 + arg1 { title = "Float?"; type = 11; diff --git a/src/p_mobj.c b/src/p_mobj.c index 635d4f42b..412ef7ddb 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11849,7 +11849,6 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt case MT_EMERHUNT: case MT_EMERALDSPAWN: case MT_TOKEN: - case MT_EMBLEM: case MT_RING: case MT_REDTEAMRING: case MT_BLUETEAMRING: @@ -11861,6 +11860,10 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt offset += mthing->args[0] ? 0 : 24*FRACUNIT; break; + case MT_EMBLEM: + offset += mthing->args[1] ? 0 : 24 * FRACUNIT; + break; + // Remaining objects. default: if (P_WeaponOrPanel(mobjtype)) diff --git a/src/p_setup.c b/src/p_setup.c index b0054119b..1c052a148 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -6288,7 +6288,6 @@ static void P_ConvertBinaryThingTypes(void) case 312: //Emerald token case 320: //Emerald hunt location case 321: //Match chaos emerald spawn - case 322: //Emblem case 330: //Bounce ring panel case 331: //Rail ring panel case 332: //Automatic ring panel @@ -6301,6 +6300,9 @@ static void P_ConvertBinaryThingTypes(void) case 1800: //Coin mapthings[i].args[0] = !(mapthings[i].options & MTF_AMBUSH); break; + case 322: //Emblem + mapthings[i].args[1] = !(mapthings[i].options & MTF_AMBUSH); + break; case 409: //Extra life monitor mapthings[i].args[2] = !(mapthings[i].options & (MTF_AMBUSH|MTF_OBJECTSPECIAL)); break; From 283f7e8919ca62ef9d6d6d1d885858f6332aaf48 Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Sat, 25 Feb 2023 13:24:14 +0100 Subject: [PATCH 85/89] Linedef type 14 UDMF conversion: Don't fill stringargs[0] if toptexture is empty --- src/p_setup.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/p_setup.c b/src/p_setup.c index b0054119b..c9e578922 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -4209,7 +4209,8 @@ static void P_ConvertBinaryLinedefTypes(void) lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; lines[i].args[1] = sides[lines[i].sidenum[0]].rowoffset >> FRACBITS; lines[i].args[2] = !!(lines[i].flags & ML_SKEWTD); - P_WriteConstant(sides[lines[i].sidenum[0]].toptexture, &lines[i].stringargs[0]); + if (sides[lines[i].sidenum[0]].toptexture) + P_WriteConstant(sides[lines[i].sidenum[0]].toptexture, &lines[i].stringargs[0]); break; case 16: //Minecart parameters lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS; From 6e7ff6972096f37ce618507ec0cdc60d272c5dee Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Sat, 25 Feb 2023 15:03:52 +0100 Subject: [PATCH 86/89] Re-add code that sets MF2_AMBUSH if MTF_AMBUSH is set, but only for binary maps --- src/p_mobj.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/p_mobj.c b/src/p_mobj.c index 635d4f42b..f8f4ee2a6 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -13277,6 +13277,23 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean return true; } +// Pre-UDMF backwards compatibility stuff. Remove for 2.3 +static void P_SetAmbush(mapthing_t *mthing, mobj_t *mobj) +{ + if (mobj->type == MT_NIGHTSBUMPER + || mobj->type == MT_AXIS + || mobj->type == MT_AXISTRANSFER + || mobj->type == MT_AXISTRANSFERLINE + || mobj->type == MT_NIGHTSBUMPER + || mobj->type == MT_STARPOST) + return; + + if ((mthing->options & MTF_OBJECTSPECIAL) && (mobj->flags & MF_PUSHABLE)) + return; + + mobj->flags2 |= MF2_AMBUSH; +} + static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, fixed_t z, mobjtype_t i) { mobj_t *mobj = NULL; @@ -13299,6 +13316,9 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, mthing->mobj = mobj; + if (!udmf && (mthing->options & MTF_AMBUSH)) + P_SetAmbush(mthing, mobj); + // Generic reverse gravity for individual objects flag. if (mthing->options & MTF_OBJECTFLIP) { From bb9e7045c5c3f5f433e7d9fb2be5ce9046f9298f Mon Sep 17 00:00:00 2001 From: MascaraSnake Date: Sat, 25 Feb 2023 15:43:51 +0100 Subject: [PATCH 87/89] Fix wrong flag being checked in P_ProcessTeamBase --- src/p_spec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_spec.c b/src/p_spec.c index 6a52f19e8..b0840b30c 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4653,7 +4653,7 @@ static void P_ProcessTeamBase(player_t *player, boolean redteam) // Make sure the team still has their own // flag at their base so they can score. - if (!P_IsFlagAtBase(redteam ? MT_BLUEFLAG : MT_REDFLAG)) + if (!P_IsFlagAtBase(redteam ? MT_REDFLAG : MT_BLUEFLAG)) return; HU_SetCEchoFlags(V_AUTOFADEOUT|V_ALLOWLOWERCASE); From c42ef9f1be3157f468f0ae881a855538c6fa540d Mon Sep 17 00:00:00 2001 From: Eidolon Date: Sun, 26 Feb 2023 16:47:00 -0600 Subject: [PATCH 88/89] Fix IT and ctf flag sign interpolation --- src/info.c | 4 ++-- src/p_user.c | 57 +++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/info.c b/src/info.c index 174eb5cc4..b55302170 100644 --- a/src/info.c +++ b/src/info.c @@ -3429,10 +3429,10 @@ state_t states[NUMSTATES] = {SPR_LCKN, 2|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LOCKONINF3 {SPR_LCKN, 3|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LOCKONINF4 - {SPR_TTAG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_TTAG + {SPR_TTAG, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_TTAG // CTF Sign - {SPR_GFLG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG + {SPR_GFLG, FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG // Finish flag {SPR_FNSF, FF_TRANS30, -1, {NULL}, 0, 0, S_NULL}, // S_FINISHFLAG diff --git a/src/p_user.c b/src/p_user.c index bdb164484..9d16bdda2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3108,14 +3108,25 @@ static void P_DoPlayerHeadSigns(player_t *player) if (G_TagGametype()) { // If you're "IT", show a big "IT" over your head for others to see. - if (player->pflags & PF_TAGIT) + if (player->pflags & PF_TAGIT && P_IsLocalPlayer(player)) { - if (!P_IsLocalPlayer(player)) // Don't display it on your own view. + mobj_t* it = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TAG); + it->x = player->mo->x; + it->y = player->mo->y; + it->z = player->mo->z; + it->old_x = player->mo->old_x; + it->old_y = player->mo->old_y; + it->old_z = player->mo->old_z; + + if (!(player->mo->eflags & MFE_VERTICALFLIP)) { - if (!(player->mo->eflags & MFE_VERTICALFLIP)) - P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height, MT_TAG); - else - P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z - mobjinfo[MT_TAG].height, MT_TAG)->eflags |= MFE_VERTICALFLIP; + it->z += player->mo->height; + it->old_z += player->mo->height; + } + else + { + it->z -= mobjinfo[MT_TAG].height; + it->old_z -= mobjinfo[MT_TAG].height; } } } @@ -3123,17 +3134,34 @@ static void P_DoPlayerHeadSigns(player_t *player) { // Spawn a got-flag message over the head of the player that // has it (but not on your own screen if you have the flag). - if (splitscreen || player != &players[consoleplayer]) + if (splitscreen || player != &players[consoleplayer] || true) { - mobj_t *sign = P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, - player->mo->z+player->mo->momz, MT_GOTFLAG); - if (player->mo->eflags & MFE_VERTICALFLIP) + fixed_t zofs; + mobj_t *sign; + boolean player_is_flipped = (player->mo->eflags & MFE_VERTICALFLIP) > 0; + + zofs = player->mo->momz; + if (player_is_flipped) { - sign->z += player->mo->height-P_GetPlayerHeight(player)-mobjinfo[MT_GOTFLAG].height-FixedMul(16*FRACUNIT, player->mo->scale); - sign->eflags |= MFE_VERTICALFLIP; + zofs += player->mo->height - P_GetPlayerHeight(player) - mobjinfo[MT_GOTFLAG].height - FixedMul(16 * FRACUNIT, player->mo->scale); } else - sign->z += P_GetPlayerHeight(player)+FixedMul(16*FRACUNIT, player->mo->scale); + { + zofs += P_GetPlayerHeight(player) + FixedMul(16 * FRACUNIT, player->mo->scale); + } + + sign = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_GOTFLAG); + sign->x = player->mo->x; + sign->y = player->mo->y; + sign->z = player->mo->z + zofs; + sign->old_x = player->mo->old_x; + sign->old_y = player->mo->old_y; + sign->old_z = player->mo->old_z + zofs; + + if (player_is_flipped) + { + sign->eflags |= MFE_VERTICALFLIP; + } if (player->gotflag & GF_REDFLAG) sign->frame = 1|FF_FULLBRIGHT; @@ -12028,7 +12056,6 @@ void P_PlayerThink(player_t *player) P_DoBubbleBreath(player); // Spawn Sonic's bubbles P_CheckUnderwaterAndSpaceTimer(player); // Display the countdown drown numbers! P_CheckInvincibilityTimer(player); // Spawn Invincibility Sparkles - P_DoPlayerHeadSigns(player); // Spawn Tag/CTF signs over player's head #if 1 // "Blur" a bit when you have speed shoes and are going fast enough @@ -12893,6 +12920,8 @@ void P_PlayerAfterThink(player_t *player) } } } + + P_DoPlayerHeadSigns(player); // Spawn Tag/CTF signs over player's head } void P_SetPlayerAngle(player_t *player, angle_t angle) From 851ca92aaaefc576d3be0fcca9a844bc6447a0fd Mon Sep 17 00:00:00 2001 From: Monster Iestyn Date: Tue, 28 Feb 2023 17:51:26 +0000 Subject: [PATCH 89/89] revert Eidolon's edits for testing the IT/CTF flag sign fixes, they were left in by mistake --- src/p_user.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 26abc0220..60a0f5106 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1,4 +1,3 @@ - // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. @@ -3108,7 +3107,7 @@ static void P_DoPlayerHeadSigns(player_t *player) if (G_TagGametype()) { // If you're "IT", show a big "IT" over your head for others to see. - if (player->pflags & PF_TAGIT && P_IsLocalPlayer(player)) + if (player->pflags & PF_TAGIT && !P_IsLocalPlayer(player)) { mobj_t* it = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TAG); it->x = player->mo->x; @@ -3134,7 +3133,7 @@ static void P_DoPlayerHeadSigns(player_t *player) { // Spawn a got-flag message over the head of the player that // has it (but not on your own screen if you have the flag). - if (splitscreen || player != &players[consoleplayer] || true) + if (splitscreen || player != &players[consoleplayer]) { fixed_t zofs; mobj_t *sign;