Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d6b2c05
Optomize memory usage and cache usage
Trainboy15 May 18, 2026
6952825
Update version from 1.0.1-beta to 1.0.0-patch
Trainboy15 May 18, 2026
743151f
Remove gradle temp
Trainboy15 May 18, 2026
0a82893
Refactor Gradle CI workflow for improved structure
Trainboy15 May 18, 2026
f499758
Update build artifact
invalid-email-address May 18, 2026
ecedd55
Remove deployment step from gradle.yml
Trainboy15 May 18, 2026
35d2da4
Update build artifact
invalid-email-address May 18, 2026
52ea90c
Fix config
Trainboy15 May 18, 2026
bcfb2b9
Add NPE catches
Trainboy15 May 18, 2026
a632e1f
Merge pull request #1 from Trainboy15/testing
Trainboy15 May 18, 2026
e6d7ee9
Update build artifact
invalid-email-address May 18, 2026
cd5988f
Tab indentation
Trainboy15 May 18, 2026
5b1cae7
Update build artifact
invalid-email-address May 18, 2026
abb5169
Clean up whitespace
UplandJacob May 18, 2026
ab3107e
Update build artifact
invalid-email-address May 18, 2026
b64bd0d
Fix IllegalArgumentException
Trainboy15 May 19, 2026
d5d0b4c
Update build artifact
invalid-email-address May 19, 2026
d7da218
Add update check
Trainboy15 May 19, 2026
c327678
Fixed update checker
Trainboy15 May 19, 2026
854f750
Update build artifact
invalid-email-address May 19, 2026
951fab7
Add testing... IDK if it will work
Trainboy15 May 19, 2026
ddd22ad
Change to java 17
Trainboy15 May 19, 2026
c97efac
Fix dependencies?
Trainboy15 May 19, 2026
8005e8f
FR this time trust
Trainboy15 May 19, 2026
509cf1b
Update build artifact
invalid-email-address May 19, 2026
2967f45
Refactor GitHub Actions workflow for tests
Trainboy15 May 19, 2026
21ac55b
Update build artifact
invalid-email-address May 19, 2026
acf5f53
maybe fix memory issuses
Trainboy15 May 20, 2026
b2a43a6
Update build artifact
invalid-email-address May 20, 2026
707b3cf
Impement @UplandJacob's changes
Trainboy15 May 21, 2026
844dbd9
Update build artifact
invalid-email-address May 21, 2026
9cc2862
undo some things for now
UplandJacob May 23, 2026
c23163c
Update build artifact
invalid-email-address May 23, 2026
e5c2f5f
oop more
UplandJacob May 23, 2026
fe9c89c
Update build artifact
invalid-email-address May 23, 2026
6427b1e
Remove unnecessary line from gradle.yml
UplandJacob May 23, 2026
fb46122
Update build artifact
invalid-email-address May 23, 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
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Comment thread
UplandJacob marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Build & Test

on:
push:
branches: [ "**" ]
pull_request:
branches: [ "**" ]

jobs:
test:
name: Unit Tests
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: gradle

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Run tests
run: ./gradlew test --no-daemon

- name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
with:
name: test-reports
path: build/reports/tests/test/
26 changes: 19 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,30 @@ repositories {
maven { url = 'https://repo.codemc.io/repository/maven-snapshots/' }
maven { url = 'https://jitpack.io' }
maven { url = 'https://repo.dmulloy2.net/repository/public/' }
maven { url = 'https://repo.seeseemelk.be/repository/maven-public/' } // MockBukkit
}

dependencies {
compileOnly 'org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT'

implementation 'com.github.retrooper:packetevents-spigot:2.11.1'

compileOnly 'com.viaversion:viabackwards:5.3.2'
compileOnly 'com.viaversion:viaversion:5.4.1'

compileOnly 'it.unimi.dsi:fastutil:8.5.16'

implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'

compileOnly 'io.netty:netty-all:4.1.97.Final'
compileOnly 'io.netty:netty-all:4.1.97.Final'

implementation 'org.java-websocket:Java-WebSocket:1.5.4'

compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'

testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
testImplementation 'com.github.seeseemelk:MockBukkit-v1.18:3.86.0'
}

