mirror of
https://github.com/jason-e/rngfix.git
synced 2025-12-06 18:08:33 +00:00
Release commit
This commit is contained in:
parent
c544c5eb0a
commit
d6c03438e4
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
package/*
|
||||
72
README.md
72
README.md
@ -1,2 +1,70 @@
|
||||
# rngfix
|
||||
A SourceMod plugin for movement game modes
|
||||
# RNGFix
|
||||
|
||||
[](https://www.youtube.com/watch?v=PlMjHAQ90G8)
|
||||
|
||||
RNGFix is a [SourceMod](https://www.sourcemod.net/about.php) plugin that fixes a number of physics bugs that show up in movement-based game modes like bhop and surf. These issues are related in that they all appear to happen at random -- as far as a human player can tell.
|
||||
|
||||
Another plugin, [Slope Landing Fix (Slopefix)](https://forums.alliedmods.net/showthread.php?p=2322788), fixes the first of these issues (downhill inclines) and is seen as a necessity for both bhop and surf. RNGFix follows the spirit of this plugin by expanding on it with fixes for many more pseudo-random bugs.
|
||||
|
||||
Nothing this plugin does is impossible otherwise -- it just keeps random chance from mattering.
|
||||
|
||||
## Dependencies
|
||||
|
||||
* **SourceMod 1.10 - Build 6326 or newer**
|
||||
The trigger jumping fix makes use of ray trace functionality added to SourceMod in in August 2018.
|
||||
|
||||
* [**DHooks**](https://forums.alliedmods.net/showthread.php?t=180114)
|
||||
|
||||
* MarkTouching Extension (included)
|
||||
This simply exposes the function `IServerGameEnts::MarkEntitiesAsTouching` for this plugin to use.
|
||||
|
||||
* (Optional, CS:GO) [Movement Unlocker](https://forums.alliedmods.net/showthread.php?t=255298)
|
||||
Enables sliding on CS:GO. If you don't care about sliding on surf and the stair sliding fix, you don't need this.
|
||||
|
||||
Also, remember that you should stop using Slopefix if using RNGFix.
|
||||
|
||||
## Fixes
|
||||
|
||||
**Downhill Inclines**
|
||||
|
||||
Sometimes a player will not be "boosted" when falling onto an inclined surface, specifically while moving downhill. This fix results in the player always getting boosted. This is the scenario addressed by the original slopefix. RNGFix also implements this fix in a way that does not cause double boosts when a `trigger_push` is on the incline, which is a problem the original slopefix had.
|
||||
|
||||
|
||||
**Uphill Inclines**
|
||||
|
||||
When bhopping *up* an incline, sometimes the player loses speed on the initial jump, and sometimes they do not. This fix makes it so the player never loses speed in this scenario, as long as it was possible for the player to not lose speed, if not for the "luck" factor that makes this random. On shallow inclines and uneven ground, this means you will no longer randomly lose small amounts of speed when jumping, and on steep inclines this means you no longer need to land sideways and then turn directly up them, which was just a method for maximizing favorable odds.
|
||||
|
||||
|
||||
**Trigger Jumping**
|
||||
|
||||
Triggers that extend less than 2 units above the ground can sometimes be "jumped on" without activating them. This fix prevents this bug from occuring. This fixes annoyances like jumping on thin boosters without activating them, as well as exploitable behavior such as jumping on thin teleport triggers without activating them.
|
||||
|
||||
|
||||
**Telehops**
|
||||
|
||||
It is possible to pass through a teleport trigger so quickly that you also collide with the wall (or floor) behind it before actually being teleported, despite touching the teleporter "first". This fix makes it impossible to both collide with a surface and activate a teleport in the same tick. This is most notably useful on staged bhop maps with thin stage-end teleports positioned against walls; with this fix you no longer need to go through them at an angle just to maximize the odds of keeping your speed.
|
||||
|
||||
|
||||
**Edge Bugs**
|
||||
|
||||
When moving at high speed and landing on the extreme trailing edge of a platform, it is possible to collide with the surface -- resulting in a loss of vertical speed -- but without jumping, despite pressing jump in time (or holding jump with auto-bhop enabled). This fix causes the player to always be able to jump in this scenario. Note that you are still able to slide off by not pressing jump, if you wish to do so.
|
||||
|
||||
**Stair Sliding** (Surf Only)
|
||||
|
||||
The Source engine lets you move up stairs without requiring you to actually jump up each step -- as if the stairs were a simple incline -- and if you are moving fast this means you can slide up them quickly as well (on CSGO, sliding requires [Movement Unlocker](https://forums.alliedmods.net/showthread.php?t=255298)). However, if you are airborne and try to land on them at high speed, you may collide with the vertical face of a stair step before landing on top of a step, which results in a loss of speed and likely no slide. In the interest of making the incline-like behavior of stairs more consistent, this fix lets you slide up stairs when landing on them even if you hit the side of a stair step before the top of one.
|
||||
|
||||
This fix will only be applied on surf maps (maps starting with `surf_`) because it has undesirable side-effects on bhop maps. It is also unlikely to be useful on bhop.
|
||||
|
||||
---
|
||||
A more technical explanation of these fixes can be found [here](../tech.md).
|
||||
|
||||
## Settings
|
||||
|
||||
The fixes can be disabled individually by setting the following cvars to `0` in `cfg/sourcemod/plugin.rngfix.cfg`. All fixes are enabled by default.
|
||||
|
||||
`rngfix_downhill`
|
||||
`rngfix_uphill`
|
||||
`rngfix_triggerjump`
|
||||
`rngfix_telehop`
|
||||
`rngfix_edge`
|
||||
`rngfix_stairs`
|
||||
|
||||
448
extension/AMBuildScript
Normal file
448
extension/AMBuildScript
Normal file
@ -0,0 +1,448 @@
|
||||
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
|
||||
import os, sys
|
||||
|
||||
# Simple extensions do not need to modify this file.
|
||||
|
||||
class SDK(object):
|
||||
def __init__(self, sdk, ext, aDef, name, platform, dir):
|
||||
self.folder = 'hl2sdk-' + dir
|
||||
self.envvar = sdk
|
||||
self.ext = ext
|
||||
self.code = aDef
|
||||
self.define = name
|
||||
self.platform = platform
|
||||
self.name = dir
|
||||
self.path = None # Actual path
|
||||
|
||||
WinOnly = ['windows']
|
||||
WinLinux = ['windows', 'linux']
|
||||
WinLinuxMac = ['windows', 'linux', 'mac']
|
||||
|
||||
PossibleSDKs = {
|
||||
'episode1': SDK('HL2SDK', '1.ep1', '1', 'EPISODEONE', WinLinux, 'episode1'),
|
||||
'ep2': SDK('HL2SDKOB', '2.ep2', '3', 'ORANGEBOX', WinLinux, 'orangebox'),
|
||||
'css': SDK('HL2SDKCSS', '2.css', '6', 'CSS', WinLinuxMac, 'css'),
|
||||
'hl2dm': SDK('HL2SDKHL2DM', '2.hl2dm', '7', 'HL2DM', WinLinuxMac, 'hl2dm'),
|
||||
'dods': SDK('HL2SDKDODS', '2.dods', '8', 'DODS', WinLinuxMac, 'dods'),
|
||||
'sdk2013': SDK('HL2SDK2013', '2.sdk2013', '9', 'SDK2013', WinLinuxMac, 'sdk2013'),
|
||||
'tf2': SDK('HL2SDKTF2', '2.tf2', '11', 'TF2', WinLinuxMac, 'tf2'),
|
||||
'l4d': SDK('HL2SDKL4D', '2.l4d', '12', 'LEFT4DEAD', WinLinuxMac, 'l4d'),
|
||||
'nucleardawn': SDK('HL2SDKND', '2.nd', '13', 'NUCLEARDAWN', WinLinuxMac, 'nucleardawn'),
|
||||
'l4d2': SDK('HL2SDKL4D2', '2.l4d2', '15', 'LEFT4DEAD2', WinLinuxMac, 'l4d2'),
|
||||
'darkm': SDK('HL2SDK-DARKM', '2.darkm', '2', 'DARKMESSIAH', WinOnly, 'darkm'),
|
||||
'swarm': SDK('HL2SDK-SWARM', '2.swarm', '16', 'ALIENSWARM', WinOnly, 'swarm'),
|
||||
'bgt': SDK('HL2SDK-BGT', '2.bgt', '4', 'BLOODYGOODTIME', WinOnly, 'bgt'),
|
||||
'eye': SDK('HL2SDK-EYE', '2.eye', '5', 'EYE', WinOnly, 'eye'),
|
||||
'csgo': SDK('HL2SDKCSGO', '2.csgo', '21', 'CSGO', WinLinuxMac, 'csgo'),
|
||||
'portal2': SDK('HL2SDKPORTAL2', '2.portal2', '17', 'PORTAL2', [], 'portal2'),
|
||||
'blade': SDK('HL2SDKBLADE', '2.blade', '18', 'BLADE', WinLinux, 'blade'),
|
||||
'insurgency': SDK('HL2SDKINSURGENCY', '2.insurgency', '19', 'INSURGENCY', WinLinuxMac, 'insurgency'),
|
||||
'contagion': SDK('HL2SDKCONTAGION', '2.contagion', '14', 'CONTAGION', WinOnly, 'contagion'),
|
||||
'bms': SDK('HL2SDKBMS', '2.bms', '10', 'BMS', WinLinux, 'bms'),
|
||||
'doi': SDK('HL2SDKDOI', '2.doi', '20', 'DOI', WinLinuxMac, 'doi'),
|
||||
}
|
||||
|
||||
def ResolveEnvPath(env, folder):
|
||||
if env in os.environ:
|
||||
path = os.environ[env]
|
||||
if os.path.isdir(path):
|
||||
return path
|
||||
return None
|
||||
|
||||
head = os.getcwd()
|
||||
oldhead = None
|
||||
while head != None and head != oldhead:
|
||||
path = os.path.join(head, folder)
|
||||
if os.path.isdir(path):
|
||||
return path
|
||||
oldhead = head
|
||||
head, tail = os.path.split(head)
|
||||
|
||||
return None
|
||||
|
||||
def Normalize(path):
|
||||
return os.path.abspath(os.path.normpath(path))
|
||||
|
||||
class ExtensionConfig(object):
|
||||
def __init__(self):
|
||||
self.sdks = {}
|
||||
self.binaries = []
|
||||
self.extensions = []
|
||||
self.generated_headers = None
|
||||
self.mms_root = None
|
||||
self.sm_root = None
|
||||
|
||||
@property
|
||||
def tag(self):
|
||||
if builder.options.debug == '1':
|
||||
return 'Debug'
|
||||
return 'Release'
|
||||
|
||||
def detectSDKs(self):
|
||||
sdk_list = builder.options.sdks.split(',')
|
||||
use_all = sdk_list[0] == 'all'
|
||||
use_present = sdk_list[0] == 'present'
|
||||
|
||||
for sdk_name in PossibleSDKs:
|
||||
sdk = PossibleSDKs[sdk_name]
|
||||
if builder.target_platform in sdk.platform:
|
||||
if builder.options.hl2sdk_root:
|
||||
sdk_path = os.path.join(builder.options.hl2sdk_root, sdk.folder)
|
||||
else:
|
||||
sdk_path = ResolveEnvPath(sdk.envvar, sdk.folder)
|
||||
if sdk_path is None or not os.path.isdir(sdk_path):
|
||||
if use_all or sdk_name in sdk_list:
|
||||
raise Exception('Could not find a valid path for {0}'.format(sdk.envvar))
|
||||
continue
|
||||
if use_all or use_present or sdk_name in sdk_list:
|
||||
sdk.path = Normalize(sdk_path)
|
||||
self.sdks[sdk_name] = sdk
|
||||
|
||||
if len(self.sdks) < 1:
|
||||
raise Exception('At least one SDK must be available.')
|
||||
|
||||
if builder.options.sm_path:
|
||||
self.sm_root = builder.options.sm_path
|
||||
else:
|
||||
self.sm_root = ResolveEnvPath('SOURCEMOD18', 'sourcemod-1.8')
|
||||
if not self.sm_root:
|
||||
self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod')
|
||||
if not self.sm_root:
|
||||
self.sm_root = ResolveEnvPath('SOURCEMOD_DEV', 'sourcemod-central')
|
||||
|
||||
if not self.sm_root or not os.path.isdir(self.sm_root):
|
||||
raise Exception('Could not find a source copy of SourceMod')
|
||||
self.sm_root = Normalize(self.sm_root)
|
||||
|
||||
if builder.options.mms_path:
|
||||
self.mms_root = builder.options.mms_path
|
||||
else:
|
||||
self.mms_root = ResolveEnvPath('MMSOURCE110', 'mmsource-1.10')
|
||||
if not self.mms_root:
|
||||
self.mms_root = ResolveEnvPath('MMSOURCE', 'metamod-source')
|
||||
if not self.mms_root:
|
||||
self.mms_root = ResolveEnvPath('MMSOURCE_DEV', 'mmsource-central')
|
||||
|
||||
if not self.mms_root or not os.path.isdir(self.mms_root):
|
||||
raise Exception('Could not find a source copy of Metamod:Source')
|
||||
self.mms_root = Normalize(self.mms_root)
|
||||
|
||||
def configure(self):
|
||||
cxx = builder.DetectCompilers()
|
||||
|
||||
if cxx.like('gcc'):
|
||||
self.configure_gcc(cxx)
|
||||
elif cxx.vendor == 'msvc':
|
||||
self.configure_msvc(cxx)
|
||||
|
||||
# Optimizaiton
|
||||
if builder.options.opt == '1':
|
||||
cxx.defines += ['NDEBUG']
|
||||
|
||||
# Debugging
|
||||
if builder.options.debug == '1':
|
||||
cxx.defines += ['DEBUG', '_DEBUG']
|
||||
|
||||
# Platform-specifics
|
||||
if builder.target_platform == 'linux':
|
||||
self.configure_linux(cxx)
|
||||
elif builder.target_platform == 'mac':
|
||||
self.configure_mac(cxx)
|
||||
elif builder.target_platform == 'windows':
|
||||
self.configure_windows(cxx)
|
||||
|
||||
# Finish up.
|
||||
cxx.includes += [
|
||||
os.path.join(self.sm_root, 'public'),
|
||||
]
|
||||
|
||||
def configure_gcc(self, cxx):
|
||||
cxx.defines += [
|
||||
'stricmp=strcasecmp',
|
||||
'_stricmp=strcasecmp',
|
||||
'_snprintf=snprintf',
|
||||
'_vsnprintf=vsnprintf',
|
||||
'HAVE_STDINT_H',
|
||||
'GNUC',
|
||||
]
|
||||
cxx.cflags += [
|
||||
'-pipe',
|
||||
'-fno-strict-aliasing',
|
||||
'-Wall',
|
||||
'-Werror',
|
||||
'-Wno-unused',
|
||||
'-Wno-switch',
|
||||
'-Wno-array-bounds',
|
||||
'-msse',
|
||||
'-m32',
|
||||
'-fvisibility=hidden',
|
||||
]
|
||||
cxx.cxxflags += [
|
||||
'-std=c++11',
|
||||
'-fno-exceptions',
|
||||
'-fno-threadsafe-statics',
|
||||
'-Wno-non-virtual-dtor',
|
||||
'-Wno-overloaded-virtual',
|
||||
'-fvisibility-inlines-hidden',
|
||||
]
|
||||
cxx.linkflags += ['-m32']
|
||||
|
||||
have_gcc = cxx.vendor == 'gcc'
|
||||
have_clang = cxx.vendor == 'clang'
|
||||
if cxx.version >= 'clang-3.6':
|
||||
cxx.cxxflags += ['-Wno-inconsistent-missing-override']
|
||||
if have_clang or (cxx.version >= 'gcc-4.6'):
|
||||
cxx.cflags += ['-Wno-narrowing']
|
||||
if have_clang or (cxx.version >= 'gcc-4.7'):
|
||||
cxx.cxxflags += ['-Wno-delete-non-virtual-dtor']
|
||||
if cxx.version >= 'gcc-4.8':
|
||||
cxx.cflags += ['-Wno-unused-result']
|
||||
|
||||
if have_clang:
|
||||
cxx.cxxflags += ['-Wno-implicit-exception-spec-mismatch']
|
||||
if cxx.version >= 'apple-clang-5.1' or cxx.version >= 'clang-3.4':
|
||||
cxx.cxxflags += ['-Wno-deprecated-register']
|
||||
else:
|
||||
cxx.cxxflags += ['-Wno-deprecated']
|
||||
cxx.cflags += ['-Wno-sometimes-uninitialized']
|
||||
|
||||
if have_gcc:
|
||||
cxx.cflags += ['-mfpmath=sse']
|
||||
|
||||
if builder.options.opt == '1':
|
||||
cxx.cflags += ['-O3']
|
||||
|
||||
def configure_msvc(self, cxx):
|
||||
if builder.options.debug == '1':
|
||||
cxx.cflags += ['/MTd']
|
||||
cxx.linkflags += ['/NODEFAULTLIB:libcmt']
|
||||
else:
|
||||
cxx.cflags += ['/MT']
|
||||
cxx.defines += [
|
||||
'_CRT_SECURE_NO_DEPRECATE',
|
||||
'_CRT_SECURE_NO_WARNINGS',
|
||||
'_CRT_NONSTDC_NO_DEPRECATE',
|
||||
'_ITERATOR_DEBUG_LEVEL=0',
|
||||
]
|
||||
cxx.cflags += [
|
||||
'/W3',
|
||||
]
|
||||
cxx.cxxflags += [
|
||||
'/EHsc',
|
||||
'/GR-',
|
||||
'/TP',
|
||||
]
|
||||
cxx.linkflags += [
|
||||
'/MACHINE:X86',
|
||||
'kernel32.lib',
|
||||
'user32.lib',
|
||||
'gdi32.lib',
|
||||
'winspool.lib',
|
||||
'comdlg32.lib',
|
||||
'advapi32.lib',
|
||||
'shell32.lib',
|
||||
'ole32.lib',
|
||||
'oleaut32.lib',
|
||||
'uuid.lib',
|
||||
'odbc32.lib',
|
||||
'odbccp32.lib',
|
||||
]
|
||||
|
||||
if builder.options.opt == '1':
|
||||
cxx.cflags += ['/Ox', '/Zo']
|
||||
cxx.linkflags += ['/OPT:ICF', '/OPT:REF']
|
||||
|
||||
if builder.options.debug == '1':
|
||||
cxx.cflags += ['/Od', '/RTC1']
|
||||
|
||||
# This needs to be after our optimization flags which could otherwise disable it.
|
||||
# Don't omit the frame pointer.
|
||||
cxx.cflags += ['/Oy-']
|
||||
|
||||
def configure_linux(self, cxx):
|
||||
cxx.defines += ['_LINUX', 'POSIX']
|
||||
cxx.linkflags += ['-Wl,--exclude-libs,ALL', '-lm']
|
||||
if cxx.vendor == 'gcc':
|
||||
cxx.linkflags += ['-static-libgcc']
|
||||
elif cxx.vendor == 'clang':
|
||||
cxx.linkflags += ['-lgcc_eh']
|
||||
|
||||
def configure_mac(self, cxx):
|
||||
cxx.defines += ['OSX', '_OSX', 'POSIX']
|
||||
cxx.cflags += ['-mmacosx-version-min=10.5']
|
||||
cxx.linkflags += [
|
||||
'-mmacosx-version-min=10.5',
|
||||
'-arch', 'i386',
|
||||
'-lstdc++',
|
||||
'-stdlib=libstdc++',
|
||||
]
|
||||
cxx.cxxflags += ['-stdlib=libstdc++']
|
||||
|
||||
def configure_windows(self, cxx):
|
||||
cxx.defines += ['WIN32', '_WINDOWS']
|
||||
|
||||
def ConfigureForExtension(self, context, compiler):
|
||||
compiler.cxxincludes += [
|
||||
os.path.join(context.currentSourcePath),
|
||||
os.path.join(context.currentSourcePath, 'sdk'),
|
||||
os.path.join(self.sm_root, 'public'),
|
||||
os.path.join(self.sm_root, 'public', 'extensions'),
|
||||
os.path.join(self.sm_root, 'sourcepawn', 'include'),
|
||||
os.path.join(self.sm_root, 'public', 'amtl', 'amtl'),
|
||||
os.path.join(self.sm_root, 'public', 'amtl'),
|
||||
]
|
||||
return compiler
|
||||
|
||||
def ConfigureForHL2(self, binary, sdk):
|
||||
compiler = binary.compiler
|
||||
|
||||
if sdk.name == 'episode1':
|
||||
mms_path = os.path.join(self.mms_root, 'core-legacy')
|
||||
else:
|
||||
mms_path = os.path.join(self.mms_root, 'core')
|
||||
|
||||
compiler.cxxincludes += [
|
||||
os.path.join(mms_path),
|
||||
os.path.join(mms_path, 'sourcehook'),
|
||||
]
|
||||
|
||||
defines = ['SE_' + PossibleSDKs[i].define + '=' + PossibleSDKs[i].code for i in PossibleSDKs]
|
||||
compiler.defines += defines
|
||||
|
||||
paths = [
|
||||
['public'],
|
||||
['public', 'engine'],
|
||||
['public', 'mathlib'],
|
||||
['public', 'vstdlib'],
|
||||
['public', 'tier0'],
|
||||
['public', 'tier1']
|
||||
]
|
||||
if sdk.name == 'episode1' or sdk.name == 'darkm':
|
||||
paths.append(['public', 'dlls'])
|
||||
paths.append(['game_shared'])
|
||||
else:
|
||||
paths.append(['public', 'game', 'server'])
|
||||
paths.append(['public', 'toolframework'])
|
||||
paths.append(['game', 'shared'])
|
||||
paths.append(['common'])
|
||||
|
||||
compiler.defines += ['SOURCE_ENGINE=' + sdk.code]
|
||||
|
||||
if sdk.name in ['sdk2013', 'bms'] and compiler.like('gcc'):
|
||||
# The 2013 SDK already has these in public/tier0/basetypes.h
|
||||
compiler.defines.remove('stricmp=strcasecmp')
|
||||
compiler.defines.remove('_stricmp=strcasecmp')
|
||||
compiler.defines.remove('_snprintf=snprintf')
|
||||
compiler.defines.remove('_vsnprintf=vsnprintf')
|
||||
|
||||
if compiler.like('msvc'):
|
||||
compiler.defines += ['COMPILER_MSVC', 'COMPILER_MSVC32']
|
||||
else:
|
||||
compiler.defines += ['COMPILER_GCC']
|
||||
|
||||
# For everything after Swarm, this needs to be defined for entity networking
|
||||
# to work properly with sendprop value changes.
|
||||
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
|
||||
compiler.defines += ['NETWORK_VARS_ENABLED']
|
||||
|
||||
if sdk.name in ['css', 'hl2dm', 'dods', 'sdk2013', 'bms', 'tf2', 'l4d', 'nucleardawn', 'l4d2']:
|
||||
if builder.target_platform in ['linux', 'mac']:
|
||||
compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE']
|
||||
|
||||
if sdk.name == 'csgo' and builder.target_platform == 'linux':
|
||||
compiler.linkflags += ['-lstdc++']
|
||||
|
||||
for path in paths:
|
||||
compiler.cxxincludes += [os.path.join(sdk.path, *path)]
|
||||
|
||||
if builder.target_platform == 'linux':
|
||||
if sdk.name == 'episode1':
|
||||
lib_folder = os.path.join(sdk.path, 'linux_sdk')
|
||||
elif sdk.name in ['sdk2013', 'bms']:
|
||||
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'linux32')
|
||||
else:
|
||||
lib_folder = os.path.join(sdk.path, 'lib', 'linux')
|
||||
elif builder.target_platform == 'mac':
|
||||
if sdk.name in ['sdk2013', 'bms']:
|
||||
lib_folder = os.path.join(sdk.path, 'lib', 'public', 'osx32')
|
||||
else:
|
||||
lib_folder = os.path.join(sdk.path, 'lib', 'mac')
|
||||
|
||||
if builder.target_platform in ['linux', 'mac']:
|
||||
if sdk.name in ['sdk2013', 'bms']:
|
||||
compiler.postlink += [
|
||||
compiler.Dep(os.path.join(lib_folder, 'tier1.a')),
|
||||
compiler.Dep(os.path.join(lib_folder, 'mathlib.a'))
|
||||
]
|
||||
else:
|
||||
compiler.postlink += [
|
||||
compiler.Dep(os.path.join(lib_folder, 'tier1_i486.a')),
|
||||
compiler.Dep(os.path.join(lib_folder, 'mathlib_i486.a'))
|
||||
]
|
||||
|
||||
if sdk.name in ['blade', 'insurgency', 'doi', 'csgo']:
|
||||
compiler.postlink += [compiler.Dep(os.path.join(lib_folder, 'interfaces_i486.a'))]
|
||||
|
||||
dynamic_libs = []
|
||||
if builder.target_platform == 'linux':
|
||||
if sdk.name in ['css', 'hl2dm', 'dods', 'tf2', 'sdk2013', 'bms', 'nucleardawn', 'l4d2', 'insurgency', 'doi']:
|
||||
dynamic_libs = ['libtier0_srv.so', 'libvstdlib_srv.so']
|
||||
elif sdk.name in ['l4d', 'blade', 'insurgency', 'doi', 'csgo']:
|
||||
dynamic_libs = ['libtier0.so', 'libvstdlib.so']
|
||||
else:
|
||||
dynamic_libs = ['tier0_i486.so', 'vstdlib_i486.so']
|
||||
elif builder.target_platform == 'mac':
|
||||
compiler.linkflags.append('-liconv')
|
||||
dynamic_libs = ['libtier0.dylib', 'libvstdlib.dylib']
|
||||
elif builder.target_platform == 'windows':
|
||||
libs = ['tier0', 'tier1', 'vstdlib', 'mathlib']
|
||||
if sdk.name in ['swarm', 'blade', 'insurgency', 'doi', 'csgo']:
|
||||
libs.append('interfaces')
|
||||
for lib in libs:
|
||||
lib_path = os.path.join(sdk.path, 'lib', 'public', lib) + '.lib'
|
||||
compiler.linkflags.append(compiler.Dep(lib_path))
|
||||
|
||||
for library in dynamic_libs:
|
||||
source_path = os.path.join(lib_folder, library)
|
||||
output_path = os.path.join(binary.localFolder, library)
|
||||
|
||||
def make_linker(source_path, output_path):
|
||||
def link(context, binary):
|
||||
cmd_node, (output,) = context.AddSymlink(source_path, output_path)
|
||||
return output
|
||||
return link
|
||||
|
||||
linker = make_linker(source_path, output_path)
|
||||
compiler.linkflags[0:0] = [compiler.Dep(library, linker)]
|
||||
|
||||
return binary
|
||||
|
||||
def HL2Library(self, context, name, sdk):
|
||||
binary = context.compiler.Library(name)
|
||||
self.ConfigureForExtension(context, binary.compiler)
|
||||
return self.ConfigureForHL2(binary, sdk)
|
||||
|
||||
def HL2Project(self, context, name):
|
||||
project = context.compiler.LibraryProject(name)
|
||||
self.ConfigureForExtension(context, project.compiler)
|
||||
return project
|
||||
|
||||
def HL2Config(self, project, name, sdk):
|
||||
binary = project.Configure(name, '{0} - {1}'.format(self.tag, sdk.name))
|
||||
return self.ConfigureForHL2(binary, sdk)
|
||||
|
||||
Extension = ExtensionConfig()
|
||||
Extension.detectSDKs()
|
||||
Extension.configure()
|
||||
|
||||
# Add additional buildscripts here
|
||||
BuildScripts = [
|
||||
'AMBuilder',
|
||||
]
|
||||
|
||||
if builder.backend == 'amb2':
|
||||
BuildScripts += [
|
||||
'PackageScript',
|
||||
]
|
||||
|
||||
builder.RunBuildScripts(BuildScripts, { 'Extension': Extension})
|
||||
31
extension/AMBuilder
Normal file
31
extension/AMBuilder
Normal file
@ -0,0 +1,31 @@
|
||||
# vim: set sts=2 ts=8 sw=2 tw=99 et ft=python:
|
||||
import os, sys
|
||||
|
||||
projectName = 'marktouching'
|
||||
|
||||
# smsdk_ext.cpp will be automatically added later
|
||||
sourceFiles = [
|
||||
'extension.cpp',
|
||||
]
|
||||
|
||||
###############
|
||||
# Make sure to edit PackageScript, which copies your files to their appropriate locations
|
||||
# Simple extensions do not need to modify past this point.
|
||||
|
||||
project = Extension.HL2Project(builder, projectName + '.ext')
|
||||
|
||||
if os.path.isfile(os.path.join(builder.currentSourcePath, 'sdk', 'smsdk_ext.cpp')):
|
||||
# Use the copy included in the project
|
||||
project.sources += [os.path.join('sdk', 'smsdk_ext.cpp')]
|
||||
else:
|
||||
# Use the copy included with SM 1.6 and newer
|
||||
project.sources += [os.path.join(Extension.sm_root, 'public', 'smsdk_ext.cpp')]
|
||||
|
||||
project.sources += sourceFiles
|
||||
|
||||
for sdk_name in Extension.sdks:
|
||||
sdk = Extension.sdks[sdk_name]
|
||||
|
||||
binary = Extension.HL2Config(project, projectName + '.ext.' + sdk.ext, sdk)
|
||||
|
||||
Extension.extensions = builder.Add(project)
|
||||
23
extension/configure.py
Normal file
23
extension/configure.py
Normal file
@ -0,0 +1,23 @@
|
||||
# vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
import sys
|
||||
from ambuild2 import run
|
||||
|
||||
# Simple extensions do not need to modify this file.
|
||||
|
||||
builder = run.PrepareBuild(sourcePath = sys.path[0])
|
||||
|
||||
builder.options.add_option('--hl2sdk-root', type=str, dest='hl2sdk_root', default=None,
|
||||
help='Root search folder for HL2SDKs')
|
||||
builder.options.add_option('--mms-path', type=str, dest='mms_path', default=None,
|
||||
help='Path to Metamod:Source')
|
||||
builder.options.add_option('--sm-path', type=str, dest='sm_path', default=None,
|
||||
help='Path to SourceMod')
|
||||
builder.options.add_option('--enable-debug', action='store_const', const='1', dest='debug',
|
||||
help='Enable debugging symbols')
|
||||
builder.options.add_option('--enable-optimize', action='store_const', const='1', dest='opt',
|
||||
help='Enable optimization')
|
||||
builder.options.add_option('-s', '--sdks', default='all', dest='sdks',
|
||||
help='Build against specified SDKs; valid args are "all", "present", or '
|
||||
'comma-delimited list of engine names (default: %default)')
|
||||
|
||||
builder.Configure()
|
||||
43
extension/extension.cpp
Normal file
43
extension/extension.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "extension.h"
|
||||
|
||||
MarkTouching g_MarkTouching; /**< Global singleton for extension's main interface */
|
||||
IServerGameEnts *gameents = NULL;
|
||||
|
||||
SMEXT_LINK(&g_MarkTouching);
|
||||
|
||||
void MarkTouching::SDK_OnAllLoaded()
|
||||
{
|
||||
sharesys->AddNatives(myself, MyNatives);
|
||||
}
|
||||
|
||||
bool MarkTouching::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
||||
{
|
||||
GET_V_IFACE_ANY(GetServerFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cell_t MarkEntitiesAsTouching(IPluginContext *pContext, const cell_t *params)
|
||||
{
|
||||
edict_t *pEdict1 = gamehelpers->EdictOfIndex(params[1]);
|
||||
if (!pEdict1 || pEdict1->IsFree())
|
||||
{
|
||||
return pContext->ThrowNativeError("Entity %d is invalid", params[1]);
|
||||
}
|
||||
|
||||
edict_t *pEdict2 = gamehelpers->EdictOfIndex(params[2]);
|
||||
if (!pEdict2 || pEdict2->IsFree())
|
||||
{
|
||||
return pContext->ThrowNativeError("Entity %d is invalid", params[2]);
|
||||
}
|
||||
|
||||
gameents->MarkEntitiesAsTouching(pEdict1, pEdict2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sp_nativeinfo_t MyNatives[] =
|
||||
{
|
||||
{"MarkEntitiesAsTouching", MarkEntitiesAsTouching},
|
||||
{NULL, NULL},
|
||||
};
|
||||
81
extension/extension.h
Normal file
81
extension/extension.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||
#define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||
|
||||
#include "smsdk_ext.h"
|
||||
|
||||
extern IServerGameEnts *gameents;
|
||||
extern sp_nativeinfo_t MyNatives[];
|
||||
|
||||
class MarkTouching : public SDKExtension
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief This is called after the initial loading sequence has been processed.
|
||||
*
|
||||
* @param error Error message buffer.
|
||||
* @param maxlength Size of error message buffer.
|
||||
* @param late Whether or not the module was loaded after map load.
|
||||
* @return True to succeed loading, false to fail.
|
||||
*/
|
||||
//virtual bool SDK_OnLoad(char *error, size_t maxlength, bool late);
|
||||
|
||||
/**
|
||||
* @brief This is called right before the extension is unloaded.
|
||||
*/
|
||||
//virtual void SDK_OnUnload();
|
||||
|
||||
/**
|
||||
* @brief This is called once all known extensions have been loaded.
|
||||
* Note: It is is a good idea to add natives here, if any are provided.
|
||||
*/
|
||||
virtual void SDK_OnAllLoaded();
|
||||
|
||||
/**
|
||||
* @brief Called when the pause state is changed.
|
||||
*/
|
||||
//virtual void SDK_OnPauseChange(bool paused);
|
||||
|
||||
/**
|
||||
* @brief this is called when Core wants to know if your extension is working.
|
||||
*
|
||||
* @param error Error message buffer.
|
||||
* @param maxlength Size of error message buffer.
|
||||
* @return True if working, false otherwise.
|
||||
*/
|
||||
//virtual bool QueryRunning(char *error, size_t maxlength);
|
||||
public:
|
||||
#if defined SMEXT_CONF_METAMOD
|
||||
/**
|
||||
* @brief Called when Metamod is attached, before the extension version is called.
|
||||
*
|
||||
* @param error Error buffer.
|
||||
* @param maxlength Maximum size of error buffer.
|
||||
* @param late Whether or not Metamod considers this a late load.
|
||||
* @return True to succeed, false to fail.
|
||||
*/
|
||||
virtual bool SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlength, bool late);
|
||||
|
||||
/**
|
||||
* @brief Called when Metamod is detaching, after the extension version is called.
|
||||
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||
*
|
||||
* @param error Error buffer.
|
||||
* @param maxlength Maximum size of error buffer.
|
||||
* @return True to succeed, false to fail.
|
||||
*/
|
||||
//virtual bool SDK_OnMetamodUnload(char *error, size_t maxlength);
|
||||
|
||||
/**
|
||||
* @brief Called when Metamod's pause state is changing.
|
||||
* NOTE: By default this is blocked unless sent from SourceMod.
|
||||
*
|
||||
* @param paused Pause state being set.
|
||||
* @param error Error buffer.
|
||||
* @param maxlength Maximum size of error buffer.
|
||||
* @return True to succeed, false to fail.
|
||||
*/
|
||||
//virtual bool SDK_OnMetamodPauseChange(bool paused, char *error, size_t maxlength);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
|
||||
45
extension/smsdk_config.h
Normal file
45
extension/smsdk_config.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
||||
#define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
||||
|
||||
/* Basic information exposed publicly */
|
||||
#define SMEXT_CONF_NAME "MarkTouching"
|
||||
#define SMEXT_CONF_DESCRIPTION "Exposes IServerGameEnts::MarkEntitiesAsTouching"
|
||||
#define SMEXT_CONF_VERSION "1.0.0.0"
|
||||
#define SMEXT_CONF_AUTHOR "rio"
|
||||
#define SMEXT_CONF_URL ""
|
||||
#define SMEXT_CONF_LOGTAG "MARKTOUCHING"
|
||||
#define SMEXT_CONF_LICENSE "GPL"
|
||||
#define SMEXT_CONF_DATESTRING __DATE__
|
||||
|
||||
/**
|
||||
* @brief Exposes plugin's main interface.
|
||||
*/
|
||||
#define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name;
|
||||
|
||||
/**
|
||||
* @brief Sets whether or not this plugin required Metamod.
|
||||
* NOTE: Uncomment to enable, comment to disable.
|
||||
*/
|
||||
#define SMEXT_CONF_METAMOD
|
||||
|
||||
/** Enable interfaces you want to use here by uncommenting lines */
|
||||
//#define SMEXT_ENABLE_FORWARDSYS
|
||||
//#define SMEXT_ENABLE_HANDLESYS
|
||||
//#define SMEXT_ENABLE_PLAYERHELPERS
|
||||
//#define SMEXT_ENABLE_DBMANAGER
|
||||
//#define SMEXT_ENABLE_GAMECONF
|
||||
//#define SMEXT_ENABLE_MEMUTILS
|
||||
#define SMEXT_ENABLE_GAMEHELPERS
|
||||
//#define SMEXT_ENABLE_TIMERSYS
|
||||
//#define SMEXT_ENABLE_THREADER
|
||||
//#define SMEXT_ENABLE_LIBSYS
|
||||
//#define SMEXT_ENABLE_MENUS
|
||||
//#define SMEXT_ENABLE_ADTFACTORY
|
||||
//#define SMEXT_ENABLE_PLUGINSYS
|
||||
//#define SMEXT_ENABLE_ADMINSYS
|
||||
//#define SMEXT_ENABLE_TEXTPARSERS
|
||||
//#define SMEXT_ENABLE_USERMSGS
|
||||
//#define SMEXT_ENABLE_TRANSLATOR
|
||||
//#define SMEXT_ENABLE_ROOTCONSOLEMENU
|
||||
|
||||
#endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
|
||||
31
plugin/gamedata/gamemovement.games.txt
Normal file
31
plugin/gamedata/gamemovement.games.txt
Normal file
@ -0,0 +1,31 @@
|
||||
"Games"
|
||||
{
|
||||
"#default"
|
||||
{
|
||||
"Keys"
|
||||
{
|
||||
"IGameMovement" "GameMovement001"
|
||||
}
|
||||
"Signatures"
|
||||
{
|
||||
"CreateInterface"
|
||||
{
|
||||
"library" "server"
|
||||
"windows" "@CreateInterface"
|
||||
"linux" "@CreateInterface"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"#default"
|
||||
{
|
||||
"Offsets"
|
||||
{
|
||||
"ProcessMovement"
|
||||
{
|
||||
"windows" "1"
|
||||
"linux" "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
plugin/gamedata/triggerfilters.games.txt
Normal file
42
plugin/gamedata/triggerfilters.games.txt
Normal file
@ -0,0 +1,42 @@
|
||||
"Games"
|
||||
{
|
||||
"csgo"
|
||||
{
|
||||
"Offsets"
|
||||
{
|
||||
// applies to trigger_vphysics_motion and trigger_wind
|
||||
"CBaseVPhysicsTrigger::PassesTriggerFilters"
|
||||
{
|
||||
"windows" "196"
|
||||
"linux" "197"
|
||||
}
|
||||
|
||||
// applies to all other triggers
|
||||
"CBaseTrigger::PassesTriggerFilters"
|
||||
{
|
||||
"windows" "206"
|
||||
"linux" "207"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"cstrike"
|
||||
{
|
||||
"Offsets"
|
||||
{
|
||||
// applies to trigger_vphysics_motion and trigger_wind
|
||||
"CBaseVPhysicsTrigger::PassesTriggerFilters"
|
||||
{
|
||||
"windows" "188"
|
||||
"linux" "189"
|
||||
}
|
||||
|
||||
// applies to all other triggers
|
||||
"CBaseTrigger::PassesTriggerFilters"
|
||||
{
|
||||
"windows" "197"
|
||||
"linux" "198"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
plugin/scripting/include/marktouching.inc
Normal file
38
plugin/scripting/include/marktouching.inc
Normal file
@ -0,0 +1,38 @@
|
||||
#if defined _marktouching_included
|
||||
#endinput
|
||||
#endif
|
||||
#define _marktouching_included
|
||||
|
||||
/**
|
||||
* Mark two entities as touching each other
|
||||
* This is the same call that is triggered when two entities touch, and triggers StartTouch, Touch, and later EndTouch.
|
||||
* Note that this will fire Touch() even if the entities were already touching, meaning Touch() will be called multiple times in one tick
|
||||
*
|
||||
* @param entity1 Entity index.
|
||||
* @param entity2 Entity index.
|
||||
* @noreturn
|
||||
*/
|
||||
native void MarkEntitiesAsTouching(int entity1, int entity2);
|
||||
|
||||
#if !defined REQUIRE_EXTENSIONS
|
||||
public __ext_marktouching_SetNTVOptional()
|
||||
{
|
||||
MarkNativeAsOptional("MarkEntitiesAsTouching");
|
||||
}
|
||||
#endif
|
||||
|
||||
public Extension __ext_marktouching =
|
||||
{
|
||||
name = "MarkTouching",
|
||||
file = "marktouching.ext",
|
||||
#if defined AUTOLOAD_EXTENSIONS
|
||||
autoload = 1,
|
||||
#else
|
||||
autoload = 0,
|
||||
#endif
|
||||
#if defined REQUIRE_EXTENSIONS
|
||||
required = 1,
|
||||
#else
|
||||
required = 0,
|
||||
#endif
|
||||
};
|
||||
1234
plugin/scripting/rngfix.sp
Normal file
1234
plugin/scripting/rngfix.sp
Normal file
File diff suppressed because it is too large
Load Diff
91
tech.md
Normal file
91
tech.md
Normal file
@ -0,0 +1,91 @@
|
||||
## Tick Simulation
|
||||
|
||||
This is a simple overview of some key engine steps for reference. Not all of these are used by RNGFix, nor is this all-inclusive.
|
||||
|
||||
1. **`OnPlayerRunCmd`** (SourceMod's public forward) - This function should really only be used for modifying player inputs. If you need to run some code for each client for each command that is actually executed (i.e. where the server is not overloaded), you should probably use a PreThink or PreThinkPost hook. In many cases doing stuff here (or in OnPlayerRunCmdPost) is fine as long as you are aware that this does not have a 1:1 relationship with physically simulated client ticks.
|
||||
|
||||
(Note that if the server is overloaded, the command is dropped and the following steps are skipped)
|
||||
|
||||
1. **`PreThink/PreThinkPost`** - This is a convenient function to hook that is run right before the command is processed and the player is moved, triggers are touched, etc.
|
||||
2. **`CGameMovement::ProcessMovement`** - This is where the key and mouse data for this user command are applied and the player is moved one tick
|
||||
3. **`CGameMovement::ProcessImpacts`** - This is where triggers are actually "touched", note that this is enitrely after movement for this tick has completed
|
||||
4. **`PostThink/PostThinkPost`** - This is a convenient function to hook that is run right after the command is processed and the player is moved, triggers are touched, etc.
|
||||
|
||||
|
||||
2. **`OnPlayerRunCmdPost`** - This should most correctly be used as a convenient way to check the final inputs for this command after all plugins have potentially modified them. This forward is fired even if the command was actually dropped.
|
||||
|
||||
|
||||
|
||||
## Technical Overview of Fixes
|
||||
|
||||
These fixes are split into two groups: pre-tick fixes -- which are detected and applied immediately *before* a user command is run and a tick is simulated -- and post-tick fixes, which correct the results of a tick immediately *after* it is simulated. This just depends on what I decided was the best way to apply these fixes.
|
||||
|
||||
The actions of this plugin are coupled fairly tightly to the engine's movement processing (the most important parts are executed immediately before `CGameMovement::ProcessMovement` is run, the rest happens before each player's `PostThink`) and thus this plugin is unlikely to interfere with other plugins, or be negatively impacted by them. To put it another way, it is safe for other plugins to do whatever they want in `OnPlayerRunCmd/OnPlayerRunCmdPost` and player `PreThink` calls without interfering with these fixes.
|
||||
|
||||
In order to understand some of these problems and their fixes, it is relevant to know that the engine will only consider a player "on the ground", and thus able to walk and jump, if their Z velocity is less than positive 140.0. If this is not the case, surfaces that are otherwise shallow enough to walk on will essentially behave like surf ramps because the player is considered in the air rather than standing on them. This is why the player is not able to rapidly bhop up steep inclines when moving faster than a certain speed.
|
||||
|
||||
---
|
||||
|
||||
|
||||
**Downhill Inclines** [Post-tick]
|
||||
|
||||
If the plugin detects that the player just landed on the ground, but did so without ever colliding with it this tick, the player's velocity is updated to reflect what it would have been had the player actually collided with it. This fix is only applied if a collision would result in the player having a larger absolute amount of horizontal speed than before, which is always the case when falling straight down or moving "downhill", and is *sometimes* the case when moving uphill, especially for steep inclines. This is the same criteria that the original slopefix used to determine when to apply the fix.
|
||||
|
||||
The reason it is possible to "land" on the ground without actually touching it is because the engine will consider a player "on the ground" if there is walkable ground *within 2 units* below them at certain times within the simulation of a tick. If it just so happens that you end up within 2 units of a walkable surface at the end of a tick, the engine effectively considers you to have landed on it, which zeroes out your Z velocity immediately with no consideration for the interaction between that Z velocity and the angle of the ground. This issue is more prevalent on **higher** tickrates.
|
||||
|
||||
The original slopefix plugin handled this fix slightly differently. Its deflection velocity calculation does not take basevelocity into effect, and more importantly: when the new velocity is applied, any existing basevelocity is baked into the player's velocity immediately, which unfortunately results in a "double boost" if the player jumps on an incline while touching a `trigger_push`. RNGFix handles these things more accurately which eliminates this side-effect, but if you *really* want the old behavior (double boosts) for legacy reasons, set `rngfix_useoldslopefixlogic` to `1` on a case-by-case, per-map basis.
|
||||
|
||||
---
|
||||
**Uphill Inclines** [Pre-tick]
|
||||
|
||||
This fix is very much the opposite of the downhill incline fix and aims to guarantee the result that is the opposite of what the downhill incline fix does. Occurrences of this issue are more prevalent on **lower** tickrates.
|
||||
|
||||
If the plugin detects that the player *will* collide with an incline (in an "uphill" direction, or into the incline) once this tick is simulated, and it is possible to land on this surface (that is, the surface is not too steep to walk on, and the player's Z velocity at the time of collision is less than positive 140.0), then the player is moved *away* from the incline such that they will barely not collide with the incline by the end of the tick. Note that this adjustment is often only a few units or less and is totally imperceptible to the player in real-time.
|
||||
|
||||
This change means that instead of colliding and deflecting along the incline, the player will instead land on the incline without colliding with it. This is desirable because landing immediately zeroes out Z velocity, and the player is able to jump while having the full horizontal velocity they started with. In the event of going up a moderately steep incline, this results in the most favorable possible collision with the surface on the following tick and the greatest possible amount of retained speed when "launching" off the incline.
|
||||
|
||||
Note that this fix will not be applied if the downhill fix is enabled *and* that fix would result in horizontal speed gain as explained above.
|
||||
|
||||
This plugin also gives you the option of normalizing the random behavior of jumping uphill in the opposite way, such that doing so always results in a *collision* with the surface -- and thus a loss of speed. This setting is not recommended, as jumping up even the slightest of inclines can quickly sap player speed, while doing so without the plugin would almost never result in lost speed. To enable this, set `rngfix_uphill` to `-1`. This effectively makes the uphill incline fix function identically to the downhill incline fix, except it is executed even when moving uphill and when doing so results in horizontal speed loss.
|
||||
|
||||
---
|
||||
**Edge Bugs** [Pre-tick]
|
||||
|
||||
The upcoming tick is simulated to determine if the following are true:
|
||||
1. The player will collide with a walkable surface -- This is important because the general possibility of being able to land/jump but not actually doing so is what defines this bug
|
||||
2. After colliding, the player's Z velocity is less than positive 140.0 (a requirement to be able to land, and thus jump rather than slide)
|
||||
3. Once the *remainder* of the tick is simulated following the collision, the player ends up in a location where there is no ground to land on below them
|
||||
|
||||
If all of these are true, the player's position is adjusted such that they will barely avoid colliding with the ground by the end of the tick. This is effectively the same solution as the uphill incline fix, but with different activating conditions. Occurrences of this issue are more prevalent on **lower** tickrates.
|
||||
|
||||
---
|
||||
**Trigger Jumping** [Post-tick]
|
||||
|
||||
If the plugin detects that the player just landed on the ground, it determines how far below the player the ground is (which could be as many as 2 units below), finds any triggers that are in this space between the player and the ground, and manually signals to the engine that the player is touching these triggers (if the player was not already touching them).
|
||||
|
||||
The rationale behind this is that, if the player is "landed" on the ground, then their hitbox logically must extend all the way to the ground, and thus any triggers in such space should activate. This fix is pretty easy to justify and likely would have been handled better in the engine itself if not for the fact that it *really* only matters in maps made for movement game modes, as thin ground triggers do not come into play in first-party content (and neither does autobhop). Occurrences of this issue are more prevalent on **higher** tickrates.
|
||||
|
||||
---
|
||||
**Telehops** [Post-tick]
|
||||
|
||||
If the plugin detects that a `trigger_teleport` was activated during this tick, and either:
|
||||
* The plugin predicted right before the tick that a collision would occur during this tick (resulting in a change / loss of velocity)
|
||||
*or*
|
||||
* The plugin detected that the client landed during the simulation of the tick (resulting in an instant removal of Z velocity)
|
||||
|
||||
Then the player's velocity is restored to the velocity they would have had after this tick (including any influence from key and mouse inputs) had the player not collided with -- or landed on -- anything.
|
||||
|
||||
The engine simulates each tick in a sequence of discrete steps, which to put it simply starts with a complete simulation of player movement including collisions with any solids, and only *after* this has finished does the engine check to see if the client is touching any triggers and activates them. This means it is not all that unlikely that a player will collide with something inside of or behind a thin `trigger_teleport` before triggering it, despite passing through it to even reach the point of collision. Occurrences of this issue are more prevalent on **lower** tickrates.
|
||||
|
||||
---
|
||||
**Stair Sliding** [Post-tick]
|
||||
|
||||
The plugin checks if the following conditions are true:
|
||||
1. The plugin predicted that a collision with a vertical surface would occur during this tick.
|
||||
2. There is walkable ground directly below the point of collision, within the maximum step size (generally, 18.0 units).
|
||||
3. If the player were to stand on the ground below the point of collision, they would activate no triggers.
|
||||
4. From the ground below the point of collision, the surface collided with can be stepped up (the step must be as high as the maximum step size at most, there is nothing above the player that prevents the player from traveling up that distance, and the surface on top of the step must be walkable).
|
||||
|
||||
If all of these conditions are true, then the player is placed just barely on top of the stair step they just collided with, and the velocity they would have had if they had not collided with the face of the stair step is restored. This issue is mostly unaffected by tickrate.
|
||||
|
||||
This fix is only applied on surf maps (maps starting with `surf_`) because it can save a bhopping player from losing all of their speed if they barely hit a small single step even if they had no intention of sliding. Stairs are very uncommonly found on bhop maps, and even then I can't say I've ever seen a staircase that was worth sliding up as part of an optimal route, so the fix is really not needed on bhop anyway.
|
||||
Loading…
Reference in New Issue
Block a user