Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3e255aa
Release 5.2.0
rdbtCVS Aug 3, 2026
fcd475d
Use client ctjs api for mixins
rdbtCVS Aug 3, 2026
20b3f2d
Move v5 mixins into ctjs
rdbtCVS Aug 3, 2026
9e01cb0
new rendering
rdbtCVS Aug 3, 2026
b694044
typings
rdbtCVS Aug 3, 2026
916ad5d
fix: delete rdbt-slop; use skija&gizmo not the absurdity he pushed
EpsilonPhoenix Aug 4, 2026
2f41d7e
codex: perf(renderer): reduce Skija frame overhead
rdbtCVS Aug 5, 2026
1651119
codex: optimise pathfinder by 2-3x.
rdbtCVS Aug 5, 2026
2bb1813
codex: optimise pathfinder by 2-3x.
rdbtCVS Aug 5, 2026
1553f5f
fix cache invalidation sorta
rdbtCVS Aug 5, 2026
65ffff5
codex: further optimise pathfinder
rdbtCVS Aug 5, 2026
b88f75f
codex: further optimise pathfinder
rdbtCVS Aug 5, 2026
08a71a6
codex: change double to float for performance improvements in pathfin…
rdbtCVS Aug 7, 2026
760cae7
codex: change double to float for performance improvements in pathfin…
rdbtCVS Aug 7, 2026
6d1a3b5
Refactor RenderUtils -> Render3D and Renderer -> Render2D
rdbtCVS Aug 14, 2026
b0a9541
Merge new renderer into 5.2.0
rdbtCVS Aug 16, 2026
296c6ad
Merge new renderer into 5.2.0
rdbtCVS Aug 16, 2026
339825c
Merge branch '26.1.2' into 5.2.0
rdbtCVS Aug 16, 2026
c09d652
Merge remote-tracking branch 'origin/5.2.0' into 5.2.0
rdbtCVS Aug 16, 2026
d626e3a
Merge remote-tracking branch 'origin/5.2.0' into 5.2.0
rdbtCVS Aug 16, 2026
ba0823f
Remove legacy mod loader support
rdbtCVS Aug 17, 2026
2a85254
codex: 26.2 support
rdbtCVS Aug 18, 2026
22933c7
typings grrr
rdbtCVS Aug 18, 2026
68d28b3
ponytail + testing agents.md
rdbtCVS Aug 18, 2026
f9c723f
codex: optimise rendering
rdbtCVS Aug 19, 2026
4b016db
bump deps
rdbtCVS Aug 19, 2026
2ae7269
add bulk 3d render apis
rdbtCVS Aug 19, 2026
c83cc36
typings upadte
rdbtCVS Aug 19, 2026
e6abc33
ConcurrentHashMap image cache
rdbtCVS Aug 19, 2026
ad43578
optimise text rendering (like 10% gui optimisation)
rdbtCVS Aug 19, 2026
55a7918
codex: add vulkan support
rdbtCVS Aug 19, 2026
8372986
fix background process getting left
rdbtCVS Aug 19, 2026
d62bc70
fix
rdbtCVS Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ permissions:

jobs:
build:
name: Build
name: Build Minecraft ${{ matrix.minecraft }}
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
matrix:
minecraft: [26.1.2, 26.2]

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Expand All @@ -29,7 +32,7 @@ jobs:
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
distribution: temurin
java-version: 21
java-version: 25

- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
Expand All @@ -41,18 +44,18 @@ jobs:
${{ runner.os }}-gradle-

- name: Build
run: ./gradlew --no-daemon build -PreleaseBuild
run: ./gradlew --no-daemon :${{ matrix.minecraft }}:build -PreleaseBuild

- name: Upload Loader
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: V5-Loader-26.1.2
path: build/libs/V5-Loader-26.1.2.jar
name: V5-Loader-${{ matrix.minecraft }}
path: versions/${{ matrix.minecraft }}/build/libs/V5-Loader-${{ matrix.minecraft }}.jar
if-no-files-found: error

publish:
needs: build
if: ${{ github.ref == 'refs/heads/26.1.2' }}
if: ${{ github.ref == 'refs/heads/26.1.2' || github.ref == 'refs/heads/26.2' }}
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: write
Expand All @@ -65,8 +68,9 @@ jobs:
- name: Download Loader
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: V5-Loader-26.1.2
pattern: V5-Loader-*
path: build/libs
merge-multiple: true

- name: Publish GitHub release
env:
Expand All @@ -82,7 +86,7 @@ jobs:
tag="${version}-r$(( ${revision:-0} + 1 ))"
gh release create "$tag" --target "$SHA" --title "$tag" --generate-notes
fi
gh release upload "$tag" build/libs/V5-Loader-26.1.2.jar --clobber
gh release upload "$tag" build/libs/V5-Loader-*.jar --clobber

- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
Expand All @@ -97,6 +101,9 @@ jobs:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
sha256sum build/libs/V5-Loader-26.1.2.jar | awk '{print $1}' > V5-Loader-26.1.2.jar.sha256
wrangler r2 object put v5-builds/V5-Loader-26.1.2.jar.sha256 --file V5-Loader-26.1.2.jar.sha256 --remote
wrangler r2 object put v5-builds/V5-Loader-26.1.2.jar --file build/libs/V5-Loader-26.1.2.jar --remote
for jar in build/libs/V5-Loader-*.jar; do
name=$(basename "$jar")
sha256sum "$jar" | awk '{print $1}' > "$name.sha256"
wrangler r2 object put "v5-builds/$name.sha256" --file "$name.sha256" --remote
wrangler r2 object put "v5-builds/$name" --file "$jar" --remote
done
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ replay_*.log
*.jfr

versions/*/api/
versions/*/logs/

grunt-main.jar
grunt-main.jar
42 changes: 42 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Repository Guidelines

## Coding Style

You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.

Before writing any code, stop at the first rung that holds:

Does this need to be built at all? (YAGNI)
Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
Does the standard library already do this? Use it.
Does a native platform feature cover it? Use it.
Does an already-installed dependency solve it? Use it.
Can this be one line? Make it one line.
Only then: write the minimum code that works.
The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb.

Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken.

Rules:

No abstractions that weren't explicitly requested.
No new dependency if it can be avoided.
No boilerplate nobody asked for.
Deletion over addition. Boring over clever. Fewest files possible.
Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
Question complex requests: "Do you actually need X, or does Y cover it?"
Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
Mark deliberate simplifications that cut a real corner with a known ceiling (global lock, O(n²) scan, naive heuristic) with a ponytail: comment naming the ceiling and upgrade path.
Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.

## Build, Test, and Development Process

- `./gradlew build` — compile all supported Minecraft versions and produces `versions/<version>/build/libs/V5-Loader-DEV-<version>.jar`.
- `./gradlew generateTypings` — regenerate `typing-generator/src/main/resources/typings.d.ts` after exported API changes.

## Testing Guidelines

Do not implement any testing coverage.
Before finishing, run `./gradlew build` only, do not use any other gradlew commands for confirming build status.
Changes affecting API should run `./gradlew generateTypings`. NEVER manually edit the `typings.d.ts` file EVER.
For native changes, rebuild with CMake for local only; Automated workflows will handle building all natives on push.
62 changes: 32 additions & 30 deletions NativeSrc/src/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,40 +50,42 @@ inline uint64_t chunkKey(const int x, const int z) {
return (px << 32) | pz;
}