processResources {
Expand All @@ -58,12 +62,12 @@ processResources {
shadowJar {
archiveClassifier.set('')
archiveFileName.set("${project.name}-${project.version}.jar")

relocate 'com.github.retrooper.packetevents', 'tf.tuff.packetevents'
relocate 'io.github.retrooper.packetevents', 'tf.tuff.packetevents'
relocate 'com.fasterxml.jackson', 'tf.tuff.jackson'
relocate 'org.java_websocket', 'tf.tuff.websocket'

exclude 'META-INF/*.SF'
exclude 'META-INF/*.DSA'
exclude 'META-INF/*.RSA'
Expand All @@ -80,3 +84,11 @@ tasks.named('jar') {
tasks.named('build') {
dependsOn shadowJar
}

test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
exceptionFormat "full"
}
}
Binary file added builds/TuffXPlus-1.0.0-patch-2.jar
Comment thread
Trainboy15 marked this conversation as resolved.
Binary file not shown.
Binary file added builds/TuffXPlus-1.0.0-patch.jar
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Binary file not shown.
Binary file modified builds/TuffXPlus-1.0.1-beta.jar
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you've made this PR from your main branch, the Action keeps updating the builds. You should really create a separate branch to make this PR from.

Binary file not shown.
35 changes: 35 additions & 0 deletions src/main/java/tf/tuff/TuffX.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ public class TuffX extends JavaPlugin implements Listener, PluginMessageListener
public ViaBlocksPlugin viaBlocksPlugin;
public TuffActions tuffActions;
public ViaEntitiesPlugin viaEntitiesPlugin;
public String latestAvailableVersion = null;

private ChunkInjector chunkInjector;
private static final String CURRENT_VERSION = "1.0.0-patch";
private static final String BUILDS_API_URL = "https://api.github.com/repos/Trainboy15/TuffXPlus/contents/builds";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've still got a lot of update checker things here that should be in a new PR


@Override
public void onLoad() {
Expand Down Expand Up @@ -101,6 +106,8 @@ public void onDisable() {
}

PacketEvents.getAPI().terminate();

getServer().getMessenger().unregisterIncomingPluginChannel(this);
}

public void reloadTuffX(){
Expand Down Expand Up @@ -143,6 +150,25 @@ public boolean TuffXCommand(CommandSender sender, Command command, String label,
return true;
}


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this

// Simple version comparator — handles semver-style and suffix strings
private int compareVersions(String a, String b) {
// Strip non-numeric suffixes for comparison (e.g. "1.0.0-patch" → "1.0.0")
String[] partsA = a.replaceAll("-.*", "").split("\\.");
String[] partsB = b.replaceAll("-.*", "").split("\\.");
int len = Math.max(partsA.length, partsB.length);
for (int i = 0; i < len; i++) {
int numA = i < partsA.length ? parseIntSafe(partsA[i]) : 0;
int numB = i < partsB.length ? parseIntSafe(partsB[i]) : 0;
if (numA != numB) return Integer.compare(numA, numB);
}
return 0;
}

private int parseIntSafe(String s) {
try { return Integer.parseInt(s); } catch (NumberFormatException e) { return 0; }
}

@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (command.getName().equalsIgnoreCase("tuffx")) return TuffXCommand(sender, command, label, args);
Expand Down Expand Up @@ -180,6 +206,15 @@ public void onBlockFade(BlockFadeEvent e) {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent e) {
y0Plugin.handlePlayerJoin(e);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this

if (latestAvailableVersion != null) {
Player p = e.getPlayer();
if (p.hasPermission("tuffx.admin") || p.isOp()) {
p.sendMessage("§e[TuffX] §fA new version is available: §a" + latestAvailableVersion +
" §f(running §c" + CURRENT_VERSION + "§f)");
p.sendMessage("§e[TuffX] §fDownload: §bhttps://github.com/Trainboy15/TuffXPlus/tree/main/builds");
}
}
}

@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
Expand Down
50 changes: 33 additions & 17 deletions src/main/java/tf/tuff/viablocks/CustomBlockListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@

import tf.tuff.viablocks.version.VersionAdapter;

import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;

public class CustomBlockListener {

public final ViaBlocksPlugin plugin;
Expand Down Expand Up @@ -83,6 +88,7 @@ public byte[] getCachedChunkData(String worldName, int x, int z) {
}

public void setChunkInjector(tf.tuff.netty.ChunkInjector injector) {
if (injector == null) { return; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (injector == null) { return; }
if (injector == null) return;

This is what the meant. It literally doesn’t need a block at all.

this.chunkInjector = injector;
}

Expand Down Expand Up @@ -258,21 +264,30 @@ public void cacheChunkWithCallback(World world, int x, int z, Consumer<byte[]> c
}

private Map<Integer, List<Long>> findModernBlocksInChunk(ChunkSnapshot chunkSnapshot, int minHeight, int maxHeight) {
Map<Integer, List<Long>> foundBlocks = new HashMap<>();
Int2ObjectMap<LongList> foundBlocks = new Int2ObjectOpenHashMap<>();

int chunkX = chunkSnapshot.getX() << 4;
int chunkZ = chunkSnapshot.getZ() << 4;

for (int y = minHeight; y < maxHeight; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {

for (int x = 0; x < 16; x++) {
int worldX = chunkX + x;
for (int z = 0; z < 16; z++) {
int worldZ = chunkZ + z;
for (int y = minHeight; y < maxHeight; y++) {

// Check material FIRST — getBlockType() returns an enum, no allocation
Material blockType = chunkSnapshot.getBlockType(x, y, z);
if (blockType == Material.AIR || !this.modernMaterials.contains(blockType)) {

if (blockType == Material.AIR
|| blockType == Material.CAVE_AIR
|| blockType == Material.VOID_AIR
|| !this.modernMaterials.contains(blockType)) {
continue;
}
@SuppressWarnings("null")
@Nonnull BlockData data = chunkSnapshot.getBlockData(x, y, z);

// Only allocate BlockData for confirmed modern blocks
BlockData data = chunkSnapshot.getBlockData(x, y, z);

Integer cachedId = blockDataIdCache.getIfPresent(data);
int materialId;
if (cachedId != null) {
Expand All @@ -281,22 +296,23 @@ private Map<Integer, List<Long>> findModernBlocksInChunk(ChunkSnapshot chunkSnap
materialId = this.paletteManager.getOrCreateId(data.getAsString());
blockDataIdCache.put(data, materialId);
}

if (materialId != -1) {
long packedLocation = packLocation(chunkX + x, y, chunkZ + z);
List<Long> locs = foundBlocks.get(materialId);
long packedLocation = packLocation(worldX, y, worldZ);
LongList locs = foundBlocks.get(materialId);
if (locs == null) {
locs = new ArrayList<>();
locs = new LongArrayList();
foundBlocks.put(materialId, locs);
}
locs.add(packedLocation);
}
}
}
}
return foundBlocks;
}


return (Map<Integer, List<Long>>) (Map<?, ?>) foundBlocks;
}

public byte[] getExtraDataForMultiBlock(World world, List<Long> locations) {
Map<Integer, List<Long>> foundBlocks = new HashMap<>();

Expand Down
31 changes: 20 additions & 11 deletions src/main/java/tf/tuff/y0/Y0Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ public boolean isPlayerReady(Player p) {
}

public void setChunkInjector(tf.tuff.netty.ChunkInjector injector) {
if (injector == null) return;
this.chunkInjector = injector;
}

Expand Down Expand Up @@ -689,49 +690,57 @@ public void handleChunkLoad(org.bukkit.event.world.ChunkLoadEvent e) {
}

private byte[] csp(ChunkSnapshot s, int x, int z, int sy, Object2ObjectOpenHashMap<BlockData, int[]> c) throws IOException {
// Ensure thread-local buffer is exactly 12,288 bytes to prevent overflow
byte[] bd = tlbd.get();
int idx = 0;
boolean h = false;
boolean hasContent = false;
Comment thread
Trainboy15 marked this conversation as resolved.
int by = sy << 4;

for (int y = 0; y < 16; y++) {
int wy = by + y;
// Optimized Loop Order: Matches standard Minecraft internal memory layouts
for (int xx = 0; xx < 16; xx++) {
for (int zz = 0; zz < 16; zz++) {
for (int xx = 0; xx < 16; xx++) {
for (int y = 0; y < 16; y++) {
int wy = by + y;

BlockData bdata = s.getBlockData(xx, wy, zz);
int[] ld = c.getOrDefault(bdata, EMPTY_LEGACY);
if (ld == EMPTY_LEGACY && v != null) {
ld = v.toLegacy(bdata);
int[] ld = c.get(bdata); // Fast map lookup

if (ld == null) { // Avoid getOrDefault overhead
ld = (v != null) ? v.toLegacy(bdata) : EMPTY_LEGACY;
c.put(bdata, ld);
}

// Bitwise packing
short lb = (short) ((ld[1] << 12) | (ld[0] & 0xFFF));
byte pl = (byte) ((s.getBlockSkyLight(xx, wy, zz) << 4) | s.getBlockEmittedLight(xx, wy, zz));

// Write sequence
bd[idx++] = (byte) (lb >> 8);
bd[idx++] = (byte) lb;
bd[idx++] = pl;

if (lb != 0 || pl != 0) {
h = true;
hasContent = true;
}
}
}
}

if (!h) return null;
if (!hasContent) return null;

ByteArrayOutputStream b = tlos.get();
b.reset();

// DataOutputStream wrapper safely writes schema
try (DataOutputStream o = new DataOutputStream(b)) {
o.writeUTF("chunk_data");
o.writeInt(x);
o.writeInt(z);
o.writeInt(sy);
o.write(bd, 0, idx);
return b.toByteArray();
}

return b.toByteArray();
}

public void handleBlockBreak(BlockBreakEvent e) {
Expand Down Expand Up @@ -903,4 +912,4 @@ private byte[] clp(ChunkSnapshot s, CSC sc) throws IOException {
}
}

}
}
Loading