Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 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
6a48b75
Implement some of @UplandJacob's changes
Trainboy15 May 26, 2026
0c404b3
Update build artifact
invalid-email-address May 26, 2026
1ca2cda
Sync with upstream
Trainboy15 May 29, 2026
6fb653a
Revert "Sync with upstream"
UplandJacob Jun 2, 2026
517518d
Merge remote-tracking branch 'origin/main' into Trainboy15/main
UplandJacob Jun 2, 2026
be6551f
Update build artifact
invalid-email-address Jun 2, 2026
1363885
Remove unnecessary temp vars (used only once each)
UplandJacob Jun 2, 2026
68a1656
Merge commit 'refs/pull/18/head' of https://github.com/TuffNetwork/Tu…
UplandJacob Jun 2, 2026
f23294f
Update build artifact
invalid-email-address Jun 2, 2026
b8801b2
Remove unused version checking var
UplandJacob Jun 2, 2026
126fd78
Update build artifact
invalid-email-address Jun 2, 2026
7a8a860
Remove old tests
Trainboy15 Jun 3, 2026
2f3586c
Fix test logging configuration in build.gradle
Trainboy15 Jun 3, 2026
c5c1873
Update actions/checkout to v6
UplandJacob Jun 4, 2026
25ce262
Fix buffer index order with optimized loop order
UplandJacob Jun 4, 2026
5802769
Update build artifact
invalid-email-address Jun 4, 2026
b74a4b1
Move test to correct dir
UplandJacob Jun 13, 2026
4c7e53f
Merge branch 'main'
UplandJacob Jun 13, 2026
6e6af3f
Merge commit 'refs/pull/18/head'
UplandJacob Jun 13, 2026
32d4faa
Partially fix test
UplandJacob Jun 14, 2026
d35f378
Unregister outgoing as well
UplandJacob Jun 14, 2026
8cc4058
Merge branch 'main' into main
UplandJacob Jun 14, 2026
38c44be
Add permission for read to make advanced security happy
UplandJacob Jun 14, 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
39 changes: 39 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,39 @@
name: Build & Test

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

permissions:
contents: read

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

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

- 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/
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
31 changes: 24 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,29 @@ dependencies {
compileOnly 'org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT'

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

compileOnly 'com.viaversion:viabackwards:5.3.2'
compileOnly 'com.viaversion:viaversion:5.9.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.6.0'

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

// Tests
testImplementation 'com.viaversion:viaversion:5.9.1'
testImplementation 'com.viaversion:viabackwards:5.3.2'
testImplementation 'io.netty:netty-all:4.1.97.Final'
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.0'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.11.0'
testImplementation 'com.github.seeseemelk:MockBukkit-v1.18:2.85.2'
}

processResources {
Expand All @@ -58,12 +67,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 +89,11 @@ tasks.named('jar') {
tasks.named('build') {
dependsOn shadowJar
}

test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
exceptionFormat = "full"
}
}
Binary file modified builds/TuffXPlus-1.0.1-beta.jar
Comment thread
UplandJacob marked this conversation as resolved.
Binary file not shown.
14 changes: 13 additions & 1 deletion src/main/java/tf/tuff/TuffX.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package tf.tuff;

import java.io.File;

import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
Expand All @@ -13,13 +15,15 @@
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.bukkit.plugin.messaging.PluginMessageListener;

import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerPriority;

import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;

import tf.tuff.netty.ChunkInjector;
import tf.tuff.tuffactions.TuffActions;
import tf.tuff.viablocks.ViaBlocksPlugin;
Expand All @@ -36,6 +40,11 @@ public class TuffX extends JavaPlugin implements Listener, PluginMessageListener
public ViaEntitiesPlugin viaEntitiesPlugin;
private ChunkInjector chunkInjector;

// required by MockBukkit
public TuffX(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
super(loader, description, dataFolder, file);
}

@Override
public void onLoad() {
this.y0Plugin = new Y0Plugin(this);
Expand Down Expand Up @@ -101,6 +110,9 @@ public void onDisable() {
}

PacketEvents.getAPI().terminate();

getServer().getMessenger().unregisterIncomingPluginChannel(this);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
getServer().getMessenger().unregisterOutgoingPluginChannel(this);
}

public void reloadTuffX(){
Expand Down
46 changes: 30 additions & 16 deletions src/main/java/tf/tuff/viablocks/CustomBlockListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
import tf.tuff.netty.ChunkInjector;
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 @@ -81,6 +86,7 @@ public byte[] getCachedChunkData(String worldName, int x, int z) {
}

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

Expand Down Expand Up @@ -256,21 +262,28 @@ 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++) {
for (int z = 0; z < 16; 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 @@ -279,22 +292,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);
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
41 changes: 27 additions & 14 deletions src/main/java/tf/tuff/y0/Y0Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -251,6 +252,7 @@ public boolean isPlayerReady(Player player) {
}

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

Expand Down Expand Up @@ -673,49 +675,60 @@ public void handleChunkLoad(ChunkLoadEvent event) {
}

private byte[] createSectionPayload(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 = threadData.get();
Arrays.fill(bd, (byte) 0);
int idx = 0;
boolean h = false;
boolean hasContent = false;
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));

bd[idx++] = (byte) (lb >> 8);
bd[idx++] = (byte) lb;
bd[idx++] = pl;
// Write sequence
int linear = ((y << 8) | (zz << 4) | xx) * 3;
bd[linear] = (byte) (lb >> 8);
bd[linear + 1] = (byte) lb;
bd[linear + 2] = pl;
if (linear + 3 > idx) idx = linear + 3;

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

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

ByteArrayOutputStream bout = threadOut.get();
bout.reset();

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

return bout.toByteArray();
}

public void handleBlockBreak(BlockBreakEvent event) {
Expand Down Expand Up @@ -887,4 +900,4 @@ private byte[] createLightPayload(ChunkSnapshot s, Coords sc) throws IOException
}
}

}
}
37 changes: 37 additions & 0 deletions src/test/java/tf/tuff/TuffXTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package tf.tuff;

import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import be.seeseemelk.mockbukkit.entity.PlayerMock;
import org.junit.jupiter.api.*;

import static org.junit.jupiter.api.Assertions.*;

class TuffXTest {
Comment thread
UplandJacob marked this conversation as resolved.

private static ServerMock server;
private static TuffX plugin;

@BeforeEach
void setUp() {
server = MockBukkit.mock();
plugin = MockBukkit.load(TuffX.class);
}

@AfterEach
void tearDown() {
MockBukkit.unmock();
}

@Test
void pluginEnablesSuccessfully() {
assertTrue(plugin.isEnabled(), "Plugin should be enabled after load");
}


@Test
void reloadDoesNotThrow() {
assertDoesNotThrow(() -> plugin.reloadTuffX(),
"reloadTuffX() should not throw");
}
}
Loading