Compare commits

..

26 Commits

Author SHA1 Message Date
Helvetica Volubi ac3653dda7 [ci skip]Update Gradle wrapper to version 9.2.0 2025-11-12 00:56:44 +08:00
Helvetica Volubi a08fd04751 Update Luminol to fix a sentry bug 2025-11-12 00:20:46 +08:00
Helvetica Volubi f5debe9ee1 [ci skip] Update Luminol 2025-11-10 17:27:08 +08:00
Helvetica Volubi 1a84d2e670 Update Luminol to fix a crash bug in config command system 2025-11-10 00:27:08 +08:00
Helvetica Volubi e96891275d [ci skip] update datapack command save 2025-11-09 02:38:51 +08:00
Helvetica Volubi 9e342c6683 Update Luminol 2025-11-06 00:12:13 +08:00
Helvetica Volubi 00f7f5ee2b fix: fixes #69 2025-11-01 02:53:48 +08:00
Helvetica Volubi 77b9582c4a Update Luminol 2025-10-31 22:23:17 +08:00
Helvetica Volubi 184048fd26 Update Luminol 2025-10-31 00:23:24 +08:00
Helvetica Volubi 7ccca03023 try to fix BBOR protocol 2025-10-29 02:09:22 +08:00
Helvetica Volubi b34fa8237e feat: provided void trade instead of infinite trade
fixes #77
2025-10-29 00:20:45 +08:00
Helvetica Volubi 42a4d18717 refactor: deprecated tickCount 2025-10-27 21:40:42 +08:00
Helvetica Volubi 2d8d7f5784 refactor: prepare to drop reuse tickCount 2025-10-27 21:11:51 +08:00
Helvetica Volubi bf509bcded feat: wool hopper counter(#27) 2025-10-27 19:59:03 +08:00
Helvetica Volubi ef6759a9ab [ci skip] Update Luminol to skip release by robot 2025-10-27 05:40:37 +00:00
Helvetica Volubi bc1483ea7a fix: try to fix Concurrent throw in protocol 2025-10-27 00:10:44 +08:00
Helvetica Volubi 5768a60ba8 fix: rewrite request block data method 2025-10-25 03:22:14 +08:00
Helvetica Volubi 05434f422c fix: fixes #63 2025-10-23 00:20:15 +08:00
Helvetica Volubi 21e9299352 Update code from Leaves 2025-10-22 11:34:52 +08:00
Helvetica Volubi 83e7718346 fix: try fix #63 2025-10-22 02:41:51 +08:00
Helvetica Volubi 5845b976c1 fix: try fix #70 2025-10-22 02:15:15 +08:00
xiaoxijun 4ca94280ae Add support for more mod protocols (#73)
* Add Syncmatica protocols

* Add AppleSkin protocols

* Add BBOR Protocol

* Add Jade Protocol

* Add Xaero Map Protocol

* Add Support REI protocol

* Fixed an issue where the configuration file was not loading correctly.

* move config file directory

* add license & refactor code

* update leaves fix

https://github.com/LeavesMC/Leaves/issues/747#event-19986621829

* Update Luminol

* fix up jade protocol

* fix up rei

---------

Co-authored-by: Helvetica Volubi <suisuroru@blue-millennium.fun>
Co-authored-by: Helvetica Volubi <88063803+Suisuroru@users.noreply.github.com>
2025-10-20 00:06:40 +08:00
Helvetica Volubi 4cc49c6d99 fix: fix bot save command 2025-10-20 00:02:21 +08:00
Helvetica Volubi 93c0258c7e fix: use local lastTickCount to save data 2025-10-19 23:40:17 +08:00
Helvetica Volubi 43a5ea0385 Update Luminol 2025-10-18 02:34:13 +08:00
Helvetica Volubi 678882a435 Update Luminol & fix a bug in shutdown server 2025-10-14 19:14:12 +08:00
166 changed files with 10810 additions and 316 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
run: ./gradlew --refresh-dependencies createMojmapPaperclipJar
- name: Upload Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ env.project_id_b }} CI Artifacts
path: lophine-server/build/libs/*-paperclip-*-mojmap.jar
+1 -1
View File
@@ -4,7 +4,7 @@ mcVersion=1.21.8
release=2
# 0 for skip release, 1 for pre-release, 2 for release
luminolRef=747ad851920a569d00857edea61c861c018ea684
luminolRef=a3c596e26bfdf197a1c0c7a536c8a2bf7f97c09d
org.gradle.configuration-cache=true
org.gradle.caching=true
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -5,7 +5,7 @@ Subject: [PATCH] Transformed Configs
diff --git a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
index 66ae993896ba38a7fa5302d4ff88a52e4a0bb72c..c6d53d720437f43a713469049de3d90d1082cd2e 100644
index 4051384e82dba0416838c3c9889a6e368367b601..a5535b479cdcca870d0c8be6ed6fbcb32f266ac2 100644
--- a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
@@ -11,6 +11,7 @@ public class CommandConfig implements IConfigModule {
@@ -14,8 +14,8 @@ index 66ae993896ba38a7fa5302d4ff88a52e4a0bb72c..c6d53d720437f43a713469049de3d90d
public static boolean data = false;
+ @TransformedConfig(name = "command_block_enabled", directory = {"experiment", "command"}, originInstance = "lophine")
@TransformedConfig(name = "enabled", directory = {"experiment", "force_enable_command_block_command_execution"})
@ConfigInfo(name = "enable_command_block")
public static boolean commandBlock = false;
@ConfigInfo(name = "enable_command_block", comments = """
Force to enable command blocks.
diff --git a/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java b/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
index 35e6014f04093050a9a62b895b43e5c5d8c6a28e..11abfb884f972fd73ca4c13a6e309de1bcd23fd5 100644
--- a/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
@@ -1,60 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 7 Jul 2025 16:30:54 +0800
Subject: [PATCH] Add config to allow infinite trade
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index 79242037e868a140513f032e74e8594c0c6ff41c..050e89c3e95c7c3ef7ad98f8ef03ab206241a7e2 100644
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -490,8 +490,15 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
}
private void resendOffersToTradingPlayer() {
- MerchantOffers offers = this.getOffers();
+ // Lophine start - infinite trade
+ MerchantOffers offers = this.getOffers().copy();
Player tradingPlayer = this.getTradingPlayer();
+ if (fun.bm.lophine.config.modules.function.VillagerConfig.villagerInfiniteTrade) {
+ for (MerchantOffer merchantOffer : offers) {
+ merchantOffer.maxUses = 524288;
+ }
+ }
+ // Lophine end - infinite trade
if (tradingPlayer != null && !offers.isEmpty()) {
tradingPlayer.sendMerchantOffers(
tradingPlayer.containerMenu.containerId,
diff --git a/net/minecraft/world/item/trading/Merchant.java b/net/minecraft/world/item/trading/Merchant.java
index 6e2fa442ddcd4ef3c4fe789b9ebac9dd409c9de7..0c7318a0ed214204f5493576c8a7b02fbe51c00b 100644
--- a/net/minecraft/world/item/trading/Merchant.java
+++ b/net/minecraft/world/item/trading/Merchant.java
@@ -42,7 +42,14 @@ public interface Merchant {
new SimpleMenuProvider((containerId, inventory, player1) -> new MerchantMenu(containerId, inventory, this), displayName)
);
if (optionalInt.isPresent()) {
- MerchantOffers offers = this.getOffers();
+ // Lophine start - infinite trade
+ MerchantOffers offers = this.getOffers().copy();
+ if (fun.bm.lophine.config.modules.function.VillagerConfig.villagerInfiniteTrade) {
+ for (MerchantOffer merchantOffer : offers) {
+ merchantOffer.maxUses = 5242884;
+ }
+ }
+ // Lophine end - infinite trade
if (!offers.isEmpty()) {
player.sendMerchantOffers(optionalInt.getAsInt(), offers, level, this.getVillagerXp(), this.showProgressBar(), this.canRestock());
}
diff --git a/net/minecraft/world/item/trading/MerchantOffer.java b/net/minecraft/world/item/trading/MerchantOffer.java
index 64c99df8ff305fa28c75dc03fc5ef8c61634ad84..770f37d7158c7b4cec62783025f623a68c1f2457 100644
--- a/net/minecraft/world/item/trading/MerchantOffer.java
+++ b/net/minecraft/world/item/trading/MerchantOffer.java
@@ -196,7 +196,7 @@ public class MerchantOffer {
}
public boolean isOutOfStock() {
- return this.uses >= this.maxUses;
+ return (this.uses >= this.maxUses && !fun.bm.lophine.config.modules.function.VillagerConfig.villagerInfiniteTrade); // Lophine - infinite trade
}
public void setToOutOfStock() {
@@ -18,7 +18,7 @@ index 6868b915bf3deb85783a638d4441a15fea6da2dc..70740381c6501c1a518c52b24381edd1
double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
if (rangeY != -1) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 90c36bb87a761119edb376f3bd425d48d29bf36f..a720d8db40ca56c997a4eeae14ebbc6fe764892c 100644
index f5539ca5964abb18124522a8aa7ce940517b2011..1258591aae1b926ee8085b1984d553b8d3a1df7e 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
@@ -30,7 +30,7 @@ index 90c36bb87a761119edb376f3bd425d48d29bf36f..a720d8db40ca56c997a4eeae14ebbc6f
// CraftBukkit start
private static final int CURRENT_LEVEL = 2;
public boolean preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; keep initial motion on first snapTo
@@ -6474,4 +6474,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -6482,4 +6482,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
// Paper end - Expose entity id counter
public boolean shouldTickHot() { return this.tickCount > 20 * 10 && this.isAlive(); } // KioCG
@@ -5,10 +5,10 @@ Subject: [PATCH] Add config to enable function command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index af895859057ca303177ec3c8a07cf7534e62cca6..e0e9fcf3224f55f2ef31c8afca3154a5992f761b 100644
index 11697e65a7f7871920ca7bad37de07f78a3da1f8..25ccbfa12d57d540195ecc16aa54a144f8ecc0e8 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -206,7 +206,9 @@ public class Commands {
@@ -204,7 +204,9 @@ public class Commands {
FillCommand.register(this.dispatcher, context);
FillBiomeCommand.register(this.dispatcher, context);
ForceLoadCommand.register(this.dispatcher);
@@ -1,59 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 2 Jul 2025 23:36:25 +0800
Subject: [PATCH] Add config to enable datapack command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index e05f75b6bbd48d6fb7379926d68d5f8ed6014be9..af895859057ca303177ec3c8a07cf7534e62cca6 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -192,7 +192,9 @@ public class Commands {
if(me.earthme.luminol.config.modules.experiment.CommandConfig.data) {
DataCommands.register(this.dispatcher); // Folia - region threading - TODO
}
- //DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.datapack) {
+ DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
+ }
//DebugCommand.register(this.dispatcher); // Folia - region threading - TODO
DefaultGameModeCommands.register(this.dispatcher);
//DialogCommand.register(this.dispatcher, context); // Folia - region threading - TODO
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 75027e73859c955a54034640f905cb5d91a6a2f4..b4b3ff86f7c4afb94fc4da5133746391a078a7fb 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2353,6 +2353,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
+ // Lophine start - region thread skip reload // TODO - add reload support
+ public void saveDatapacks(Collection<String> selectedIds) {
+ this.packRepository.setSelected(selectedIds, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above)
+ WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration(
+ getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()
+ );
+ this.worldData.setDataConfiguration(worldDataConfiguration);
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
+
public CompletableFuture<Void> reloadResources(Collection<String> selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause) {
// Paper end - Add ServerResourcesReloadedEvent
CompletableFuture<Void> completableFuture = CompletableFuture.<ImmutableList>supplyAsync(
diff --git a/net/minecraft/server/commands/ReloadCommand.java b/net/minecraft/server/commands/ReloadCommand.java
index b540a3c3a02f7242578a63477e34b9063e86f1d2..9f0adf157872e1c68cd7752a24eacdc636e8d17a 100644
--- a/net/minecraft/server/commands/ReloadCommand.java
+++ b/net/minecraft/server/commands/ReloadCommand.java
@@ -16,6 +16,12 @@ public class ReloadCommand {
private static final Logger LOGGER = LogUtils.getLogger();
public static void reloadPacks(Collection<String> selectedIds, CommandSourceStack source) {
+ // Lophine start - region thread skip reload // TODO - add reload support
+ if (true) {
+ source.getServer().saveDatapacks(selectedIds);
+ return;
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
source.getServer().reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally(throwable -> { // Paper - Add ServerResourcesReloadedEvent
LOGGER.warn("Failed to execute reload", throwable);
source.sendFailure(Component.translatable("commands.reload.failure"));
@@ -5,10 +5,10 @@ Subject: [PATCH] Add config to enable scoreboard command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index e0e9fcf3224f55f2ef31c8afca3154a5992f761b..6633af3cff77bcd4bcc591c032d647b7a850dced 100644
index 25ccbfa12d57d540195ecc16aa54a144f8ecc0e8..22d211bee83e3ef34d6ac81dd89c0e73924bee90 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -231,7 +231,11 @@ public class Commands {
@@ -229,7 +229,11 @@ public class Commands {
//RotateCommand.register(this.dispatcher); // Folia - region threading - TODO later
SayCommand.register(this.dispatcher);
//ScheduleCommand.register(this.dispatcher); // Folia - region threading
@@ -97,10 +97,10 @@ index c485d5b3ba95f2a969bdf0dab9654ac284976f8d..b957585cf7cf7f845e20f59c7b355722
this.scheduler.regionFailed(this, false, thr);
// regionFailed will schedule a shutdown, so we should avoid letting this region tick further
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 6633af3cff77bcd4bcc591c032d647b7a850dced..dc5a558c72a5a7ab8a650262c6a1a2dc5142b8fc 100644
index 22d211bee83e3ef34d6ac81dd89c0e73924bee90..1fb359073342a657e9e493403263b56be5b3393f 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -251,7 +251,11 @@ public class Commands {
@@ -249,7 +249,11 @@ public class Commands {
TeleportCommand.register(this.dispatcher);
TellRawCommand.register(this.dispatcher, context);
//TestCommand.register(this.dispatcher, context); // Folia - region threading
@@ -114,7 +114,7 @@ index 6633af3cff77bcd4bcc591c032d647b7a850dced..dc5a558c72a5a7ab8a650262c6a1a2dc
TitleCommand.register(this.dispatcher, context);
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index b4b3ff86f7c4afb94fc4da5133746391a078a7fb..b156b5c635f931a3d4abc0584591f90476d011a6 100644
index 75027e73859c955a54034640f905cb5d91a6a2f4..2f4970242c754cfba8ff5656a63af07d9d0a723c 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -267,7 +267,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -5,10 +5,10 @@ Subject: [PATCH] Add config to enable waypoint command & bar
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index dc5a558c72a5a7ab8a650262c6a1a2dc5142b8fc..9eee09ecc18abcc5972f65938ba8607dfeede026 100644
index 1fb359073342a657e9e493403263b56be5b3393f..451a685782da46f5683ea6707289bbaf3193771f 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -259,7 +259,11 @@ public class Commands {
@@ -257,7 +257,11 @@ public class Commands {
TimeCommand.register(this.dispatcher);
TitleCommand.register(this.dispatcher, context);
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
@@ -0,0 +1,94 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 2 Jul 2025 23:36:25 +0800
Subject: [PATCH] Fix datapack command save function
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 2f4970242c754cfba8ff5656a63af07d9d0a723c..b156b5c635f931a3d4abc0584591f90476d011a6 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2353,6 +2353,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
+ // Lophine start - region thread skip reload // TODO - add reload support
+ public void saveDatapacks(Collection<String> selectedIds) {
+ this.packRepository.setSelected(selectedIds, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above)
+ WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration(
+ getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()
+ );
+ this.worldData.setDataConfiguration(worldDataConfiguration);
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
+
public CompletableFuture<Void> reloadResources(Collection<String> selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause) {
// Paper end - Add ServerResourcesReloadedEvent
CompletableFuture<Void> completableFuture = CompletableFuture.<ImmutableList>supplyAsync(
diff --git a/net/minecraft/server/commands/DataPackCommand.java b/net/minecraft/server/commands/DataPackCommand.java
index a5032b34752061d61fd9ddb4db828d35e403f5e7..3eaa9c85d06f6ecc8ed639d8befa861439390eb7 100644
--- a/net/minecraft/server/commands/DataPackCommand.java
+++ b/net/minecraft/server/commands/DataPackCommand.java
@@ -98,7 +98,7 @@ public class DataPackCommand {
dispatcher.register(
Commands.literal("datapack")
.requires(Commands.hasPermission(2))
- /*.then( // Luminol - Add back read-only datapack command
+ .then(
Commands.literal("enable")
.then(
Commands.argument("name", StringArgumentType.string())
@@ -160,11 +160,11 @@ public class DataPackCommand {
.suggests(SELECTED_PACKS)
.executes(commandContext -> disablePack(commandContext.getSource(), getPack(commandContext, "name", false)))
)
- )*/ // Luminol - Add back read-only datapack command
+ )
.then(
Commands.literal("list")
.executes(commandContext -> listPacks(commandContext.getSource()))
- // .then(Commands.literal("available").executes(commandContext -> listAvailablePacks(commandContext.getSource()))) // Luminol - Add back read-only datapack command
+ .then(Commands.literal("available").executes(commandContext -> listAvailablePacks(commandContext.getSource())))
.then(Commands.literal("enabled").executes(commandContext -> listEnabledPacks(commandContext.getSource())))
)
.then(
@@ -252,7 +252,7 @@ public class DataPackCommand {
}
private static int listPacks(CommandSourceStack source) {
- return listEnabledPacks(source) ;// + listAvailablePacks(source); // Luminol - Add back read-only datapack command
+ return listEnabledPacks(source) + listAvailablePacks(source);
}
private static int listAvailablePacks(CommandSourceStack source) {
@@ -280,7 +280,7 @@ public class DataPackCommand {
private static int listEnabledPacks(CommandSourceStack source) {
PackRepository packRepository = source.getServer().getPackRepository();
- // packRepository.reload(); // Luminol - Add back read-only datapack command
+ packRepository.reload();
Collection<? extends Pack> selectedPacks = packRepository.getSelectedPacks();
if (selectedPacks.isEmpty()) {
source.sendSuccess(() -> Component.translatable("commands.datapack.list.enabled.none"), false);
diff --git a/net/minecraft/server/commands/ReloadCommand.java b/net/minecraft/server/commands/ReloadCommand.java
index b540a3c3a02f7242578a63477e34b9063e86f1d2..bc999de32931558d0d8dc26264653dd63c1cad19 100644
--- a/net/minecraft/server/commands/ReloadCommand.java
+++ b/net/minecraft/server/commands/ReloadCommand.java
@@ -16,11 +16,17 @@ public class ReloadCommand {
private static final Logger LOGGER = LogUtils.getLogger();
public static void reloadPacks(Collection<String> selectedIds, CommandSourceStack source) {
+ // Lophine start - region thread skip reload // TODO - add reload support
+ source.getServer().saveDatapacks(selectedIds);
+ source.getSender().sendMessage("You need to restart server to load datapacks update.");
+ if (false) {
source.getServer().reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally(throwable -> { // Paper - Add ServerResourcesReloadedEvent
LOGGER.warn("Failed to execute reload", throwable);
source.sendFailure(Component.translatable("commands.reload.failure"));
return null;
});
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
}
private static Collection<String> discoverNewPacks(PackRepository packRepository, WorldData worldData, Collection<String> selectedIds) {
@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 29 Oct 2025 00:09:09 +0800
Subject: [PATCH] Leaves: Configurable trading with the void
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 985ddea9d74a37f58f32a27375f6f7e6af0f2192..9796c449f924b8fd9c0dd6bb02cf196641e495ed 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2901,7 +2901,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Spigot start
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
// Paper start - Fix merchant inventory not closing on entity removal
- if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) {
+ if (!fun.bm.lophine.config.modules.function.OldFeatureConfig.villagerVoidTrade && entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { // Leaves - Configurable trading with the void
merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED);
}
// Paper end - Fix merchant inventory not closing on entity removal
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 593dbcda4b497291eb1ecdc64b947a8d279f8be6..6c1f4f943a5be5f2e43a82cca52ca76a98226915 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -649,7 +649,7 @@ public abstract class PlayerList {
player.stopRiding();
rootVehicle.getPassengersAndSelf().forEach(entity -> {
// Paper start - Fix villager boat exploit
- if (entity instanceof net.minecraft.world.entity.npc.AbstractVillager villager) {
+ if (!fun.bm.lophine.config.modules.function.OldFeatureConfig.villagerVoidTrade && entity instanceof net.minecraft.world.entity.npc.AbstractVillager villager) { // Leaves - Configurable trading with the void
final net.minecraft.world.entity.player.Player human = villager.getTradingPlayer();
if (human != null) {
villager.setTradingPlayer(null);
diff --git a/net/minecraft/world/inventory/MerchantMenu.java b/net/minecraft/world/inventory/MerchantMenu.java
index d59f67ffe34201c63e3d9706a4434f33b6732edb..c635c7211bff9d190b7035b284dda162334fd5c2 100644
--- a/net/minecraft/world/inventory/MerchantMenu.java
+++ b/net/minecraft/world/inventory/MerchantMenu.java
@@ -74,6 +74,7 @@ public class MerchantMenu extends AbstractContainerMenu {
@Override
public boolean stillValid(Player player) {
+ if (fun.bm.lophine.config.modules.function.OldFeatureConfig.villagerVoidTrade) return this.trader.getTradingPlayer() == player; // Leaves - Configurable trading with the void
if (!checkReachable) return true; // Paper - checkReachable
return this.trader.stillValid(player);
}
@@ -1,59 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 28 Sep 2025 13:21:30 +0800
Subject: [PATCH] Rewrite tickCount support
diff --git a/io/papermc/paper/threadedregions/TickRegionScheduler.java b/io/papermc/paper/threadedregions/TickRegionScheduler.java
index b957585cf7cf7f845e20f59c7b355722880f3b4e..9b279d003eb9f780ed2723c98b28d9f4d6e3a281 100644
--- a/io/papermc/paper/threadedregions/TickRegionScheduler.java
+++ b/io/papermc/paper/threadedregions/TickRegionScheduler.java
@@ -412,6 +412,8 @@ public final class TickRegionScheduler {
}
// Luminol end - Add tick command support
+ MinecraftServer.getServer().handleTickCount(tickCount); // Lophine - reuse tick count
+
if (!this.tryMarkTicking()) {
if (!this.cancelled.get()) {
throw new IllegalStateException("Scheduled region should be acquirable");
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index b156b5c635f931a3d4abc0584591f90476d011a6..ef0b401affa58077e8dd32b52063202a1c7e0bb6 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -305,6 +305,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Folia start - regionised ticking
public final io.papermc.paper.threadedregions.RegionizedServer regionizedServer = new io.papermc.paper.threadedregions.RegionizedServer();
+ private int tickCount; // Lophine - reuse tick count
+ private int lastTickCount;
@Override
public <V> CompletableFuture<V> submit(java.util.function.Supplier<V> task) {
@@ -2227,9 +2229,25 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return false;
}
+ // Lophine start - reuse tick count
public int getTickCount() {
- throw new UnsupportedOperationException(); // Folia - region threading
+ return tickCount;
+ }
+
+ public boolean checkTickCount(int period) {
+ if (tickCount % period == 0) {
+ return true;
+ }
+
+ int nextPeriodTick = ((lastTickCount / period) + 1) * period;
+ return nextPeriodTick < tickCount;
+ }
+
+ public void handleTickCount(int tickCount) {
+ this.lastTickCount = this.tickCount;
+ this.tickCount += tickCount;
}
+ // Lophine end - reuse tick count
public int getSpawnProtectionRadius() {
return 16;
@@ -446,7 +446,7 @@ index 10b2cc20bbf3b70e4e09dbbe14b90506e6366158..1535bb8cec8692e8d26eaff090288f1b
}
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index aca3303a043144894ae20ddc43abc55790db44ad..55cbf9b4cc568fb9b418433f597ed6b64e4409b0 100644
index 699bb3cc2b680a4ee438a88c05854e5ab05ea910..d4fe03fd766caca1d7693a5317460ca1a9b9fa4f 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -148,7 +148,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -458,7 +458,7 @@ index aca3303a043144894ae20ddc43abc55790db44ad..55cbf9b4cc568fb9b418433f597ed6b6
}
@Override
@@ -770,9 +770,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -743,9 +743,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
if (item.isEmpty()) {
// Spigot start - SPIGOT-6693, SimpleContainer#setItem
ItemStack leftover = ItemStack.EMPTY; // Paper - Make hoppers respect inventory max stack size
@@ -1,7 +1,7 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sat, 30 Aug 2025 17:16:32 +0800
Subject: [PATCH] Leaves: Leaves Fakeplayer
Subject: [PATCH] Leaves: Fakeplayer
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
@@ -33,10 +33,10 @@ index 66ec0424a46dcd49cf44467357d80b1a2d84d3b2..04ae8de63af0a8abe578f14c8ef85fd4
private DisconnectionDetails disconnectionDetails;
private boolean encrypted;
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index ef0b401affa58077e8dd32b52063202a1c7e0bb6..c461ab6c6ab21008be893f84913f0e6f9cf6d336 100644
index b156b5c635f931a3d4abc0584591f90476d011a6..81726e5782d25ff35a2bea8820a62786e46148b1 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -349,6 +349,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -347,6 +347,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
// Folia end - regionised ticking
@@ -45,7 +45,7 @@ index ef0b401affa58077e8dd32b52063202a1c7e0bb6..c461ab6c6ab21008be893f84913f0e6f
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
AtomicReference<S> atomicReference = new AtomicReference<>();
@@ -1040,6 +1042,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1038,6 +1040,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Folia end - region threading
public void stopServer() {
@@ -57,7 +57,7 @@ index ef0b401affa58077e8dd32b52063202a1c7e0bb6..c461ab6c6ab21008be893f84913f0e6f
// Folia start - region threading
// halt scheduler
// don't wait, we may be on a scheduler thread
@@ -1590,7 +1597,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1588,7 +1595,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
int i = this.pauseWhileEmptySeconds() * 20;
this.removeDisabledPluginsBlockingSleep(); // Paper - API to allow/disallow tick sleeping
if (false && i > 0) { // Folia - region threading - this is complicated to implement, and even if done correctly is messy
@@ -66,7 +66,7 @@ index ef0b401affa58077e8dd32b52063202a1c7e0bb6..c461ab6c6ab21008be893f84913f0e6f
this.emptyTicks++;
} else {
this.emptyTicks = 0;
@@ -1914,6 +1921,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1912,6 +1919,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public void tickConnection() {
this.getConnection().tick();
@@ -74,7 +74,7 @@ index ef0b401affa58077e8dd32b52063202a1c7e0bb6..c461ab6c6ab21008be893f84913f0e6f
}
private void synchronizeTime(ServerLevel level) {
@@ -3001,6 +3009,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2983,6 +2991,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return 0;
}
@@ -219,7 +219,7 @@ index f2286b96b8f40b4588f817913c42ae7b4a92340f..e6c7bbb023000b9de90c1256274ff5ab
playerList.op(gameProfile);
i++;
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 79c911003c7b1c3d94e7e9d3974eeadcc16bde3a..328428f0ef8a2305f661c167e6fe1f604b0d6890 100644
index 0e278f7b9ed85ce8c3cedd9154f817b30ff1e48a..09d4b1d68c3f046ef304016a1afe7a3038dfae3e 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -162,6 +162,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -266,7 +266,7 @@ index 70740381c6501c1a518c52b24381edd16792507f..5e31b499a894113b4be1982a1071348f
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 576dde41a4b48aac9079cca902f4371a31470af3..952b92c55d7192791a19ecebe80894f6cf992049 100644
index 4f1976c47a087bf4f36687124ef29ca7b664f682..96aafe4d63446b3d212f21f14091858f72bb081f 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -207,6 +207,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -298,7 +298,7 @@ index 576dde41a4b48aac9079cca902f4371a31470af3..952b92c55d7192791a19ecebe80894f6
@Override
public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
Optional<Holder<PoiType>> optional = PoiTypes.forState(oldState);
@@ -2826,6 +2834,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2827,6 +2835,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.add(serverPlayer);
@@ -310,7 +310,7 @@ index 576dde41a4b48aac9079cca902f4371a31470af3..952b92c55d7192791a19ecebe80894f6
if (serverPlayer.isReceivingWaypoints()) {
ServerLevel.this.getWaypointManager().addPlayer(serverPlayer);
}
@@ -2913,6 +2926,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2914,6 +2927,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
ServerLevel.this.getChunkSource().removeEntity(entity);
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
@@ -434,7 +434,7 @@ index 144a2644c15f276f02bb3be859dc5d05a677ac55..c6a89e8936cffa49b3de153ed3c40e3c
boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
GameProfileCache.GameProfileInfo gameProfileInfo = this.profilesByName.get(string);
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index cb863a36fc67e993956ec3be655e6e4b9764c051..908ec29a6e285122d1a8a43dc547b74af2697442 100644
index 8e2775e873c92beed6a0e4e8ce1304ab8ca6e8d1..580f114fd99b017403c61e994d0c947d00e5115d 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -425,6 +425,19 @@ public abstract class PlayerList {
@@ -445,7 +445,7 @@ index cb863a36fc67e993956ec3be655e6e4b9764c051..908ec29a6e285122d1a8a43dc547b74a
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) {
+ org.leavesmc.leaves.bot.ServerBot bot = this.server.getBotList().getBotByName(player.getScoreboardName());
+ if (bot != null) {
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false);
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false, true);
+ }
+ this.server.getBotList().bots.forEach(bot1 -> {
+ bot1.sendPlayerInfo(player);
@@ -548,7 +548,7 @@ index 52fd730998535ea071bfc99b7cc2c254b9b656d7..0abf6f0265fcb916f2c2c76fb1313bd1
for (WaypointTransmitter waypointTransmitter : this.waypoints) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index a720d8db40ca56c997a4eeae14ebbc6fe764892c..118a21e7ba82832560fb0d8fb8d6b21beeb14000 100644
index 1258591aae1b926ee8085b1984d553b8d3a1df7e..365cb2aed3d0f428ba33d4debdf3c3a3c494882d 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1225,7 +1225,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 16:51:01 +0800
Subject: [PATCH] Leaves: Syncmatica Protocol
This patch is Powered by Syncmatica(https://github.com/End-Tech/syncmatica)
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index e17f9629369f8653b484b4cc71de4874b5faa4d6..9be4bc9e78c22c12ba594b16dac6901023f0fd47 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -330,9 +330,12 @@ public class ServerGamePacketListenerImpl
this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(player.getUUID(), server::enforceSecureProfile);
this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat
this.tickEndEvent = new io.papermc.paper.event.packet.ClientTickEndEvent(player.getBukkitEntity()); // Paper - add client tick end event
+ this.exchangeTarget = new org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget(this); // Leaves - Syncmatica Protocol
this.playerGameConnection = new io.papermc.paper.connection.PaperPlayerGameConnection(this); // Paper
}
+ public final org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget exchangeTarget; // Leaves - Syncmatica Protocol
+
// Paper start - configuration phase API
@Override
public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
@@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 13:03:42 +0800
Subject: [PATCH] Leaves: BBOR Protocol
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 29788fedd54b8411c9c8872b4d83b037874fb5f0..24af6821694768058dc0146220877ab70066b5f9 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -754,6 +754,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
public void setLoaded(boolean loaded) {
this.loaded = loaded;
+ // Leaves start - bbor
+ if (loaded) {
+ org.leavesmc.leaves.protocol.BBORProtocol.onChunkLoaded(this);
+ }
+ // Leaves end - bbor
}
public Level getLevel() {
@@ -0,0 +1,114 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 13:33:19 +0800
Subject: [PATCH] Leaves: Jade Protocol
This patch is Powered by Jade(https://github.com/Snownee/Jade)
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
index c1798db2972c8f2a343cf6e16fd9354ff212d906..a8d617b16ab2b2c0cdb289a0aa05fa171940cd7e 100644
--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java
+++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
@@ -63,7 +63,7 @@ public class Armadillo extends Animal {
public final AnimationState rollOutAnimationState = new AnimationState();
public final AnimationState rollUpAnimationState = new AnimationState();
public final AnimationState peekAnimationState = new AnimationState();
- private int scuteTime;
+ public int scuteTime; // Leaves - private -> public
private boolean peekReceivedClient = false;
public Armadillo(EntityType<? extends Animal> entityType, Level level) {
diff --git a/net/minecraft/world/entity/animal/frog/Tadpole.java b/net/minecraft/world/entity/animal/frog/Tadpole.java
index 17f58246849ed407821a987b200cc765eb7943f9..ac27df3ba0ce9bbdf2f32ea87171fbb9407008d6 100644
--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
@@ -254,7 +254,7 @@ public class Tadpole extends AbstractFish {
}
}
- private int getTicksLeftUntilAdult() {
+ public int getTicksLeftUntilAdult() { // Leaves - private -> public
return Math.max(0, ticksToBeFrog - this.age);
}
diff --git a/net/minecraft/world/level/storage/loot/LootPool.java b/net/minecraft/world/level/storage/loot/LootPool.java
index 6901e629d941e22e64d83eed4e8cfee3165a96a1..fdc26c8d8c82c20534c57af2a0281b99998cc9f6 100644
--- a/net/minecraft/world/level/storage/loot/LootPool.java
+++ b/net/minecraft/world/level/storage/loot/LootPool.java
@@ -37,7 +37,7 @@ public class LootPool {
)
.apply(instance, LootPool::new)
);
- private final List<LootPoolEntryContainer> entries;
+ public final List<LootPoolEntryContainer> entries; // Leaves - private -> public
private final List<LootItemCondition> conditions;
private final Predicate<LootContext> compositeCondition;
private final List<LootItemFunction> functions;
diff --git a/net/minecraft/world/level/storage/loot/LootTable.java b/net/minecraft/world/level/storage/loot/LootTable.java
index 8612cdf7161f8ddff60a6478cc901318b8f958ba..07a962d647baa99b0e1bf3898a07cc914e91397e 100644
--- a/net/minecraft/world/level/storage/loot/LootTable.java
+++ b/net/minecraft/world/level/storage/loot/LootTable.java
@@ -50,7 +50,7 @@ public class LootTable {
public static final LootTable EMPTY = new LootTable(LootContextParamSets.EMPTY, Optional.empty(), List.of(), List.of());
private final ContextKeySet paramSet;
private final Optional<ResourceLocation> randomSequence;
- private final List<LootPool> pools;
+ public final List<LootPool> pools; // Leaves - private -> public
private final List<LootItemFunction> functions;
private final BiFunction<ItemStack, LootContext, ItemStack> compositeFunction;
public org.bukkit.craftbukkit.CraftLootTable craftLootTable; // CraftBukkit
diff --git a/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java b/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
index eeaa49e9f70a18b5d39493aeff73f31b05ac2faa..8cd0403d7873c4c37caef75935b06b056c3d951d 100644
--- a/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
+++ b/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
@@ -16,7 +16,7 @@ public abstract class CompositeEntryBase extends LootPoolEntryContainer {
return "Empty children list";
}
};
- protected final List<LootPoolEntryContainer> children;
+ public final List<LootPoolEntryContainer> children; // Leaves - private -> public
private final ComposableEntryContainer composedChildren;
protected CompositeEntryBase(List<LootPoolEntryContainer> children, List<LootItemCondition> conditions) {
diff --git a/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java b/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
index 65e27bce9e59ef97bc8b914d646fba924d0f0877..a49bdcdf37b351436e0ba6d7865f10827c4e6ab4 100644
--- a/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
+++ b/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
@@ -14,7 +14,7 @@ import net.minecraft.world.level.storage.loot.predicates.ConditionUserBuilder;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
public abstract class LootPoolEntryContainer implements ComposableEntryContainer {
- protected final List<LootItemCondition> conditions;
+ public final List<LootItemCondition> conditions; // Leaves - private -> public
private final Predicate<LootContext> compositeCondition;
protected LootPoolEntryContainer(List<LootItemCondition> conditions) {
diff --git a/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java b/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
index 141026601cd9a4561426b85fd1f8e7dc0544fbd7..a5d7ebb93c147bf0f806ac3c9b2dc4b878573944 100644
--- a/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
+++ b/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
@@ -29,7 +29,7 @@ public class NestedLootTable extends LootPoolSingletonContainer {
return "->{inline}";
}
};
- private final Either<ResourceKey<LootTable>, LootTable> contents;
+ public final Either<ResourceKey<LootTable>, LootTable> contents; // Leaves - private -> public
private NestedLootTable(
Either<ResourceKey<LootTable>, LootTable> contents, int weight, int quality, List<LootItemCondition> conditions, List<LootItemFunction> functions
diff --git a/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java b/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
index bae72197acc929c7ed3e964f156115d728eb2176..8f3094f42f3366a1313d70c0b27fbe5632b2082a 100644
--- a/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
+++ b/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
@@ -12,7 +12,7 @@ import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.ValidationContext;
public abstract class CompositeLootItemCondition implements LootItemCondition {
- protected final List<LootItemCondition> terms;
+ public final List<LootItemCondition> terms; // Leaves - private -> public
private final Predicate<LootContext> composedPredicate;
protected CompositeLootItemCondition(List<LootItemCondition> terms, Predicate<LootContext> composedPredicate) {
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Fri, 7 Feb 2025 14:23:43 +0800
Subject: [PATCH] Leaves: Xaero Map Protocol
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 908ec29a6e285122d1a8a43dc547b74af2697442..1bf1731e43278b71423fee5d92401c48d6dabaca 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -1247,6 +1247,7 @@ public abstract class PlayerList {
player.connection.send(new ClientboundInitializeBorderPacket(worldBorder));
player.connection.send(new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)));
player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getSharedSpawnPos(), level.getSharedSpawnAngle()));
+ org.leavesmc.leaves.protocol.XaeroMapProtocol.onSendWorldInfo(player); // Leaves - xaero map protocol
if (level.isRaining()) {
// CraftBukkit start - handle player weather
// player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F));
@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Thu, 27 Mar 2025 13:04:35 +0800
Subject: [PATCH] Leaves: Support REI protocol
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
index 9bc0a9c3577d63a0ad5489bfd4c07d5006245c5f..bb2e891539b7eb49dc7925630695149aa531f672 100644
--- a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
+++ b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
@@ -83,6 +83,12 @@ public class SmithingTransformRecipe implements SmithingRecipe {
);
}
+ // Leaves start - REI
+ public SlotDisplay getResult() {
+ return this.result.display();
+ }
+ // Leaves end - REI
+
// CraftBukkit start
@Override
public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) {
diff --git a/net/minecraft/world/item/crafting/SmithingTrimRecipe.java b/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
index c324896afc2ee28ebb4d426ce4a469ee847ce24d..d5b26ff7be916e07e1162536cb2ddf5674c82f46 100644
--- a/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
+++ b/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
@@ -85,6 +85,12 @@ public class SmithingTrimRecipe implements SmithingRecipe {
return Optional.of(this.addition);
}
+ // Leaves start
+ public Holder<TrimPattern> pattern() {
+ return pattern;
+ }
+ // Leaves end
+
@Override
public RecipeSerializer<SmithingTrimRecipe> getSerializer() {
return RecipeSerializer.SMITHING_TRIM;
@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 27 Oct 2025 15:38:29 +0800
Subject: [PATCH] Leaves: Wool Hopper Counter
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: MIT
This patch is Powered by fabric-carpet(https://github.com/gnembon/fabric-carpet)
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index d4fe03fd766caca1d7693a5317460ca1a9b9fa4f..a821deaa5c076151336762d17a8da45abb4c43cd 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -232,8 +232,30 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items
}
+ // Leaves start - Wool hopper counter
+ if (fun.bm.lophine.config.modules.function.WoolHopperCounterConfig.unlimitedSpeed && org.leavesmc.leaves.util.HopperCounter.isEnabled()) {
+ net.minecraft.world.item.DyeColor woolColor = org.leavesmc.leaves.util.WoolUtils.getWoolColorAtPosition(level, blockEntity.getBlockPos().relative(state.getValue(HopperBlock.FACING)));
+ if (woolColor != null) {
+ for (int i = 0; i < Short.MAX_VALUE; i++) {
+ flag |= suckInItems(level, blockEntity);
+ if (!flag) {
+ break;
+ } else {
+ woolHopperCounter(level, pos, state, HopperBlockEntity.getContainerAt(level, pos));
+ }
+ }
+ }
+ }
+ // Leaves end - Wool hopper counter
+
if (flag) {
blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
+ // Leaves start - Wool hopper counter
+ if (fun.bm.lophine.config.modules.function.WoolHopperCounterConfig.unlimitedSpeed && org.leavesmc.leaves.util.HopperCounter.isEnabled() && woolHopperCounter(level, pos, state, HopperBlockEntity.getContainerAt(level, pos))) {
+ blockEntity.setCooldown(0);
+ return true;
+ }
+ // Leaves end - Wool hopper counter
setChanged(level, pos, state);
// Leaves start - Lithium Sleeping Block Entity
if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled
@@ -465,6 +487,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// Paper end - Perf: Optimize Hoppers
private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) {
+ // Leaves start - hopper counter
+ if (org.leavesmc.leaves.util.HopperCounter.isEnabled()) {
+ if (woolHopperCounter(level, pos, level.getBlockState(pos), HopperBlockEntity.getContainerAt(level, pos))) {
+ return true;
+ }
+ }
+ // Leaves end - hopper counter
Container attachedContainer = me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled ? blockEntity.getInsertInventory(level) : getAttachedContainer(level, pos, blockEntity); // Leaves - Lithium Sleeping Block Entity
if (attachedContainer == null) {
return false;
@@ -541,6 +570,26 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
+ // Leaves start - hopper counter
+ private static boolean woolHopperCounter(Level level, BlockPos blockPos, BlockState state, @Nullable Container container) {
+ if (container == null) {
+ return false;
+ }
+ net.minecraft.world.item.DyeColor woolColor = org.leavesmc.leaves.util.WoolUtils.getWoolColorAtPosition(level, blockPos.relative(state.getValue(HopperBlock.FACING)));
+ if (woolColor != null) {
+ for (int i = 0; i < container.getContainerSize(); ++i) {
+ if (!container.getItem(i).isEmpty()) {
+ ItemStack itemstack = container.getItem(i);
+ org.leavesmc.leaves.util.HopperCounter.getCounter(woolColor).add(level, itemstack);
+ container.setItem(i, ItemStack.EMPTY);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ // Leaves end - hopper counter
+
private static int[] getSlots(Container container, Direction direction) {
if (container instanceof WorldlyContainer worldlyContainer) {
return worldlyContainer.getSlotsForFace(direction);
@@ -683,6 +732,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public static boolean addItem(Container container, ItemEntity item) {
+ if (fun.bm.lophine.config.modules.function.WoolHopperCounterConfig.unlimitedSpeed && org.leavesmc.leaves.util.HopperCounter.isEnabled() && item.isRemoved()) return false; // Leaves - Wool hopper counter
boolean flag = false;
// CraftBukkit start
if (org.bukkit.event.inventory.InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers
@@ -161,7 +161,7 @@ index 852e1ffef6a022caad7c8eff34091e50112a2290..8eb5d014d9ed688ffebaffb4ce0bb408
if (entity instanceof EnderDragonPart complexPart) {
if (complexPart.parentMob instanceof EnderDragon) {
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index e040f9a7c9451fbe547dda56952410047a99ddc0..05753de195bd7f4ac2952183662a8cc9ec8f4ba0 100644
index 317837a2a9b3b511b5cac4df4db248d4652816dc..a8253d1a3129c8258e9ead7c430e1bcf82f735d7 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -897,7 +897,11 @@ public class CraftEventFactory {
@@ -0,0 +1,41 @@
package fun.bm.lophine.command.counter;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import fun.bm.lophine.command.counter.sub.DisplayCommand;
import fun.bm.lophine.command.counter.sub.ResetCommand;
import fun.bm.lophine.command.counter.sub.ToggleCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.command.RootNode;
import org.leavesmc.leaves.util.HopperCounter;
public class CounterCommand extends RootNode {
private final String PERM_BASE;
public CounterCommand() {
super("counter", "lophine.commands.counter");
this.PERM_BASE = "lophine.commands.counter";
children(
new ToggleCommand(this),
new ResetCommand(this),
new DisplayCommand(this)
);
}
@Override
protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
context.getSender().sendMessage(Component.join(JoinConfiguration.noSeparators(),
Component.text("Hopper Counter: ", NamedTextColor.GRAY),
Component.text(HopperCounter.isEnabled(), HopperCounter.isEnabled() ? NamedTextColor.AQUA : NamedTextColor.GRAY)
));
return true;
}
public boolean hasPermission(@NotNull CommandSender sender, String... subcommand) {
return hasPermission(PERM_BASE, sender, subcommand);
}
}
@@ -0,0 +1,24 @@
package fun.bm.lophine.command.counter;
import net.minecraft.commands.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.LiteralNode;
public class CounterSubCommand extends LiteralNode {
protected final CounterCommand parent;
protected CounterSubCommand(String name, CounterCommand parent) {
super(name);
this.parent = parent;
}
@Override
public boolean requires(@NotNull CommandSourceStack source) {
return hasPermission(source.getSender());
}
protected boolean hasPermission(CommandSender sender) {
return parent.hasPermission(sender, this.name);
}
}
@@ -0,0 +1,91 @@
package fun.bm.lophine.command.counter.sub;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import fun.bm.lophine.command.counter.CounterCommand;
import fun.bm.lophine.command.counter.CounterSubCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.item.DyeColor;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.ArgumentNode;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.util.HopperCounter;
import java.util.concurrent.CompletableFuture;
public class DisplayCommand extends CounterSubCommand {
public DisplayCommand(CounterCommand parent) {
super("display", parent);
children(
DyeColorArg::new
);
}
public static void displayCounter(CommandContext context, @NotNull HopperCounter counter, boolean realTime) {
for (Component component : counter.format(MinecraftServer.getServer(), context.getSource().getLevel(), realTime)) {
context.getSender().sendMessage(component);
}
}
private static class DyeColorArg extends ArgumentNode<String> {
protected DyeColorArg() {
super("color", StringArgumentType.string());
children(
TimeArg::new
);
}
@Override
protected boolean execute(@NotNull CommandContext context) {
String color0 = context.getArgument(DyeColorArg.class);
DyeColor color = DyeColor.byName(color0, null);
if (color == null) return true;
HopperCounter counter = HopperCounter.getCounter(color);
displayCounter(context, counter, false);
return true;
}
@Override
protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
String path = context.getArgumentOrDefault(DyeColorArg.class, "");
for (DyeColor value : DyeColor.values()) {
String color = value.getName();
if (color.startsWith(path)) {
builder.suggest(color);
}
}
return builder.buildFuture();
}
private static class TimeArg extends ArgumentNode<String> {
protected TimeArg() {
super("time", StringArgumentType.string());
}
@Override
protected boolean execute(@NotNull CommandContext context) {
String color0 = context.getArgument(DyeColorArg.class);
DyeColor color = DyeColor.byName(color0, null);
if (color == null) return true;
HopperCounter counter = HopperCounter.getCounter(color);
String timeType = context.getArgument(TimeArg.class);
switch (timeType) {
case "realtime" -> displayCounter(context, counter, true);
case "gametick" -> displayCounter(context, counter, false);
default ->
context.getSender().sendMessage(Component.text("Invalid time type: " + timeType, NamedTextColor.RED));
}
return true;
}
protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
builder.suggest("realtime");
builder.suggest("gametick");
return builder.buildFuture();
}
}
}
}
@@ -0,0 +1,75 @@
package fun.bm.lophine.command.counter.sub;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import fun.bm.lophine.command.counter.CounterCommand;
import fun.bm.lophine.command.counter.CounterSubCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.format.TextColor;
import net.minecraft.world.item.DyeColor;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.ArgumentNode;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.util.HopperCounter;
import java.util.concurrent.CompletableFuture;
public class ResetCommand extends CounterSubCommand {
public ResetCommand(CounterCommand parent) {
super("reset", parent);
children(
DyeColorArg::new
);
}
@Override
protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
HopperCounter.resetAll(context.getSource().getLevel(), false);
context.getSender().sendMessage(Component.text("Restarted all counters."));
return true;
}
private static class DyeColorArg extends ArgumentNode<String> {
protected DyeColorArg() {
super("color", StringArgumentType.string());
}
@Override
protected boolean execute(@NotNull CommandContext context) {
String color0 = context.getArgument(DyeColorArg.class);
if (color0.equals("all")) {
HopperCounter.resetAll(context.getSource().getLevel(), false);
context.getSender().sendMessage(Component.text("Restarted all counters."));
return true;
}
DyeColor color = DyeColor.byName(color0, null);
if (color == null) return true;
HopperCounter counter = HopperCounter.getCounter(color);
counter.reset(context.getSource().getLevel());
context.getSender().sendMessage(Component.join(JoinConfiguration.noSeparators(),
Component.text("Restarted "),
Component.text(color.getName(), TextColor.color(color.getTextColor())),
Component.text(" counter.")
));
return true;
}
@Override
protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
String path = context.getArgumentOrDefault(DyeColorArg.class, "");
if ("all".startsWith(path)) {
builder.suggest("all");
}
for (DyeColor value : DyeColor.values()) {
String color = value.getName();
if (color.startsWith(path)) {
builder.suggest(color);
}
}
return builder.buildFuture();
}
}
}
@@ -0,0 +1,55 @@
package fun.bm.lophine.command.counter.sub;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import fun.bm.lophine.command.counter.CounterCommand;
import fun.bm.lophine.command.counter.CounterSubCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.ArgumentNode;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.util.HopperCounter;
public class ToggleCommand extends CounterSubCommand {
public ToggleCommand(CounterCommand parent) {
super("toggle", parent);
children(
BooleanArg::new
);
}
@Override
protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
if (!HopperCounter.isEnabled()) {
HopperCounter.setEnabled(true);
context.getSender().sendMessage(Component.text("Hopper Counter now is enabled.", NamedTextColor.AQUA));
} else {
HopperCounter.setEnabled(false);
context.getSender().sendMessage(Component.text("Hopper Counter now is disabled.", NamedTextColor.RED));
}
return true;
}
private static class BooleanArg extends ArgumentNode<Boolean> {
protected BooleanArg() {
super("enabled", BoolArgumentType.bool());
}
@Override
protected boolean execute(@NotNull CommandContext context) {
boolean enabled = context.getArgument(BooleanArg.class);
if (enabled == HopperCounter.isEnabled()) {
context.getSender().sendMessage(Component.text("Hopper Counter is already " + (enabled ? "enabled" : "disabled") + ".", NamedTextColor.GRAY));
} else {
HopperCounter.setEnabled(enabled);
if (enabled) {
context.getSender().sendMessage(Component.text("Hopper Counter now is enabled.", NamedTextColor.AQUA));
} else {
context.getSender().sendMessage(Component.text("Hopper Counter now is disabled.", NamedTextColor.RED));
}
}
return true;
}
}
}
@@ -13,12 +13,6 @@ public class CommandConfig implements IConfigModule {
Allow to use tick command""")
public static boolean tick = false;
@ConfigInfo(name = "datapack_command_enabled", comments =
"""
Allow to use datapack command
--- datapack hot-update unsupported, please restart server""")
public static boolean datapack = false;
@ConfigInfo(name = "function_command_enabled", comments =
"""
Allow to use function command""")
@@ -5,10 +5,12 @@ import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.command.bot.BotCommand;
import java.util.List;
import java.util.Set;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "fakeplayer")
public class FakeplayerConfig implements IConfigModule {
@@ -91,7 +93,7 @@ public class FakeplayerConfig implements IConfigModule {
}
@Override
public void onLoaded(CommentedFileConfig configInstance) {
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> exs) {
if (enable) {
command = new BotCommand();
command.register();
@@ -24,4 +24,12 @@ public class OldFeatureConfig implements IConfigModule {
@TransformedConfig(name = "give_bad_omen_when_kill_raid_captain", directory = {"misc", "revert_raid_changes"}, transformComments = false)
@ConfigInfo(name = "old_raid_behavior")
public static boolean oldRaidBehavior = false;
@TransformedConfig(name = "villager-infinite-trade", directory = {"function", "villager"}, transformComments = false)
@TransformedConfig(name = "villager-infinite-trade", directory = {"misc", "villager"}, transformComments = false)
@TransformedConfig(name = "villager-infinite-trade", directory = {"misc", "villager-config"}, transformComments = false)
@ConfigInfo(name = "villager-void-trade", comments =
"""
Allow villager void trade.""")
public static boolean villagerVoidTrade = false;
}
@@ -1,19 +0,0 @@
package fun.bm.lophine.config.modules.function;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.config.flags.TransformedConfig;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.MISC, name = "villager")
public class VillagerConfig implements IConfigModule {
@TransformedConfig(name = "villager-infinite-trade", directory = {"misc", "villager"})
@TransformedConfig(name = "villager-infinite-trade", directory = {"misc", "villager-config"})
@ConfigInfo(name = "villager-infinite-trade", comments =
"""
Allow villager infinite trade (limit of 524288 times)
---- we won't edit saved data, only edit in send data to client.
(after we fixed void trade, this config will drop to void trade)""")
public static boolean villagerInfiniteTrade = false;
}
@@ -0,0 +1,31 @@
package fun.bm.lophine.config.modules.function;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import fun.bm.lophine.command.counter.CounterCommand;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "wool-hopper-counter")
public class WoolHopperCounterConfig implements IConfigModule {
@ConfigInfo(name = "enabled")
public static boolean enabled = false;
@ConfigInfo(name = "unlimited-speed")
public static boolean unlimitedSpeed = false;
@Override
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> exs) {
if (enabled) new CounterCommand().register();
}
@Override
public void onUnloaded(CommentedFileConfig configInstance) {
Bukkit.getCommandMap().getKnownCommands().remove("luminol:tpsbar");
}
}
@@ -0,0 +1,16 @@
package fun.bm.lophine.config.modules.function.protocol;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "appleskin", directory = {"protocol"})
public class AppleSkinProtocolConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Enable AppleSkin protocol support""")
public static boolean enabled = false;
@ConfigInfo(name = "sync-tick-interval", comments = """
Set AppleSkin Synchronization Frequency (Unit: Game Ticks)""")
public static int syncTickInterval = 20;
}
@@ -0,0 +1,13 @@
package fun.bm.lophine.config.modules.function.protocol;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "bbor", directory = {"protocol"})
public class BBORProtocolConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Enable BBOR protocol support""")
public static boolean enabled = false;
}
@@ -0,0 +1,13 @@
package fun.bm.lophine.config.modules.function.protocol;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "jade", directory = {"protocol"})
public class JadeProtocolConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Enable Jade protocol support""")
public static boolean enabled = false;
}
@@ -0,0 +1,13 @@
package fun.bm.lophine.config.modules.function.protocol;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "rei", directory = {"protocol"})
public class REIServerProtocolConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Enable Roughly Enough Items protocol support""")
public static boolean enabled = false;
}
@@ -1,4 +1,4 @@
package fun.bm.lophine.config.modules.function;
package fun.bm.lophine.config.modules.function.protocol;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.CommandSuggestions;
@@ -9,54 +9,64 @@ import me.earthme.luminol.enums.EnumConfigCategory;
import java.util.List;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "servux-protocol")
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "servux", directory = {"protocol"})
public class ServuxProtocolConfig implements IConfigModule {
@TransformedConfig(name = "entity-protocol", directory = {"function", "servux-protocol", "data"})
@TransformedConfig(name = "entity-protocol", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "entity-protocol", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "entity-protocol", directory = {"data"})
public static boolean entityProtocol = false;
@TransformedConfig(name = "hud-logger-protocol", directory = {"function", "servux-protocol"})
@TransformedConfig(name = "hud-logger-protocol", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "hud-logger-protocol", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "hud-logger-protocol")
public static boolean hudLoggerProtocol = false;
@TransformedConfig(name = "hud-metadata-protocol", directory = {"function", "servux-protocol"})
@TransformedConfig(name = "hud-metadata-protocol", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "hud-metadata-protocol", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "hud-metadata-protocol")
public static boolean hudMetadataProtocol = false;
@TransformedConfig(name = "hud-metadata-share-seed", directory = {"function", "servux-protocol"})
@TransformedConfig(name = "hud-metadata-share-seed", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "hud-metadata-share-seed", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "hud-metadata-share-seed")
public static boolean hudMetadataShareSeed = false;
@TransformedConfig(name = "structure-protocol", directory = {"function", "servux-protocol"})
@TransformedConfig(name = "structure-protocol", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "structure-protocol", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "structure-protocol")
public static boolean structureProtocol = false;
@TransformedConfig(name = "hud-enabled-loggers", directory = {"function", "servux-protocol"})
@TransformedConfig(name = "hud-enabled-loggers", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "hud-enabled-loggers", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "hud-enabled-loggers")
public static List<String> hudEnabledLoggers = List.of("tps", "mob_caps");
@TransformedConfig(name = "hud-update-interval", directory = {"function", "servux-protocol"})
@TransformedConfig(name = "hud-update-interval", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "hud-update-interval", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "hud-update-interval")
public static int hudUpdateInterval = 1;
@TransformedConfig(name = "litematics-enabled", directory = {"function", "servux-protocol", "litematics"})
@TransformedConfig(name = "litematics-enabled", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "litematics-enabled", directory = {"misc", "survux-protocol"})
@ConfigInfo(name = "litematics-enabled", directory = {"litematics"})
public static boolean litematicsEnabled = false;
@TransformedConfig(name = "litematics-max-nbt-size", directory = {"function", "servux-protocol", "litematics"})
@TransformedConfig(name = "litematics-max-nbt-size", directory = {"function", "survux-protocol"})
@TransformedConfig(name = "litematics-max-nbt-size", directory = {"misc", "survux-protocol"})
@CommandSuggestions(suggest = {"-1", "2097152"})
@ConfigInfo(name = "litematics-max-nbt-size", directory = {"litematics"})
public static int litematicsMaxNbtSize = 2097152;
@TransformedConfig(name = "litematics-print-max-delay-ticks", directory = {"function", "servux-protocol", "litematics"})
@TransformedConfig(name = "litematics-print-max-delay-ticks", directory = {"function", "survux-protocol"})
@CommandSuggestions(suggest = {"-1", "1200"})
@ConfigInfo(name = "litematics-print-max-delay-ticks", directory = {"litematics"}, comments = "The max delay ticks for printing litematics, -1 to disable")
@@ -0,0 +1,28 @@
package fun.bm.lophine.config.modules.function.protocol;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol;
import java.util.Set;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "syncmatica", directory = {"protocol"})
public class SyncmaticaProtocolConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Enable Syncmatica protocol support""")
public static boolean enabled = false;
@ConfigInfo(name = "useQuota", comments = """
Is there a limit on the size of projection files?""")
public static boolean useQuota = false;
@ConfigInfo(name = "quota-Limit", comments = """
Maximum Projection File Size (in bytes)""")
public static int quotaLimit = 40000000;
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> e) {
SyncmaticaProtocol.init(enabled);
}
}
@@ -0,0 +1,17 @@
package fun.bm.lophine.config.modules.function.protocol;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
import java.util.Random;
@ConfigClassInfo(category = EnumConfigCategory.FUNCTION, name = "xaero-map", directory = {"protocol"})
public class XaeroMapProtocolConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Enable Xaero World Map Protocol Support""")
public static boolean enabled = false;
@ConfigInfo(name = "xaeroMapServerID")
public static int xaeroMapServerID = new Random().nextInt();
}
@@ -8,6 +8,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.REMOVED, name = "removed_config")
public class RemovedConfig implements IConfigModule {
@TransformedConfig(name = "datapack_command_enabled", directory = {"experiment", "command"}, transform = false)
@TransformedConfig(name = "disable_end_crystal_check", directory = {"fixes", "end_crystal"}, transform = false)
@TransformedConfig(name = "disable_end_crystal_check", directory = {"misc", "end_crystal"}, transform = false)
@TransformedConfig(name = "allow_skip_cooldown", directory = {"misc", "revert_raid_changes"}, transform = false)
@@ -24,6 +24,7 @@ import com.mojang.authlib.properties.Property;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.threadedregions.RegionizedServer;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.minecraft.nbt.CompoundTag;
@@ -173,31 +174,14 @@ public class BotList {
bot.supressTrackerForLogin = true;
io.papermc.paper.threadedregions.RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
world, net.minecraft.util.Mth.floor(location.getX()) >> 4, net.minecraft.util.Mth.floor(location.getZ()) >> 4,
() -> {
world.addNewPlayer(bot);
BotJoinEvent event1 = new BotJoinEvent(bot.getBukkitEntity(), PaperAdventure.asAdventure(Component.translatable("multiplayer.player.joined", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
this.server.server.getPluginManager().callEvent(event1);
net.kyori.adventure.text.Component joinMessage = event1.joinMessage();
if (joinMessage != null && !joinMessage.equals(net.kyori.adventure.text.Component.empty())) {
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(joinMessage), false);
}
bot.renderInfo();
bot.supressTrackerForLogin = false;
bot.level().getChunkSource().chunkMap.addEntity(bot);
bot.renderData();
bot.initInventoryMenu();
botsNameByWorldUuid
.computeIfAbsent(bot.level().uuid.toString(), (k) -> new HashSet<>())
.add(bot.getBukkitEntity().getRealName());
BotList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", bot.getName().getString(), "Local", bot.getId(), bot.level().serverLevelData.getLevelName(), bot.getX(), bot.getY(), bot.getZ());
},
ca.spottedleaf.concurrentutil.util.Priority.HIGHER);
if (TickThread.isTickThreadFor(world, net.minecraft.util.Mth.floor(location.getX()) >> 4, net.minecraft.util.Mth.floor(location.getZ()) >> 4)) {
summonBot(bot, world);
} else {
RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
world, net.minecraft.util.Mth.floor(location.getX()) >> 4, net.minecraft.util.Mth.floor(location.getZ()) >> 4,
() -> summonBot(bot, world),
ca.spottedleaf.concurrentutil.util.Priority.HIGHER);
}
optional.ifPresent(nbt -> {
bot.loadAndSpawnEnderPearls(nbt);
bot.loadAndSpawnParentVehicle(nbt);
@@ -205,6 +189,42 @@ public class BotList {
return bot;
}
private ServerBot summonBot(ServerBot bot, ServerLevel world) {
world.addNewPlayer(bot);
BotJoinEvent event1 = new BotJoinEvent(bot.getBukkitEntity(), PaperAdventure.asAdventure(Component.translatable("multiplayer.player.joined", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
this.server.server.getPluginManager().callEvent(event1);
net.kyori.adventure.text.Component joinMessage = event1.joinMessage();
if (joinMessage != null && !joinMessage.equals(net.kyori.adventure.text.Component.empty())) {
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(joinMessage), false);
}
bot.renderInfo();
bot.supressTrackerForLogin = false;
bot.level().getChunkSource().chunkMap.addEntity(bot);
bot.renderData();
bot.initInventoryMenu();
botsNameByWorldUuid
.computeIfAbsent(bot.level().uuid.toString(), (k) -> new HashSet<>())
.add(bot.getBukkitEntity().getRealName());
BotList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", bot.getName().getString(), "Local", bot.getId(), bot.level().serverLevelData.getLevelName(), bot.getX(), bot.getY(), bot.getZ());
return bot;
}
/*
* return true if async
*/
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean saved, boolean async) {
if (async && !TickThread.isTickThreadFor(bot.level(), bot.getX(), bot.getZ())) {
bot.getBukkitEntity().taskScheduler.schedule((Entity unused) -> this.removeBot(bot, reason, remover, saved), null, 1L);
return true;
}
this.removeBot(bot, reason, remover, saved);
return false;
}
public boolean removeBot(@NotNull ServerBot bot, @NotNull BotRemoveEvent.RemoveReason reason, @Nullable CommandSender remover, boolean saved) {
return this.removeBot(bot, reason, remover, saved, this.dataStorage);
}
@@ -296,24 +316,36 @@ public class BotList {
AtomicInteger received = new AtomicInteger();
for (ServerBot bot : this.bots) {
bot.resume = FakeplayerConfig.canResident;
if (TickThread.isTickThreadFor(bot)) {
if (TickThread.isTickThreadFor(bot.level(), bot.getX(), bot.getZ())) {
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
} else {
finished = false;
check.getAndIncrement();
bot.getBukkitEntity().taskScheduler.schedule((Entity unused) -> {
BotList.this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
received.getAndIncrement();
if (received.get() >= check.get()) {
this.forceShutdown = true;
MinecraftServer.getServer().stopServer();
}
}, null, 1L);
this.removeBot(bot, check, received, new AtomicInteger());
}
}
return finished;
}
private void removeBot(ServerBot bot, AtomicInteger check, AtomicInteger received, AtomicInteger counter) {
bot.getBukkitEntity().taskScheduler.schedule((Entity unused) -> {
if (counter.get() >= 20) {
BotList.LOGGER.info("Try to remove bot {} located in [{}]{},{},{} too many times!", bot.getName().getString(), bot.level().serverLevelData.getLevelName(), bot.getX(), bot.getY(), bot.getZ());
}
counter.getAndIncrement();
try {
this.removeBot(bot, BotRemoveEvent.RemoveReason.INTERNAL, null, FakeplayerConfig.canResident);
received.getAndIncrement();
} catch (Exception e) {
this.removeBot(bot, check, received, counter);
}
if (received.get() >= check.get()) {
this.forceShutdown = true;
MinecraftServer.getServer().stopServer();
}
}, null, 1L);
}
public void loadBotInfo() {
if (!FakeplayerConfig.enable || !FakeplayerConfig.canResident) {
return;
@@ -154,7 +154,7 @@ public class ServerBot extends ServerPlayer {
this.notSleepTicks++;
}
if (FakeplayerConfig.regenAmount > 0.0 && getServer().checkTickCount(20)) {
if (FakeplayerConfig.regenAmount > 0.0 && this.tickCount % 20 == 0) {
float regenAmount = (float) (FakeplayerConfig.regenAmount * 20);
this.setHealth(Math.min(this.getHealth() + regenAmount, this.getMaxHealth()));
}
@@ -56,9 +56,7 @@ public class RemoveCommand extends BotSubcommand {
private static boolean removeBot(@NotNull ServerBot bot, @Nullable CommandSender sender, boolean taskQueue) {
if (taskQueue) {
bot.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
removeBotOrigin(bot, sender);
}, null, 1L);
bot.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> removeBotOrigin(bot, sender), null, 1L);
} else {
return removeBotOrigin(bot, sender);
}
@@ -21,6 +21,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.LivingEntity;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.BotList;
@@ -55,8 +56,24 @@ public class SaveCommand extends BotSubcommand {
@Override
protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
ServerBot bot = context.getCustomArgument(BotArgument.class);
CommandSender sender = context.getSender();
save(context.getCustomArgument(BotArgument.class), context.getSender());
return true;
}
private boolean save(ServerBot bot, CommandSender sender) {
return save(bot, sender, true);
}
private boolean save(ServerBot bot, CommandSender sender, boolean taskQueue) {
if (taskQueue) {
bot.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> saveOrigin(bot, sender), null, 1L);
} else {
saveOrigin(bot, sender);
}
return true;
}
private boolean saveOrigin(ServerBot bot, CommandSender sender) {
BotList botList = BotList.INSTANCE;
boolean success = botList.removeBot(bot, BotRemoveEvent.RemoveReason.COMMAND, sender, true);
@@ -0,0 +1,145 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol;
import fun.bm.lophine.config.modules.function.protocol.AppleSkinProtocolConfig;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.food.FoodData;
import net.minecraft.world.level.GameRules;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.core.Context;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@LeavesProtocol.Register(namespace = "appleskin")
public class AppleSkinProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "appleskin";
private static final ResourceLocation SATURATION_KEY = id("saturation");
private static final ResourceLocation EXHAUSTION_KEY = id("exhaustion");
private static final ResourceLocation NATURAL_REGENERATION_KEY = id("natural_regeneration");
private static final float MINIMUM_EXHAUSTION_CHANGE_THRESHOLD = 0.01F;
private static final Map<ServerPlayer, Float> previousSaturationLevels = new ConcurrentHashMap<>();
private static final Map<ServerPlayer, Float> previousExhaustionLevels = new ConcurrentHashMap<>();
private static final Map<ServerPlayer, Boolean> previousNaturalRegeneration = new ConcurrentHashMap<>();
private static final Map<UUID, Set<String>> subscribedChannels = new ConcurrentHashMap<>();
@Contract("_ -> new")
public static ResourceLocation id(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
}
@ProtocolHandler.PlayerJoin
public static void onPlayerLoggedIn(@NotNull ServerPlayer player) {
resetPlayerData(player);
}
@ProtocolHandler.PlayerLeave
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
subscribedChannels.remove(player.getUUID());
resetPlayerData(player);
}
@ProtocolHandler.MinecraftRegister(onlyNamespace = true)
public static void onPlayerSubscribed(@NotNull Context context, ResourceLocation id) {
subscribedChannels.computeIfAbsent(context.profile().getId(), k -> new HashSet<>()).add(id.getPath());
}
@ProtocolHandler.Ticker
public static void tick() {
for (Map.Entry<UUID, Set<String>> entry : subscribedChannels.entrySet()) {
ServerPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(entry.getKey());
if (player == null) {
continue;
}
FoodData data = player.getFoodData();
for (String channel : entry.getValue()) {
switch (channel) {
case "saturation" -> {
float saturation = data.getSaturationLevel();
Float previousSaturation = previousSaturationLevels.get(player);
if (previousSaturation == null || saturation != previousSaturation) {
ProtocolUtils.sendBytebufPacket(player, SATURATION_KEY, buf -> buf.writeFloat(saturation));
previousSaturationLevels.put(player, saturation);
}
}
case "exhaustion" -> {
float exhaustion = data.exhaustionLevel;
Float previousExhaustion = previousExhaustionLevels.get(player);
if (previousExhaustion == null || Math.abs(exhaustion - previousExhaustion) >= MINIMUM_EXHAUSTION_CHANGE_THRESHOLD) {
ProtocolUtils.sendBytebufPacket(player, EXHAUSTION_KEY, buf -> buf.writeFloat(exhaustion));
previousExhaustionLevels.put(player, exhaustion);
}
}
case "natural_regeneration" -> {
boolean regeneration = player.level().getGameRules().getBoolean(GameRules.RULE_NATURAL_REGENERATION);
Boolean previousRegeneration = previousNaturalRegeneration.get(player);
if (previousRegeneration == null || regeneration != previousRegeneration) {
ProtocolUtils.sendBytebufPacket(player, NATURAL_REGENERATION_KEY, buf -> buf.writeBoolean(regeneration));
previousNaturalRegeneration.put(player, regeneration);
}
}
}
}
}
}
@ProtocolHandler.ReloadServer
public static void onServerReload() {
disableAllPlayer();
}
public static void disableAllPlayer() {
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
onPlayerLoggedOut(player);
}
}
private static void resetPlayerData(@NotNull ServerPlayer player) {
previousExhaustionLevels.remove(player);
previousSaturationLevels.remove(player);
previousNaturalRegeneration.remove(player);
}
@Override
public int tickerInterval(String tickerID) {
return AppleSkinProtocolConfig.syncTickInterval;
}
@Override
public boolean isActive() {
return AppleSkinProtocolConfig.enabled;
}
}
@@ -0,0 +1,246 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol;
import fun.bm.lophine.config.modules.function.protocol.BBORProtocolConfig;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@LeavesProtocol.Register(namespace = "bbor")
public class BBORProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "bbor";
// send
private static final ResourceLocation INITIALIZE_CLIENT = id("initialize");
private static final ResourceLocation ADD_BOUNDING_BOX = id("add_bounding_box_v2");
private static final ResourceLocation STRUCTURE_LIST_SYNC = id("structure_list_sync_v1");
// call
private static final Map<Integer, ServerPlayer> players = new ConcurrentHashMap<>();
private static final Map<Integer, Set<BBoundingBox>> playerBoundingBoxesCache = new ConcurrentHashMap<>();
private static final Map<ResourceLocation, Map<BBoundingBox, Set<BBoundingBox>>> dimensionCache = new ConcurrentHashMap<>();
private static boolean initialized = false;
@Contract("_ -> new")
public static ResourceLocation id(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
}
@ProtocolHandler.Ticker
public static void tick() {
for (var playerEntry : players.entrySet()) {
sendBoundingToPlayer(playerEntry.getKey(), playerEntry.getValue());
}
}
@ProtocolHandler.ReloadServer
public static void onServerReload() {
if (BBORProtocolConfig.enabled) {
initAllPlayer();
} else {
loggedOutAllPlayer();
}
}
@ProtocolHandler.PlayerJoin
public static void onPlayerLoggedIn(@NotNull ServerPlayer player) {
ServerLevel overworld = MinecraftServer.getServer().overworld();
ProtocolUtils.sendBytebufPacket(player, INITIALIZE_CLIENT, buf -> {
buf.writeLong(overworld.getSeed());
buf.writeInt(overworld.levelData.getSpawnPos().getX());
buf.writeInt(overworld.levelData.getSpawnPos().getZ());
});
sendStructureList(player);
}
@ProtocolHandler.PlayerLeave
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
players.remove(player.getId());
playerBoundingBoxesCache.remove(player.getId());
}
@ProtocolHandler.BytebufReceiver(key = "subscribe")
public static void onPlayerSubscribed(@NotNull ServerPlayer player, FriendlyByteBuf buf) {
players.put(player.getId(), player);
sendBoundingToPlayer(player.getId(), player);
}
@ProtocolHandler.ReloadDataPack
public static void onDataPackReload() {
players.values().forEach(BBORProtocol::sendStructureList);
}
public static void onChunkLoaded(@NotNull LevelChunk chunk) {
Map<String, StructureStart> structures = new HashMap<>();
final Registry<Structure> structureFeatureRegistry = chunk.getLevel().registryAccess().lookupOrThrow(Registries.STRUCTURE);
for (var es : chunk.getAllStarts().entrySet()) {
final var optional = structureFeatureRegistry.getResourceKey(es.getKey());
optional.ifPresent(key -> structures.put(key.location().toString(), es.getValue()));
}
if (!structures.isEmpty()) {
onStructuresLoaded(chunk.getLevel().dimension().location(), structures);
}
}
public static void onStructuresLoaded(@NotNull ResourceLocation dimensionID, @NotNull Map<String, StructureStart> structures) {
Map<BBoundingBox, Set<BBoundingBox>> cache = getOrCreateCache(dimensionID);
for (var entry : structures.entrySet()) {
StructureStart structureStart = entry.getValue();
if (structureStart == null || !structureStart.isValid()) {
continue;
}
String type = "structure:" + entry.getKey();
BoundingBox bb = structureStart.getBoundingBox();
BBoundingBox boundingBox = buildStructure(bb, type);
if (cache.containsKey(boundingBox)) {
continue;
}
Set<BBoundingBox> structureBoundingBoxes = new HashSet<>();
if (!structureStart.getPieces().isEmpty()) {
for (StructurePiece structureComponent : structureStart.getPieces()) {
structureBoundingBoxes.add(buildStructure(structureComponent.getBoundingBox(), type));
}
cache.put(boundingBox, structureBoundingBoxes);
}
}
}
private static @NotNull BBoundingBox buildStructure(@NotNull BoundingBox bb, String type) {
BlockPos min = new BlockPos(bb.minX(), bb.minY(), bb.minZ());
BlockPos max = new BlockPos(bb.maxX(), bb.maxY(), bb.maxZ());
return new BBoundingBox(type, min, max);
}
private static void sendStructureList(@NotNull ServerPlayer player) {
final Registry<Structure> structureRegistry = MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.STRUCTURE);
final Set<String> structureIds = structureRegistry.entrySet().stream()
.map(e -> e.getKey().location().toString()).collect(Collectors.toSet());
ProtocolUtils.sendBytebufPacket(player, STRUCTURE_LIST_SYNC, buf -> {
buf.writeVarInt(structureIds.size());
structureIds.forEach(buf::writeUtf);
});
}
private static void sendBoundingToPlayer(int id, ServerPlayer player) {
for (var entry : dimensionCache.entrySet()) {
if (entry.getValue() == null) {
return;
}
Set<BBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(id, k -> new HashSet<>());
Map<BBoundingBox, Set<BBoundingBox>> boundingBoxMap = entry.getValue();
for (BBoundingBox key : boundingBoxMap.keySet()) {
if (playerBoundingBoxes.contains(key)) {
continue;
}
Set<BBoundingBox> boundingBoxes = boundingBoxMap.get(key);
ProtocolUtils.sendBytebufPacket(player, ADD_BOUNDING_BOX, buf -> {
buf.writeResourceLocation(entry.getKey());
key.serialize(buf);
if (boundingBoxes != null && boundingBoxes.size() > 1) {
for (BBoundingBox box : boundingBoxes) {
box.serialize(buf);
}
}
});
playerBoundingBoxes.add(key);
}
}
}
public static void initAllPlayer() {
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
onPlayerLoggedIn(player);
}
initialized = true;
}
public static void loggedOutAllPlayer() {
players.clear();
playerBoundingBoxesCache.clear();
for (var cache : dimensionCache.values()) {
cache.clear();
}
dimensionCache.clear();
}
private static Map<BBoundingBox, Set<BBoundingBox>> getOrCreateCache(ResourceLocation dimensionId) {
return dimensionCache.computeIfAbsent(dimensionId, dt -> new ConcurrentHashMap<>());
}
@Override
public boolean isActive() {
boolean active = BBORProtocolConfig.enabled;
if (!active && initialized) {
initialized = false;
loggedOutAllPlayer();
}
return active;
}
private record BBoundingBox(String type, BlockPos min, BlockPos max) {
private static int combineHashCodes(int @NotNull ... hashCodes) {
final int prime = 31;
int result = 0;
for (int hashCode : hashCodes) {
result = prime * result + hashCode;
}
return result;
}
public void serialize(@NotNull FriendlyByteBuf buf) {
buf.writeChar('S');
buf.writeInt(type.hashCode());
buf.writeVarInt(min.getX()).writeVarInt(min.getY()).writeVarInt(min.getZ());
buf.writeVarInt(max.getX()).writeVarInt(max.getY()).writeVarInt(max.getZ());
}
@Override
public int hashCode() {
return combineHashCodes(min.hashCode(), max.hashCode());
}
}
}
@@ -0,0 +1,64 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol;
import fun.bm.lophine.config.modules.function.protocol.XaeroMapProtocolConfig;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
@LeavesProtocol.Register(namespace = "xaerominimap_or_xaeroworldmap_i_dont_care")
public class XaeroMapProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID_MINI = "xaerominimap";
public static final String PROTOCOL_ID_WORLD = "xaeroworldmap";
private static final ResourceLocation MINIMAP_KEY = idMini("main");
private static final ResourceLocation WORLDMAP_KEY = idWorld("main");
@Contract("_ -> new")
public static ResourceLocation idMini(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID_MINI, path);
}
@Contract("_ -> new")
public static ResourceLocation idWorld(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID_WORLD, path);
}
public static void onSendWorldInfo(@NotNull ServerPlayer player) {
if (XaeroMapProtocolConfig.enabled) {
ProtocolUtils.sendBytebufPacket(player, MINIMAP_KEY, buf -> {
buf.writeByte(0);
buf.writeInt(XaeroMapProtocolConfig.xaeroMapServerID);
});
ProtocolUtils.sendBytebufPacket(player, WORLDMAP_KEY, buf -> {
buf.writeByte(0);
buf.writeInt(XaeroMapProtocolConfig.xaeroMapServerID);
});
}
}
@Override
public boolean isActive() {
return XaeroMapProtocolConfig.enabled;
}
}
@@ -24,7 +24,6 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import org.leavesmc.leaves.protocol.core.invoker.*;
import org.slf4j.Logger;
@@ -66,6 +65,8 @@ public class LeavesProtocolManager {
private static final List<EmptyInvokerHolder<ProtocolHandler.ReloadServer>> RELOAD_SERVER = new ArrayList<>();
private static final List<EmptyInvokerHolder<ProtocolHandler.ReloadDataPack>> RELOAD_DATAPACK = new ArrayList<>();
private static long lastAcceptTime = 0;
@SuppressWarnings("unchecked")
public static void init() {
for (Class<?> clazz : getClasses("org.leavesmc.leaves.protocol")) {
@@ -253,8 +254,11 @@ public class LeavesProtocolManager {
}
public static void handleTick() {
long currentTime = System.currentTimeMillis() / 50;
if (currentTime == lastAcceptTime) return;
lastAcceptTime = currentTime;
for (var tickerInfo : TICKERS) {
if (MinecraftServer.getServer().checkTickCount(tickerInfo.owner().tickerInterval(tickerInfo.handler().tickerId()))) {
if (currentTime % tickerInfo.owner().tickerInterval(tickerInfo.handler().tickerId()) == 0) {
tickerInfo.invoke();
}
}
@@ -0,0 +1,275 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade;
import ca.spottedleaf.moonrise.common.util.TickThread;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.config.modules.function.protocol.JadeProtocolConfig;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.AgeableMob;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.animal.Chicken;
import net.minecraft.world.entity.animal.allay.Allay;
import net.minecraft.world.entity.animal.armadillo.Armadillo;
import net.minecraft.world.entity.animal.frog.Tadpole;
import net.minecraft.world.entity.animal.sniffer.Sniffer;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.CampfireBlock;
import net.minecraft.world.level.block.entity.*;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.payload.*;
import org.leavesmc.leaves.protocol.jade.provider.*;
import org.leavesmc.leaves.protocol.jade.provider.block.*;
import org.leavesmc.leaves.protocol.jade.provider.entity.*;
import org.leavesmc.leaves.protocol.jade.util.*;
import org.leavesmc.leaves.protocol.servux.litematics.utils.NbtUtils;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@LeavesProtocol.Register(namespace = "jade")
public class JadeProtocol implements LeavesProtocol {
private static final Logger LOGGER = LogUtils.getLogger();
public static final String PROTOCOL_ID = "jade";
public static final String PROTOCOL_VERSION = "8";
public static final HierarchyLookup<IServerDataProvider<EntityAccessor>> entityDataProviders = new HierarchyLookup<>(Entity.class);
public static final PairHierarchyLookup<IServerDataProvider<BlockAccessor>> blockDataProviders = new PairHierarchyLookup<>(new HierarchyLookup<>(Block.class), new HierarchyLookup<>(BlockEntity.class));
public static final WrappedHierarchyLookup<IServerExtensionProvider<ItemStack>> itemStorageProviders = WrappedHierarchyLookup.forAccessor();
private static final Set<ServerPlayer> enabledPlayers = new HashSet<>();
public static PriorityStore<ResourceLocation, IJadeProvider> priorities;
private static List<Block> shearableBlocks = null;
@Contract("_ -> new")
public static ResourceLocation id(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
}
@Contract("_ -> new")
public static @NotNull ResourceLocation mc_id(String path) {
return ResourceLocation.withDefaultNamespace(path);
}
@ProtocolHandler.Init
public static void init() {
priorities = new PriorityStore<>(IJadeProvider::getDefaultPriority, IJadeProvider::getUid);
// core plugin
blockDataProviders.register(BlockEntity.class, BlockNameProvider.INSTANCE);
// universal plugin
entityDataProviders.register(Entity.class, ItemStorageProvider.getEntity());
blockDataProviders.register(Block.class, ItemStorageProvider.getBlock());
itemStorageProviders.register(Object.class, ItemStorageExtensionProvider.INSTANCE);
itemStorageProviders.register(Block.class, ItemStorageExtensionProvider.INSTANCE);
// vanilla plugin
entityDataProviders.register(Entity.class, AnimalOwnerProvider.INSTANCE);
entityDataProviders.register(LivingEntity.class, StatusEffectsProvider.INSTANCE);
entityDataProviders.register(AgeableMob.class, MobGrowthProvider.INSTANCE);
entityDataProviders.register(Tadpole.class, MobGrowthProvider.INSTANCE);
entityDataProviders.register(Animal.class, MobBreedingProvider.INSTANCE);
entityDataProviders.register(Allay.class, MobBreedingProvider.INSTANCE);
entityDataProviders.register(Mob.class, PetArmorProvider.INSTANCE);
entityDataProviders.register(Chicken.class, NextEntityDropProvider.INSTANCE);
entityDataProviders.register(Armadillo.class, NextEntityDropProvider.INSTANCE);
entityDataProviders.register(Sniffer.class, NextEntityDropProvider.INSTANCE);
entityDataProviders.register(ZombieVillager.class, ZombieVillagerProvider.INSTANCE);
blockDataProviders.register(BrewingStandBlockEntity.class, BrewingStandProvider.INSTANCE);
blockDataProviders.register(BeehiveBlockEntity.class, BeehiveProvider.INSTANCE);
blockDataProviders.register(CommandBlockEntity.class, CommandBlockProvider.INSTANCE);
blockDataProviders.register(JukeboxBlockEntity.class, JukeboxProvider.INSTANCE);
blockDataProviders.register(LecternBlockEntity.class, LecternProvider.INSTANCE);
blockDataProviders.register(ComparatorBlockEntity.class, RedstoneProvider.INSTANCE);
blockDataProviders.register(HopperBlockEntity.class, HopperLockProvider.INSTANCE);
blockDataProviders.register(CalibratedSculkSensorBlockEntity.class, RedstoneProvider.INSTANCE);
blockDataProviders.register(AbstractFurnaceBlockEntity.class, FurnaceProvider.INSTANCE);
blockDataProviders.register(ChiseledBookShelfBlockEntity.class, ChiseledBookshelfProvider.INSTANCE);
blockDataProviders.register(TrialSpawnerBlockEntity.class, MobSpawnerCooldownProvider.INSTANCE);
itemStorageProviders.register(CampfireBlock.class, CampfireProvider.INSTANCE);
blockDataProviders.idMapped();
entityDataProviders.idMapped();
blockDataProviders.loadComplete(priorities);
entityDataProviders.loadComplete(priorities);
itemStorageProviders.loadComplete(priorities);
rebuildShearableBlocks();
}
@ProtocolHandler.PayloadReceiver(payload = ClientHandshakePayload.class)
public static void clientHandshake(ServerPlayer player, ClientHandshakePayload payload) {
if (!payload.protocolVersion().equals(PROTOCOL_VERSION)) {
player.sendSystemMessage(Component.literal("You are using a different version of Jade than the server. Please update Jade or report to the server operator").withColor(0xff0000));
return;
}
ProtocolUtils.sendPayloadPacket(player, new ServerHandshakePayload(Collections.emptyMap(), shearableBlocks, blockDataProviders.mappedIds(), entityDataProviders.mappedIds()));
synchronized (enabledPlayers) {
enabledPlayers.add(player);
}
}
@ProtocolHandler.PlayerLeave
public static void onPlayerLeave(ServerPlayer player) {
synchronized (enabledPlayers) {
enabledPlayers.remove(player);
}
}
@ProtocolHandler.PayloadReceiver(payload = RequestEntityPayload.class)
public static void requestEntityData(ServerPlayer player, RequestEntityPayload payload) {
player.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
EntityAccessor accessor = payload.data().unpack(player);
if (accessor == null) {
return;
}
Entity entity = accessor.getEntity();
double maxDistance = Mth.square(player.entityInteractionRange() + 21);
if (entity == null || player.distanceToSqr(entity) > maxDistance) {
return;
}
List<IServerDataProvider<EntityAccessor>> providers = entityDataProviders.get(entity);
if (providers.isEmpty()) {
return;
}
CompoundTag tag = new CompoundTag();
for (IServerDataProvider<EntityAccessor> provider : providers) {
if (!payload.dataProviders().contains(provider)) {
continue;
}
try {
provider.appendServerData(tag, accessor);
} catch (Exception e) {
LOGGER.warn("Error while saving data for entity " + entity);
}
}
tag.putInt("EntityId", entity.getId());
ProtocolUtils.sendPayloadPacket(player, new ReceiveDataPayload(tag));
}, null, 1L);
}
@ProtocolHandler.PayloadReceiver(payload = RequestBlockPayload.class)
public static void requestBlockData(ServerPlayer player, RequestBlockPayload payload) {
ServerLevel level = player.level();
player.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
BlockAccessor accessor = payload.data().unpack(player);
if (accessor == null) {
return;
}
BlockPos pos = accessor.getPosition();
Block block = accessor.getBlock();
BlockEntity blockEntity = TickThread.isTickThreadFor(level, pos) ? accessor.getBlockEntity() : null;
double maxDistance = Mth.square(player.blockInteractionRange() + 21);
if (pos.distSqr(player.blockPosition()) > maxDistance || !accessor.getLevel().isLoaded(pos)) {
return;
}
List<IServerDataProvider<BlockAccessor>> providers;
if (blockEntity != null) {
providers = blockDataProviders.getMerged(block, blockEntity);
} else {
providers = blockDataProviders.first.get(block);
}
if (providers.isEmpty()) {
return;
}
CompoundTag tag = new CompoundTag();
for (IServerDataProvider<BlockAccessor> provider : providers) {
if (!payload.dataProviders().contains(provider)) {
continue;
}
try {
provider.appendServerData(tag, accessor);
} catch (Exception e) {
LOGGER.warn("Error while saving data for block " + accessor.getBlockState());
}
}
NbtUtils.writeBlockPosToTag(pos, tag);
tag.putString("BlockId", BuiltInRegistries.BLOCK.getKey(block).toString());
ProtocolUtils.sendPayloadPacket(player, new ReceiveDataPayload(tag));
}, null, 1L);
}
@ProtocolHandler.ReloadServer
public static void onServerReload() {
rebuildShearableBlocks();
synchronized (enabledPlayers) {
for (ServerPlayer player : enabledPlayers) {
ProtocolUtils.sendPayloadPacket(player, new ServerHandshakePayload(Collections.emptyMap(), shearableBlocks, blockDataProviders.mappedIds(), entityDataProviders.mappedIds()));
}
}
}
private static void rebuildShearableBlocks() {
try {
shearableBlocks = Collections.unmodifiableList(LootTableMineableCollector.execute(
MinecraftServer.getServer().reloadableRegistries().lookup().lookupOrThrow(Registries.LOOT_TABLE),
Items.SHEARS.getDefaultInstance()
));
} catch (Throwable ignore) {
shearableBlocks = List.of();
LOGGER.warn("Failed to collect shearable blocks");
}
}
@Override
public boolean isActive() {
return JadeProtocolConfig.enabled;
}
}
@@ -0,0 +1,39 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.accessor;
import net.minecraft.nbt.Tag;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamEncoder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.HitResult;
import org.jetbrains.annotations.Nullable;
public interface Accessor<T extends HitResult> {
ServerLevel getLevel();
Player getPlayer();
<D> Tag encodeAsNbt(StreamEncoder<RegistryFriendlyByteBuf, D> codec, D value);
T getHitResult();
@Nullable
Object getTarget();
}
@@ -0,0 +1,77 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.accessor;
import io.netty.buffer.Unpooled;
import net.minecraft.nbt.ByteArrayTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamEncoder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.HitResult;
import org.apache.commons.lang3.ArrayUtils;
import java.util.function.Supplier;
public abstract class AccessorImpl<T extends HitResult> implements Accessor<T> {
private final ServerLevel level;
private final Player player;
private final Supplier<T> hit;
protected boolean verify;
private RegistryFriendlyByteBuf buffer;
public AccessorImpl(ServerLevel level, Player player, Supplier<T> hit) {
this.level = level;
this.player = player;
this.hit = hit;
}
@Override
public ServerLevel getLevel() {
return level;
}
@Override
public Player getPlayer() {
return player;
}
private RegistryFriendlyByteBuf buffer() {
if (buffer == null) {
buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), level.registryAccess());
}
buffer.clear();
return buffer;
}
@Override
public <D> Tag encodeAsNbt(StreamEncoder<RegistryFriendlyByteBuf, D> streamCodec, D value) {
RegistryFriendlyByteBuf buffer = buffer();
streamCodec.encode(buffer, value);
ByteArrayTag tag = new ByteArrayTag(ArrayUtils.subarray(buffer.array(), 0, buffer.readableBytes()));
buffer.clear();
return tag;
}
@Override
public T getHitResult() {
return hit.get();
}
}
@@ -0,0 +1,61 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.accessor;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.ApiStatus;
import java.util.function.Supplier;
public interface BlockAccessor extends Accessor<BlockHitResult> {
Block getBlock();
BlockState getBlockState();
BlockEntity getBlockEntity();
BlockPos getPosition();
@ApiStatus.NonExtendable
interface Builder {
Builder level(ServerLevel level);
Builder player(Player player);
Builder hit(BlockHitResult hit);
Builder blockState(BlockState state);
default Builder blockEntity(BlockEntity blockEntity) {
return blockEntity(() -> blockEntity);
}
Builder blockEntity(Supplier<BlockEntity> blockEntity);
Builder from(BlockAccessor accessor);
BlockAccessor build();
}
}
@@ -0,0 +1,160 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.accessor;
import com.google.common.base.Suppliers;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier;
/**
* Class to get information of block target and context.
*/
public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements BlockAccessor {
private final BlockState blockState;
@Nullable
private final Supplier<BlockEntity> blockEntity;
private BlockAccessorImpl(Builder builder) {
super(builder.level, builder.player, Suppliers.ofInstance(builder.hit));
blockState = builder.blockState;
blockEntity = builder.blockEntity;
}
@Override
public Block getBlock() {
return getBlockState().getBlock();
}
@Override
public BlockState getBlockState() {
return blockState;
}
@Override
public BlockEntity getBlockEntity() {
return blockEntity == null ? null : blockEntity.get();
}
@Override
public BlockPos getPosition() {
return getHitResult().getBlockPos();
}
@Nullable
@Override
public Object getTarget() {
return getBlockEntity();
}
public static class Builder implements BlockAccessor.Builder {
private ServerLevel level;
private Player player;
private BlockHitResult hit;
private BlockState blockState = Blocks.AIR.defaultBlockState();
private Supplier<BlockEntity> blockEntity;
@Override
public Builder level(ServerLevel level) {
this.level = level;
return this;
}
@Override
public Builder player(Player player) {
this.player = player;
return this;
}
@Override
public Builder hit(BlockHitResult hit) {
this.hit = hit;
return this;
}
@Override
public Builder blockState(BlockState blockState) {
this.blockState = blockState;
return this;
}
@Override
public Builder blockEntity(Supplier<BlockEntity> blockEntity) {
this.blockEntity = blockEntity;
return this;
}
@Override
public Builder from(BlockAccessor accessor) {
level = accessor.getLevel();
player = accessor.getPlayer();
hit = accessor.getHitResult();
blockEntity = accessor::getBlockEntity;
blockState = accessor.getBlockState();
return this;
}
@Override
public BlockAccessor build() {
return new BlockAccessorImpl(this);
}
}
public record SyncData(boolean showDetails, BlockHitResult hit, BlockState blockState, ItemStack fakeBlock) {
public static final StreamCodec<RegistryFriendlyByteBuf, SyncData> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL,
SyncData::showDetails,
StreamCodec.of(FriendlyByteBuf::writeBlockHitResult, FriendlyByteBuf::readBlockHitResult),
SyncData::hit,
ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY),
SyncData::blockState,
ItemStack.OPTIONAL_STREAM_CODEC,
SyncData::fakeBlock,
SyncData::new
);
public BlockAccessor unpack(ServerPlayer player) {
Supplier<BlockEntity> blockEntity = null;
if (blockState.hasBlockEntity()) {
blockEntity = Suppliers.memoize(() -> player.level().getBlockEntity(hit.getBlockPos()));
}
return new Builder()
.level(player.level())
.player(player)
.hit(hit)
.blockState(blockState)
.blockEntity(blockEntity)
.build();
}
}
}
@@ -0,0 +1,59 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.accessor;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.EntityHitResult;
import org.jetbrains.annotations.ApiStatus;
import java.util.function.Supplier;
public interface EntityAccessor extends Accessor<EntityHitResult> {
Entity getEntity();
/**
* For part entity like ender dragon's, getEntity() will return the parent entity.
*/
Entity getRawEntity();
@ApiStatus.NonExtendable
interface Builder {
Builder level(ServerLevel level);
Builder player(Player player);
default Builder hit(EntityHitResult hit) {
return hit(() -> hit);
}
Builder hit(Supplier<EntityHitResult> hit);
default Builder entity(Entity entity) {
return entity(() -> entity);
}
Builder entity(Supplier<Entity> entity);
Builder from(EntityAccessor accessor);
EntityAccessor build();
}
}
@@ -0,0 +1,129 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.accessor;
import com.google.common.base.Suppliers;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.util.CommonUtil;
import java.util.function.Supplier;
public class EntityAccessorImpl extends AccessorImpl<EntityHitResult> implements EntityAccessor {
private final Supplier<Entity> entity;
public EntityAccessorImpl(Builder builder) {
super(builder.level, builder.player, builder.hit);
entity = builder.entity;
}
@Override
public Entity getEntity() {
return CommonUtil.wrapPartEntityParent(getRawEntity());
}
@Override
public Entity getRawEntity() {
return entity.get();
}
@NotNull
@Override
public Object getTarget() {
return getEntity();
}
public static class Builder implements EntityAccessor.Builder {
private ServerLevel level;
private Player player;
private Supplier<EntityHitResult> hit;
private Supplier<Entity> entity;
@Override
public Builder level(ServerLevel level) {
this.level = level;
return this;
}
@Override
public Builder player(Player player) {
this.player = player;
return this;
}
@Override
public Builder hit(Supplier<EntityHitResult> hit) {
this.hit = hit;
return this;
}
@Override
public Builder entity(Supplier<Entity> entity) {
this.entity = entity;
return this;
}
@Override
public Builder from(EntityAccessor accessor) {
level = accessor.getLevel();
player = accessor.getPlayer();
hit = accessor::getHitResult;
entity = accessor::getEntity;
return this;
}
@Override
public EntityAccessor build() {
return new EntityAccessorImpl(this);
}
}
public record SyncData(boolean showDetails, int id, int partIndex, Vec3 hitVec) {
public static final StreamCodec<RegistryFriendlyByteBuf, SyncData> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL,
SyncData::showDetails,
ByteBufCodecs.VAR_INT,
SyncData::id,
ByteBufCodecs.VAR_INT,
SyncData::partIndex,
ByteBufCodecs.VECTOR3F.map(Vec3::new, Vec3::toVector3f),
SyncData::hitVec,
SyncData::new
);
public EntityAccessor unpack(ServerPlayer player) {
Supplier<Entity> entity = Suppliers.memoize(() -> CommonUtil.getPartEntity(player.level().getEntity(id), partIndex));
return new EntityAccessorImpl.Builder()
.level(player.level())
.player(player)
.entity(entity)
.hit(Suppliers.memoize(() -> new EntityHitResult(entity.get(), hitVec)))
.build();
}
}
}
@@ -0,0 +1,36 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.payload;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
public record ClientHandshakePayload(String protocolVersion) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_CLIENT_HANDSHAKE = JadeProtocol.id("client_handshake");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, ClientHandshakePayload> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8, ClientHandshakePayload::protocolVersion, ClientHandshakePayload::new
);
}
@@ -0,0 +1,37 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.payload;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
public record ReceiveDataPayload(CompoundTag tag) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_RECEIVE_DATA = JadeProtocol.id("receive_data");
@Codec
private static final StreamCodec<FriendlyByteBuf, ReceiveDataPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.COMPOUND_TAG, ReceiveDataPayload::tag, ReceiveDataPayload::new
);
}
@@ -0,0 +1,53 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.payload;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessorImpl;
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
import java.util.List;
import java.util.Objects;
import static org.leavesmc.leaves.protocol.jade.JadeProtocol.blockDataProviders;
public record RequestBlockPayload(BlockAccessorImpl.SyncData data,
List<@Nullable IServerDataProvider<BlockAccessor>> dataProviders) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_REQUEST_BLOCK = JadeProtocol.id("request_block");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, RequestBlockPayload> CODEC = StreamCodec.composite(
BlockAccessorImpl.SyncData.STREAM_CODEC,
RequestBlockPayload::data,
ByteBufCodecs.<ByteBuf, IServerDataProvider<BlockAccessor>>list()
.apply(ByteBufCodecs.idMapper(
$ -> Objects.requireNonNull(blockDataProviders.idMapper()).byId($),
$ -> Objects.requireNonNull(blockDataProviders.idMapper()).getIdOrThrow($))),
RequestBlockPayload::dataProviders,
RequestBlockPayload::new);
}
@@ -0,0 +1,54 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.payload;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessorImpl;
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
import java.util.List;
import java.util.Objects;
import static org.leavesmc.leaves.protocol.jade.JadeProtocol.entityDataProviders;
public record RequestEntityPayload(EntityAccessorImpl.SyncData data,
List<@Nullable IServerDataProvider<EntityAccessor>> dataProviders) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_REQUEST_ENTITY = JadeProtocol.id("request_entity");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, RequestEntityPayload> CODEC = StreamCodec.composite(
EntityAccessorImpl.SyncData.STREAM_CODEC,
RequestEntityPayload::data,
ByteBufCodecs.<ByteBuf, IServerDataProvider<EntityAccessor>>list()
.apply(ByteBufCodecs.idMapper(
$ -> Objects.requireNonNull(entityDataProviders.idMapper()).byId($),
$ -> Objects.requireNonNull(entityDataProviders.idMapper()).getIdOrThrow($)
)),
RequestEntityPayload::dataProviders,
RequestEntityPayload::new);
}
@@ -0,0 +1,59 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.payload;
import com.google.common.collect.Maps;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import java.util.List;
import java.util.Map;
import static org.leavesmc.leaves.protocol.jade.util.JadeCodec.PRIMITIVE_STREAM_CODEC;
public record ServerHandshakePayload(
Map<ResourceLocation, Object> serverConfig,
List<Block> shearableBlocks,
List<ResourceLocation> blockProviderIds,
List<ResourceLocation> entityProviderIds
) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_SERVER_HANDSHAKE = JadeProtocol.id("server_handshake");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, ServerHandshakePayload> CODEC = StreamCodec.composite(
ByteBufCodecs.map(Maps::newHashMapWithExpectedSize, ResourceLocation.STREAM_CODEC, PRIMITIVE_STREAM_CODEC),
ServerHandshakePayload::serverConfig,
ByteBufCodecs.registry(Registries.BLOCK).apply(ByteBufCodecs.list()),
ServerHandshakePayload::shearableBlocks,
ByteBufCodecs.<ByteBuf, ResourceLocation>list().apply(ResourceLocation.STREAM_CODEC),
ServerHandshakePayload::blockProviderIds,
ByteBufCodecs.<ByteBuf, ResourceLocation>list().apply(ResourceLocation.STREAM_CODEC),
ServerHandshakePayload::entityProviderIds,
ServerHandshakePayload::new
);
}
@@ -0,0 +1,29 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider;
import net.minecraft.resources.ResourceLocation;
public interface IJadeProvider {
ResourceLocation getUid();
default int getDefaultPriority() {
return 0;
}
}
@@ -0,0 +1,25 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider;
import net.minecraft.nbt.CompoundTag;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
public interface IServerDataProvider<T extends Accessor<?>> extends IJadeProvider {
void appendServerData(CompoundTag data, T accessor);
}
@@ -0,0 +1,27 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
import java.util.List;
public interface IServerExtensionProvider<T> extends IJadeProvider {
List<ViewGroup<T>> getGroups(Accessor<?> request);
}
@@ -0,0 +1,165 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.mojang.logging.LogUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.Container;
import net.minecraft.world.LockCode;
import net.minecraft.world.RandomizableContainer;
import net.minecraft.world.WorldlyContainerHolder;
import net.minecraft.world.entity.animal.horse.AbstractHorse;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.ContainerEntity;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.util.ItemCollector;
import org.leavesmc.leaves.protocol.jade.util.ItemIterator;
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
import org.slf4j.Logger;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public enum ItemStorageExtensionProvider implements IServerExtensionProvider<ItemStack> {
INSTANCE;
public static final Cache<Object, ItemCollector<?>> targetCache = CacheBuilder.newBuilder().weakKeys().expireAfterAccess(60, TimeUnit.SECONDS).build();
private static final ResourceLocation UNIVERSAL_ITEM_STORAGE = JadeProtocol.mc_id("item_storage.default");
private static final Logger LOGGER = LogUtils.getLogger();
public static ItemCollector<?> createItemCollector(Accessor<?> request) {
if (request.getTarget() instanceof AbstractHorse) {
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(o -> {
if (o instanceof AbstractHorse horse) {
return horse.inventory;
}
return null;
}, 2));
}
// TODO BlockEntity like fabric's ItemStorage
final Container container = findContainer(request);
if (container != null) {
if (container instanceof ChestBlockEntity) {
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(o -> {
if (o instanceof ChestBlockEntity blockEntity) {
if (blockEntity.getBlockState().getBlock() instanceof ChestBlock chestBlock) {
Container compound = null;
if (blockEntity.getLevel() != null) {
compound = ChestBlock.getContainer(
chestBlock, blockEntity.getBlockState(),
blockEntity.getLevel(), blockEntity.getBlockPos(),
true // Bypass lock check
);
}
if (compound != null) {
return compound;
}
}
return blockEntity;
}
return null;
}, 0));
}
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(0));
}
return ItemCollector.EMPTY;
}
public static @Nullable Container findContainer(@NotNull Accessor<?> accessor) {
Object target = accessor.getTarget();
if (target == null && accessor instanceof BlockAccessor blockAccessor &&
blockAccessor.getBlock() instanceof WorldlyContainerHolder holder) {
return holder.getContainer(blockAccessor.getBlockState(), accessor.getLevel(), blockAccessor.getPosition());
} else if (target instanceof Container container) {
return container;
}
return null;
}
@Override
public List<ViewGroup<ItemStack>> getGroups(Accessor<?> request) {
Object target = request.getTarget();
switch (target) {
case null -> {
return createItemCollector(request).update(request);
}
case RandomizableContainer te when te.getLootTable() != null -> {
return List.of();
}
case ContainerEntity containerEntity when containerEntity.getContainerLootTable() != null -> {
return List.of();
}
case EnderChestBlockEntity enderChest when request.getPlayer().getEnderChestInventory().isEmpty() -> {
return List.of();
}
default -> {
}
}
Player player = request.getPlayer();
if (!player.isCreative() && !player.isSpectator() && target instanceof BaseContainerBlockEntity te) {
if (te.lockKey != LockCode.NO_LOCK) {
return List.of();
}
}
if (target instanceof EnderChestBlockEntity) {
PlayerEnderChestContainer inventory = player.getEnderChestInventory();
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(x -> inventory, 0)).update(request);
}
ItemCollector<?> itemCollector;
try {
itemCollector = targetCache.get(target, () -> createItemCollector(request));
} catch (ExecutionException e) {
LOGGER.warn("Failed to get item collector for " + target);
return null;
}
return itemCollector.update(request);
}
@Override
public ResourceLocation getUid() {
return UNIVERSAL_ITEM_STORAGE;
}
@Override
public int getDefaultPriority() {
return 9999;
}
}
@@ -0,0 +1,104 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.LockCode;
import net.minecraft.world.RandomizableContainer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.util.CommonUtil;
import org.leavesmc.leaves.protocol.jade.util.ItemCollector;
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
import java.util.List;
import java.util.Map;
public abstract class ItemStorageProvider<T extends Accessor<?>> implements IServerDataProvider<T> {
private static final StreamCodec<RegistryFriendlyByteBuf, Map.Entry<ResourceLocation, List<ViewGroup<ItemStack>>>> STREAM_CODEC = ViewGroup.listCodec(ItemStack.OPTIONAL_STREAM_CODEC);
private static final ResourceLocation UNIVERSAL_ITEM_STORAGE = JadeProtocol.mc_id("item_storage");
public static ForBlock getBlock() {
return ForBlock.INSTANCE;
}
public static ForEntity getEntity() {
return ForEntity.INSTANCE;
}
public static void putData(CompoundTag tag, @NotNull Accessor<?> accessor) {
Object target = accessor.getTarget();
Player player = accessor.getPlayer();
Map.Entry<ResourceLocation, List<ViewGroup<ItemStack>>> entry = CommonUtil.getServerExtensionData(accessor, JadeProtocol.itemStorageProviders);
if (entry != null) {
List<ViewGroup<ItemStack>> groups = entry.getValue();
for (ViewGroup<ItemStack> group : groups) {
if (group.views.size() > ItemCollector.MAX_SIZE) {
group.views = group.views.subList(0, ItemCollector.MAX_SIZE);
}
}
tag.put(UNIVERSAL_ITEM_STORAGE.toString(), accessor.encodeAsNbt(STREAM_CODEC, entry));
return;
}
if (target instanceof RandomizableContainer containerEntity && containerEntity.getLootTable() != null) {
tag.putBoolean("Loot", true);
} else if (!player.isCreative() && !player.isSpectator() && target instanceof BaseContainerBlockEntity te) {
if (te.lockKey != LockCode.NO_LOCK) {
tag.putBoolean("Locked", true);
}
}
}
@Override
public ResourceLocation getUid() {
return UNIVERSAL_ITEM_STORAGE;
}
@Override
public void appendServerData(CompoundTag tag, @NotNull T accessor) {
if (accessor.getTarget() instanceof AbstractFurnaceBlockEntity) {
return;
}
putData(tag, accessor);
}
@Override
public int getDefaultPriority() {
return 1000;
}
public static class ForBlock extends ItemStorageProvider<BlockAccessor> {
private static final ForBlock INSTANCE = new ForBlock();
}
public static class ForEntity extends ItemStorageProvider<EntityAccessor> {
private static final ForEntity INSTANCE = new ForEntity();
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
public interface StreamServerDataProvider<T extends Accessor<?>, D> extends IServerDataProvider<T> {
@Override
default void appendServerData(CompoundTag data, T accessor) {
D value = streamData(accessor);
if (value != null) {
data.put(getUid().toString(), accessor.encodeAsNbt(streamCodec(), value));
}
}
@Nullable
D streamData(T accessor);
StreamCodec<RegistryFriendlyByteBuf, D> streamCodec();
}
@@ -0,0 +1,51 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum BeehiveProvider implements StreamServerDataProvider<BlockAccessor, Byte> {
INSTANCE;
private static final ResourceLocation MC_BEEHIVE = JadeProtocol.mc_id("beehive");
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Byte> streamCodec() {
return ByteBufCodecs.BYTE.cast();
}
@Override
public Byte streamData(@NotNull BlockAccessor accessor) {
BeehiveBlockEntity beehive = (BeehiveBlockEntity) accessor.getBlockEntity();
int bees = beehive.getOccupantCount();
return (byte) (beehive.isFull() ? bees : -bees);
}
@Override
public ResourceLocation getUid() {
return MC_BEEHIVE;
}
}
@@ -0,0 +1,77 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.Nameable;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.properties.ChestType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum BlockNameProvider implements StreamServerDataProvider<BlockAccessor, Component> {
INSTANCE;
private static final ResourceLocation CORE_OBJECT_NAME = JadeProtocol.id("object_name");
@Override
@Nullable
public Component streamData(@NotNull BlockAccessor accessor) {
if (!(accessor.getBlockEntity() instanceof Nameable nameable)) {
return null;
}
if (nameable instanceof ChestBlockEntity && accessor.getBlock() instanceof ChestBlock && accessor.getBlockState().getValue(ChestBlock.TYPE) != ChestType.SINGLE) {
MenuProvider menuProvider = accessor.getBlockState().getMenuProvider(accessor.getLevel(), accessor.getPosition());
if (menuProvider != null) {
Component name = menuProvider.getDisplayName();
if (!(name.getContents() instanceof TranslatableContents contents) || !"container.chestDouble".equals(contents.getKey())) {
return name;
}
}
} else if (nameable.hasCustomName()) {
return nameable.getDisplayName();
}
return accessor.getBlockEntity().components().get(DataComponents.ITEM_NAME);
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, Component> streamCodec() {
return ComponentSerialization.STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return CORE_OBJECT_NAME;
}
@Override
public int getDefaultPriority() {
return -10100;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.BrewingStandBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum BrewingStandProvider implements StreamServerDataProvider<BlockAccessor, BrewingStandProvider.Data> {
INSTANCE;
private static final ResourceLocation MC_BREWING_STAND = JadeProtocol.mc_id("brewing_stand");
@Override
public @NotNull Data streamData(@NotNull BlockAccessor accessor) {
BrewingStandBlockEntity brewingStand = (BrewingStandBlockEntity) accessor.getBlockEntity();
return new Data(brewingStand.fuel, brewingStand.brewTime);
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Data> streamCodec() {
return Data.STREAM_CODEC.cast();
}
@Override
public ResourceLocation getUid() {
return MC_BREWING_STAND;
}
public record Data(int fuel, int time) {
public static final StreamCodec<ByteBuf, Data> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
Data::fuel,
ByteBufCodecs.VAR_INT,
Data::time,
Data::new);
}
}
@@ -0,0 +1,72 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import com.google.common.collect.Lists;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.NbtOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.level.block.entity.CampfireBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
import org.leavesmc.leaves.protocol.jade.provider.IServerExtensionProvider;
import org.leavesmc.leaves.protocol.jade.util.ViewGroup;
import java.util.List;
public enum CampfireProvider implements IServerExtensionProvider<ItemStack> {
INSTANCE;
private static final MapCodec<Integer> COOKING_TIME_CODEC = Codec.INT.fieldOf("jade:cooking");
private static final ResourceLocation MC_CAMPFIRE = JadeProtocol.mc_id("campfire");
@Override
public @Nullable @Unmodifiable List<ViewGroup<ItemStack>> getGroups(@NotNull Accessor<?> request) {
if (request.getTarget() instanceof CampfireBlockEntity campfire) {
List<ItemStack> list = Lists.newArrayList();
for (int i = 0; i < campfire.cookingTime.length; i++) {
ItemStack stack = campfire.getItems().get(i);
if (stack.isEmpty()) {
continue;
}
stack = stack.copy();
CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY)
.update(NbtOps.INSTANCE, COOKING_TIME_CODEC, campfire.cookingTime[i] - campfire.cookingProgress[i])
.getOrThrow();
stack.set(DataComponents.CUSTOM_DATA, customData);
list.add(stack);
}
return List.of(new ViewGroup<>(list));
}
return null;
}
@Override
public ResourceLocation getUid() {
return MC_CAMPFIRE;
}
}
@@ -0,0 +1,61 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.ChiseledBookShelfBlock;
import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.ItemStorageProvider;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum ChiseledBookshelfProvider implements StreamServerDataProvider<BlockAccessor, ItemStack> {
INSTANCE;
private static final ResourceLocation MC_CHISELED_BOOKSHELF = JadeProtocol.mc_id("chiseled_bookshelf");
@Override
public @Nullable ItemStack streamData(@NotNull BlockAccessor accessor) {
int slot = ((ChiseledBookShelfBlock) accessor.getBlock()).getHitSlot(accessor.getHitResult(), accessor.getBlockState()).orElse(-1);
if (slot == -1) {
return null;
}
return ((ChiseledBookShelfBlockEntity) accessor.getBlockEntity()).getItem(slot);
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
return ItemStack.OPTIONAL_STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return MC_CHISELED_BOOKSHELF;
}
@Override
public int getDefaultPriority() {
return ItemStorageProvider.getBlock().getDefaultPriority() + 1;
}
}
@@ -0,0 +1,57 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.entity.CommandBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum CommandBlockProvider implements StreamServerDataProvider<BlockAccessor, String> {
INSTANCE;
private static final ResourceLocation MC_COMMAND_BLOCK = JadeProtocol.mc_id("command_block");
@Nullable
public String streamData(@NotNull BlockAccessor accessor) {
if (!accessor.getPlayer().canUseGameMasterBlocks()) {
return null;
}
String command = ((CommandBlockEntity) accessor.getBlockEntity()).getCommandBlock().getCommand();
if (command.length() > 40) {
command = command.substring(0, 37) + "...";
}
return command;
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, String> streamCodec() {
return ByteBufCodecs.STRING_UTF8.cast();
}
@Override
public ResourceLocation getUid() {
return MC_COMMAND_BLOCK;
}
}
@@ -0,0 +1,68 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
import java.util.List;
public enum FurnaceProvider implements StreamServerDataProvider<BlockAccessor, FurnaceProvider.Data> {
INSTANCE;
private static final ResourceLocation MC_FURNACE = JadeProtocol.mc_id("furnace");
@Override
public @NotNull Data streamData(@NotNull BlockAccessor accessor) {
AbstractFurnaceBlockEntity furnace = (AbstractFurnaceBlockEntity) accessor.getBlockEntity();
return new Data(
furnace.cookingTimer,
furnace.cookingTotalTime,
List.of(furnace.getItem(0), furnace.getItem(1), furnace.getItem(2))
);
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, Data> streamCodec() {
return Data.STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return MC_FURNACE;
}
public record Data(int progress, int total, List<ItemStack> inventory) {
public static final StreamCodec<RegistryFriendlyByteBuf, Data> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
Data::progress,
ByteBufCodecs.VAR_INT,
Data::total,
ItemStack.OPTIONAL_LIST_STREAM_CODEC,
Data::inventory,
Data::new);
}
}
@@ -0,0 +1,54 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum HopperLockProvider implements StreamServerDataProvider<BlockAccessor, Boolean> {
INSTANCE;
private static final ResourceLocation MC_HOPPER_LOCK = JadeProtocol.mc_id("hopper_lock");
@Override
public Boolean streamData(@NotNull BlockAccessor accessor) {
return !accessor.getBlockState().getValue(BlockStateProperties.ENABLED);
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Boolean> streamCodec() {
return ByteBufCodecs.BOOL.cast();
}
@Override
public ResourceLocation getUid() {
return MC_HOPPER_LOCK;
}
@Override
public int getDefaultPriority() {
return BlockNameProvider.INSTANCE.getDefaultPriority() + 10;
}
}
@@ -0,0 +1,49 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.JukeboxBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum JukeboxProvider implements StreamServerDataProvider<BlockAccessor, ItemStack> {
INSTANCE;
private static final ResourceLocation MC_JUKEBOX = JadeProtocol.mc_id("jukebox");
@Override
public @NotNull ItemStack streamData(BlockAccessor accessor) {
return ((JukeboxBlockEntity) accessor.getBlockEntity()).getTheItem();
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
return ItemStack.OPTIONAL_STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return MC_JUKEBOX;
}
}
@@ -0,0 +1,50 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum LecternProvider implements StreamServerDataProvider<BlockAccessor, ItemStack> {
INSTANCE;
private static final ResourceLocation MC_LECTERN = JadeProtocol.mc_id("lectern");
@Override
public @NotNull ItemStack streamData(@NotNull BlockAccessor accessor) {
return ((LecternBlockEntity) accessor.getBlockEntity()).getBook();
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
return ItemStack.OPTIONAL_STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return MC_LECTERN;
}
}
@@ -0,0 +1,59 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity;
import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerStateData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum MobSpawnerCooldownProvider implements StreamServerDataProvider<BlockAccessor, Integer> {
INSTANCE;
private static final ResourceLocation MC_MOB_SPAWNER_COOLDOWN = JadeProtocol.mc_id("mob_spawner.cooldown");
@Override
public @Nullable Integer streamData(@NotNull BlockAccessor accessor) {
TrialSpawnerBlockEntity spawner = (TrialSpawnerBlockEntity) accessor.getBlockEntity();
TrialSpawnerStateData spawnerData = spawner.getTrialSpawner().getStateData();
ServerLevel level = accessor.getLevel();
if (spawner.getTrialSpawner().canSpawnInLevel(level) && level.getGameTime() < spawnerData.cooldownEndsAt) {
return (int) (spawnerData.cooldownEndsAt - level.getGameTime());
}
return null;
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
return ByteBufCodecs.VAR_INT.cast();
}
@Override
public ResourceLocation getUid() {
return MC_MOB_SPAWNER_COOLDOWN;
}
}
@@ -0,0 +1,53 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.CalibratedSculkSensorBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.CalibratedSculkSensorBlockEntity;
import net.minecraft.world.level.block.entity.ComparatorBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
public enum RedstoneProvider implements IServerDataProvider<BlockAccessor> {
INSTANCE;
private static final ResourceLocation MC_REDSTONE = JadeProtocol.mc_id("redstone");
@Override
public void appendServerData(CompoundTag data, @NotNull BlockAccessor accessor) {
BlockEntity blockEntity = accessor.getBlockEntity();
if (blockEntity instanceof ComparatorBlockEntity comparator) {
data.putInt("Signal", comparator.getOutputSignal());
} else if (blockEntity instanceof CalibratedSculkSensorBlockEntity) {
Direction direction = accessor.getBlockState().getValue(CalibratedSculkSensorBlock.FACING).getOpposite();
int signal = accessor.getLevel().getSignal(accessor.getPosition().relative(direction), direction);
data.putInt("Signal", signal);
}
}
@Override
public ResourceLocation getUid() {
return MC_REDSTONE;
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.OwnableEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
import org.leavesmc.leaves.protocol.jade.util.CommonUtil;
import java.util.UUID;
public enum AnimalOwnerProvider implements StreamServerDataProvider<EntityAccessor, String> {
INSTANCE;
private static final ResourceLocation MC_ANIMAL_OWNER = JadeProtocol.mc_id("animal_owner");
public static UUID getOwnerUUID(Entity entity) {
if (entity instanceof OwnableEntity ownableEntity) {
EntityReference<LivingEntity> reference = ownableEntity.getOwnerReference();
if (reference != null) {
return reference.getUUID();
}
}
return null;
}
@Override
public String streamData(@NotNull EntityAccessor accessor) {
return CommonUtil.getLastKnownUsername(getOwnerUUID(accessor.getEntity()));
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, String> streamCodec() {
return ByteBufCodecs.STRING_UTF8.cast();
}
@Override
public ResourceLocation getUid() {
return MC_ANIMAL_OWNER;
}
}
@@ -0,0 +1,61 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.animal.allay.Allay;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum MobBreedingProvider implements StreamServerDataProvider<EntityAccessor, Integer> {
INSTANCE;
private static final ResourceLocation MC_MOB_BREEDING = JadeProtocol.mc_id("mob_breeding");
@Override
public @Nullable Integer streamData(@NotNull EntityAccessor accessor) {
int time = 0;
Entity entity = accessor.getEntity();
if (entity instanceof Allay allay) {
if (allay.duplicationCooldown > 0 && allay.duplicationCooldown < Integer.MAX_VALUE) {
time = (int) allay.duplicationCooldown;
}
} else {
time = ((Animal) entity).getAge();
}
return time > 0 ? time : null;
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
return ByteBufCodecs.VAR_INT.cast();
}
@Override
public ResourceLocation getUid() {
return MC_MOB_BREEDING;
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.AgeableMob;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.animal.frog.Tadpole;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum MobGrowthProvider implements StreamServerDataProvider<EntityAccessor, Integer> {
INSTANCE;
private static final ResourceLocation MC_MOB_GROWTH = JadeProtocol.mc_id("mob_growth");
@Override
public @Nullable Integer streamData(@NotNull EntityAccessor accessor) {
int time = -1;
Entity entity = accessor.getEntity();
if (entity instanceof AgeableMob ageable) {
time = -ageable.getAge();
} else if (entity instanceof Tadpole tadpole) {
time = tadpole.getTicksLeftUntilAdult();
}
return time > 0 ? time : null;
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
return ByteBufCodecs.VAR_INT.cast();
}
@Override
public ResourceLocation getUid() {
return MC_MOB_GROWTH;
}
}
@@ -0,0 +1,59 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.animal.Chicken;
import net.minecraft.world.entity.animal.armadillo.Armadillo;
import net.minecraft.world.entity.animal.sniffer.Sniffer;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.provider.IServerDataProvider;
public enum NextEntityDropProvider implements IServerDataProvider<EntityAccessor> {
INSTANCE;
private static final ResourceLocation MC_NEXT_ENTITY_DROP = JadeProtocol.mc_id("next_entity_drop");
@Override
public void appendServerData(CompoundTag tag, @NotNull EntityAccessor accessor) {
int max = 24000 * 2;
if (accessor.getEntity() instanceof Chicken chicken) {
if (!chicken.isBaby() && chicken.eggTime < max) {
tag.putInt("NextEggIn", chicken.eggTime);
}
} else if (accessor.getEntity() instanceof Armadillo armadillo) {
if (!armadillo.isBaby() && armadillo.scuteTime < max) {
tag.putInt("NextScuteIn", armadillo.scuteTime);
}
} else if (accessor.getEntity() instanceof Sniffer sniffer) {
long time = sniffer.getBrain().getTimeUntilExpiry(MemoryModuleType.SNIFF_COOLDOWN);
if (time > 0 && time < max) {
tag.putInt("NextSniffIn", (int) time);
}
}
}
@Override
public ResourceLocation getUid() {
return MC_NEXT_ENTITY_DROP;
}
}
@@ -0,0 +1,52 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum PetArmorProvider implements StreamServerDataProvider<EntityAccessor, ItemStack> {
INSTANCE;
private static final ResourceLocation MC_PET_ARMOR = JadeProtocol.mc_id("pet_armor");
@Nullable
@Override
public ItemStack streamData(@NotNull EntityAccessor accessor) {
ItemStack armor = ((Mob) accessor.getEntity()).getBodyArmorItem();
return armor.isEmpty() ? null : armor;
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, ItemStack> streamCodec() {
return ItemStack.OPTIONAL_STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return MC_PET_ARMOR;
}
}
@@ -0,0 +1,62 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
import java.util.List;
public enum StatusEffectsProvider implements StreamServerDataProvider<EntityAccessor, List<MobEffectInstance>> {
INSTANCE;
private static final StreamCodec<RegistryFriendlyByteBuf, List<MobEffectInstance>> STREAM_CODEC = ByteBufCodecs.<RegistryFriendlyByteBuf, MobEffectInstance>list()
.apply(MobEffectInstance.STREAM_CODEC);
private static final ResourceLocation MC_POTION_EFFECTS = JadeProtocol.mc_id("potion_effects");
@Override
@Nullable
public List<MobEffectInstance> streamData(@NotNull EntityAccessor accessor) {
List<MobEffectInstance> effects = ((LivingEntity) accessor.getEntity()).getActiveEffects()
.stream()
.filter(MobEffectInstance::isVisible)
.toList();
return effects.isEmpty() ? null : effects;
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, List<MobEffectInstance>> streamCodec() {
return STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return MC_POTION_EFFECTS;
}
}
@@ -0,0 +1,51 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.monster.ZombieVillager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum ZombieVillagerProvider implements StreamServerDataProvider<EntityAccessor, Integer> {
INSTANCE;
private static final ResourceLocation MC_ZOMBIE_VILLAGER = JadeProtocol.mc_id("zombie_villager");
@Override
public @Nullable Integer streamData(@NotNull EntityAccessor accessor) {
int time = ((ZombieVillager) accessor.getEntity()).villagerConversionTime;
return time > 0 ? time : null;
}
@Override
public @NotNull StreamCodec<RegistryFriendlyByteBuf, Integer> streamCodec() {
return ByteBufCodecs.VAR_INT.cast();
}
@Override
public ResourceLocation getUid() {
return MC_ZOMBIE_VILLAGER;
}
}

Some files were not shown because too many files have changed in this diff Show More