Skip to content

Commit

Permalink
more buttons to push
Browse files Browse the repository at this point in the history
  • Loading branch information
qimiko committed Nov 21, 2024
1 parent 50ccef2 commit 4f3c640
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 14 deletions.
31 changes: 29 additions & 2 deletions mod.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
{
"geode": "4.0.0-beta.1",
"geode": "4.0.1",
"gd": {
"mac": "2.2074",
"android": "2.2074"
},
"version": "v1.3.0",
"early-load": true,
"early-load": false,
"tags": [
"gameplay", "performance", "enhancement"
],
"settings": {
"late-input-cutoff": {
"name": "Handle Late Inputs",
"type": "bool",
"default": false,
"description": "When this option is disabled, inputs that are determined to have happened <cl>after the current frame</c> are queued to run at <cy>the start of the next frame</c>, regardless of when the input has happened. Enabling this may <cg>improve accuracy</c> in situations where the ticks per frame is inconsistent (such as lag or 144 fps) but at <cr>the cost of consistency</c> (double clicking)."
},
"input-offset": {
"name": "Input Offset (ms)",
"description": "Offsets all inputs by the selected time in milliseconds. This option will not click before frames. The <cl>Handle Late Inputs</c> may be useful for positive offsets.",
"type": "int",
"default": 0,
"min": -1000,
"max": 1000
},
"input-offset-rand": {
"name": "Input Offset +/-",
"description": "This is the same as the <cl>Input Offset</c> option, but with a <co>randomized offset</c>. Enjoy!",
"type": "int",
"default": 0,
"min": -1000,
"max": 1000
},
"misc-title": {
"type": "title",
"name": "Windows Platform Options",
"platforms": ["win"]
},
"loop-style": {
"name": "Input Loop Type",
"description": "Determines the style of <cf>input loop</c> in use. This is an <cy>advanced option</c>, please avoid changing it unless you know what you are doing!\n<cc>Auto</c> selects the <cg>best choice</c> of loop from your setup. <co>Wait</c> will hold until a new input is received, which has increased precision and performance over <cf>Poll</c>. Using the wait option will also <cr>globally disable</c> controller support.",
Expand Down
69 changes: 58 additions & 11 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ void CustomGJBaseGameLayer::queueButton(int btnType, bool push, bool secondPlaye
// this is another workaround for it not being very easy to pass arguments to things
// oh well, ig

auto& fields = this->m_fields;

auto inputTimestamp = static_cast<AsyncUILayer*>(this->m_uiLayer)->getLastTimestamp();
auto timeRelativeBegin = this->m_fields->m_timeBeginMs;
auto timeRelativeBegin = fields->m_timeBeginMs;

if (!inputTimestamp) {
inputTimestamp = getTimestampCompat();
Expand All @@ -58,14 +60,29 @@ void CustomGJBaseGameLayer::queueButton(int btnType, bool push, bool secondPlaye
}
#endif

// if you felt like it, you could calculate the step too
// i personally don't. this maintains compatibility with physics bypass

auto inputOffset = fields->m_inputOffset;
if (fields->m_inputOffsetRand != 0) {
auto offsetMax = fields->m_inputOffsetRand;
auto randValue = static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
auto randOffset = static_cast<std::int32_t>((offsetMax * 2) * randValue - offsetMax);
inputOffset += randOffset;
}

// if addition would cause a negative time, just reset it to zero
if (inputOffset < 0 && -inputOffset > currentTime) {
currentTime = 0;
} else {
currentTime += inputOffset;
}

#if DEBUG_STEPS
geode::log::debug("queueing input type={} down={} p2={} at time {} (ts {} -> {})", btnType, push, secondPlayer, currentTime, timeRelativeBegin, inputTimestamp);
#endif

// if you felt like it, you could calculate the step too
// i personally don't. this maintains compatibility with physics bypass

this->m_fields->m_timedCommands.push({
fields->m_timedCommands.push({
static_cast<PlayerButton>(btnType),
push,
secondPlayer,
Expand All @@ -81,6 +98,9 @@ void CustomGJBaseGameLayer::resetLevelVariables() {
fields->m_timeBeginMs = 0;
fields->m_timeOffset = 0.0;
fields->m_timedCommands = {};
fields->m_disableInputCutoff = geode::Mod::get()->getSettingValue<bool>("late-input-cutoff");
fields->m_inputOffset = static_cast<std::int32_t>(geode::Mod::get()->getSettingValue<std::int64_t>("input-offset"));
fields->m_inputOffsetRand = static_cast<std::int32_t>(geode::Mod::get()->getSettingValue<std::int64_t>("input-offset-rand"));
}

void CustomGJBaseGameLayer::processTimedInputs() {
Expand All @@ -94,14 +114,14 @@ void CustomGJBaseGameLayer::processTimedInputs() {

auto& commands = fields->m_timedCommands;
if (!commands.empty()) {
auto nextTime = commands.front().m_step;
auto nextTime = commands.top().m_step;

#if DEBUG_STEPS
geode::log::debug("step info: time={}, waiting for {}", timeMs, nextTime);
#endif

while (!commands.empty() && nextTime <= timeMs) {
auto btn = commands.front();
auto btn = commands.top();
commands.pop();

#if DEBUG_STEPS
Expand All @@ -119,17 +139,40 @@ void CustomGJBaseGameLayer::processTimedInputs() {
#endif

if (!commands.empty()) {
nextTime = commands.front().m_step;
nextTime = commands.top().m_step;
}
}
}
}

void CustomGJBaseGameLayer::updateInputQueue() {
// failsafe, but with late input cutoff disabled, so we take each command and reset its time
auto& fields = this->m_fields;
auto& commands = fields->m_timedCommands;
auto timeMs = static_cast<std::int32_t>(fields->m_timeOffset * 1000.0);

PlayerButtonCommandQueue newCommands{};

while (!commands.empty()) {
auto btn = commands.top();
commands.pop();

auto currentStep = btn.m_step;
btn.m_step = std::max(btn.m_step - timeMs, 0);

newCommands.push(btn);
}

commands.swap(newCommands);
}

void CustomGJBaseGameLayer::dumpInputQueue() {
// failsafe, if an input hasn't been processed then we'll force it to be processed by the next frame
auto& commands = this->m_fields->m_timedCommands;
auto& fields = this->m_fields;
auto& commands = fields->m_timedCommands;

while (!commands.empty()) {
auto btn = commands.front();
auto btn = commands.top();
commands.pop();

#if DEBUG_STEPS
Expand All @@ -153,7 +196,11 @@ void CustomGJBaseGameLayer::update(float dt) {

GJBaseGameLayer::update(dt);

dumpInputQueue();
if (fields->m_disableInputCutoff) {
updateInputQueue();
} else {
dumpInputQueue();
}
}

void CustomGJBaseGameLayer::processCommands(float timeStep) {
Expand Down
17 changes: 16 additions & 1 deletion src/main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
#include <Geode/modify/GJBaseGameLayer.hpp>

#include <queue>
#include <vector>

struct PlayerButtonCommandCompare {
constexpr bool operator()(const PlayerButtonCommand& a, const PlayerButtonCommand& b) {
return a.m_step > b.m_step;
}
};

using PlayerButtonCommandQueue = std::priority_queue<PlayerButtonCommand, std::vector<PlayerButtonCommand>, PlayerButtonCommandCompare>;

struct CustomGJBaseGameLayer : geode::Modify<CustomGJBaseGameLayer, GJBaseGameLayer> {
struct Fields {
Expand All @@ -16,7 +25,11 @@ struct CustomGJBaseGameLayer : geode::Modify<CustomGJBaseGameLayer, GJBaseGameLa

// store timed commands separately from the typical input queue
// they will instead be added at the correct timestamp
std::queue<PlayerButtonCommand> m_timedCommands{};
PlayerButtonCommandQueue m_timedCommands{};

bool m_disableInputCutoff{};
std::int32_t m_inputOffset{};
std::int32_t m_inputOffsetRand{};
};

void update(float dt);
Expand All @@ -31,6 +44,8 @@ struct CustomGJBaseGameLayer : geode::Modify<CustomGJBaseGameLayer, GJBaseGameLa
void processCommands(float timeStep);

void processTimedInputs();

void updateInputQueue();
void dumpInputQueue();

// windows workaround
Expand Down

0 comments on commit 4f3c640

Please sign in to comment.