struct ActionCosts {
static constexpr double INF_COST = 1e6;
static constexpr double FLY_ONE_BLOCK_TIME = 1.0 / 0.7;
static constexpr double SPRINT_ONE_BLOCK_TIME = 1.0 / 0.2806;
static constexpr double SPRINT_DIAGONAL_TIME = SPRINT_ONE_BLOCK_TIME * 1.4142135623730951;
static constexpr double MOMENTUM_LOSS_PENALTY = 6.0;
static constexpr double JUMP_PENALTY = 2.0;
static constexpr double GAP_JUMP_REWARD_OFFSET = 1.5;
static constexpr double SLAB_ASCENT_TIME = SPRINT_ONE_BLOCK_TIME * 1.1;
static constexpr double WALK_OFF_EDGE_TIME = SPRINT_ONE_BLOCK_TIME * 0.5;
static constexpr double LAND_RECOVERY_TIME = 2.0;
static constexpr double JUMP_UP_ONE_BLOCK_TIME = 28.0 + MOMENTUM_LOSS_PENALTY + SPRINT_ONE_BLOCK_TIME;

std::array<double, 257> fallTimes{};
constexpr std::array<float, 257> makeFallTimes() {
std::array<float, 257> fallTimes{};
float currentDistance = 0.0f;
int tick = 0;
float velocity = 0.0f;

ActionCosts() {
double currentDistance = 0.0;
int tick = 0;
double velocity = 0.0;

for (int targetDistance = 1; targetDistance <= 256; targetDistance++) {
while (currentDistance < targetDistance) {
velocity = (velocity - 0.08) * 0.98;
currentDistance -= velocity;
tick++;
}
fallTimes[static_cast<size_t>(targetDistance)] = static_cast<double>(tick);
for (int targetDistance = 1; targetDistance <= 256; targetDistance++) {
while (currentDistance < targetDistance) {
velocity = (velocity - 0.08f) * 0.98f;
currentDistance -= velocity;
tick++;
}
fallTimes[static_cast<size_t>(targetDistance)] = static_cast<float>(tick);
}
return fallTimes;
}

struct ActionCosts {
static constexpr float INF_COST = 1e6f;
static constexpr float FLY_ONE_BLOCK_TIME = 1.0f / 0.7f;
static constexpr float SPRINT_ONE_BLOCK_TIME = 1.0f / 0.2806f;
static constexpr float SPRINT_DIAGONAL_TIME = SPRINT_ONE_BLOCK_TIME * 1.41421356f;
static constexpr float MOMENTUM_LOSS_PENALTY = 6.0f;
static constexpr float JUMP_PENALTY = 2.0f;
static constexpr float GAP_JUMP_REWARD_OFFSET = 1.5f;
static constexpr float SLAB_ASCENT_TIME = SPRINT_ONE_BLOCK_TIME * 1.1f;
static constexpr float WALK_OFF_EDGE_TIME = SPRINT_ONE_BLOCK_TIME * 0.5f;
static constexpr float LAND_RECOVERY_TIME = 2.0f;
static constexpr float JUMP_UP_ONE_BLOCK_TIME = 28.0f + MOMENTUM_LOSS_PENALTY + SPRINT_ONE_BLOCK_TIME;

inline static constexpr auto FALL_TIMES = makeFallTimes();

[[nodiscard]] double getFallTime(const int blocks) const {
if (blocks <= 0) return 0.0;
if (blocks >= static_cast<int>(fallTimes.size())) return INF_COST;
return fallTimes[static_cast<size_t>(blocks)] + LAND_RECOVERY_TIME;
[[nodiscard]] static float getFallTime(const int blocks) {
if (blocks <= 0) return 0.0f;
if (blocks >= static_cast<int>(FALL_TIMES.size())) return INF_COST;
return FALL_TIMES[static_cast<size_t>(blocks)] + LAND_RECOVERY_TIME;
}
};

Expand Down
4 changes: 2 additions & 2 deletions NativeSrc/src/etherwarp_search.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ struct SimplifiedEtherwarpPath {

struct SharedState {
explicit SharedState(const int reserveTarget)
: heap(nodeF, nodeHeapPos) {
: heap(nodeF, nodeH, nodeG, nodeHeapPos) {
nodeX.reserve(static_cast<size_t>(reserveTarget));
nodeY.reserve(static_cast<size_t>(reserveTarget));
nodeZ.reserve(static_cast<size_t>(reserveTarget));
Expand Down Expand Up @@ -135,7 +135,7 @@ struct SharedState {
std::vector<float> nodePitch;
std::vector<int> nodeHeapPos;
std::unordered_map<uint64_t, int> coordToNode;
detail::Heap heap;
detail::Heap<double> heap;

int iterations = 0;
int activeExpanders = 0;
Expand Down
6 changes: 3 additions & 3 deletions NativeSrc/src/jni_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ JNIEXPORT jobject JNICALL Java_com_chattriggers_ctjs_api_world_pathfinding_Nativ
avoidMetaFlat[base + 2],
avoidMetaFlat[base + 3],
avoidMetaFlat[base + 4],
avoidPenaltyFlat[i],
static_cast<float>(avoidPenaltyFlat[i]),
});
}
}
Expand All @@ -355,8 +355,8 @@ JNIEXPORT jobject JNICALL Java_com_chattriggers_ctjs_api_world_pathfinding_Nativ
params.goals = goals;
params.isFly = isFly == JNI_TRUE;
params.maxIterations = static_cast<int>(maxIterations);
params.heuristicWeight = static_cast<double>(heuristicWeight);
params.nonPrimaryStartPenalty = static_cast<double>(nonPrimaryStartPenalty);
params.heuristicWeight = static_cast<float>(heuristicWeight);
params.nonPrimaryStartPenalty = static_cast<float>(nonPrimaryStartPenalty);
params.moveOrderOffset = static_cast<int>(moveOrderOffset);
params.avoidZones = std::move(avoidZones);

Expand Down
45 changes: 45 additions & 0 deletions NativeSrc/src/lazy_section_array.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include "common.hpp"

#include <array>
#include <memory>
#include <unordered_map>

namespace v5pf::detail {

template<typename T>
class LazySectionArray {
public:
T& at(const int x, const int y, const int z) {
const uint64_t key = coordKey(x >> 4, y >> 4, z >> 4);
const size_t cursorIdx = static_cast<size_t>((key ^ (key >> 26) ^ (key >> 38)) & 3ULL);
auto& cursor = cursors_[cursorIdx];
if (cursor.section == nullptr || cursor.key != key) {
auto [it, inserted] = sections_.try_emplace(key);
if (inserted) it->second = std::make_unique<Section>();
cursor = {key, it->second.get()};
}

const size_t index = static_cast<size_t>(((y & 15) << 8) | ((z & 15) << 4) | (x & 15));
return (*cursor.section)[index];
}

void clear() {
sections_.clear();
cursors_ = {};
}

private:
using Section = std::array<T, 4096>;

struct Cursor {
uint64_t key = 0;
Section* section = nullptr;
};

std::unordered_map<uint64_t, std::unique_ptr<Section>> sections_;
std::array<Cursor, 4> cursors_{};
};

} // namespace v5pf::detail
8 changes: 0 additions & 8 deletions NativeSrc/src/path_simplifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,6 @@ std::vector<Int3> extractKeyPoints(
if (canSimplify) {
if (isFly) {
canSimplify = canFlyDirectly(world, start, end);
} else {
for (int i = from + 1; i < to; i++) {
const Int3& p = points[static_cast<size_t>(i)];
if (!isWalkSafeVoxel(world, p.x, p.y, p.z)) {
canSimplify = false;
break;
}
}
}
}

Expand Down
Loading
Loading