Update master #3

Zusammengeführt
YoyoNow hat 7 Commits von Mirrors/AxiomPaperPlugin:master nach master 2023-09-22 17:32:24 +02:00 zusammengeführt
19 geänderte Dateien mit 1096 neuen und 94 gelöschten Zeilen

Datei anzeigen

@ -12,7 +12,7 @@ public class AxiomConstants {
}
}
public static final int API_VERSION = 5;
public static final int API_VERSION = 6;
public static final NamespacedKey ACTIVE_HOTBAR_INDEX = new NamespacedKey("axiom", "active_hotbar_index");
public static final NamespacedKey HOTBAR_DATA = new NamespacedKey("axiom", "hotbar_data");

Datei anzeigen

@ -1,10 +1,17 @@
package com.moulberry.axiom;
import com.moulberry.axiom.buffer.CompressedBlockEntity;
import com.moulberry.axiom.event.AxiomCreateWorldPropertiesEvent;
import com.moulberry.axiom.event.AxiomTimeChangeEvent;
import com.moulberry.axiom.packet.*;
import com.moulberry.axiom.world_properties.WorldPropertyCategory;
import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.papermc.paper.event.player.PlayerFailMoveEvent;
import io.papermc.paper.event.world.WorldGameRuleChangeEvent;
import io.papermc.paper.network.ChannelInitializeListener;
import io.papermc.paper.network.ChannelInitializeListenerHolder;
import net.kyori.adventure.key.Key;
@ -14,22 +21,34 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundCustomPayloadPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.GameRules;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.Messenger;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class AxiomPaper extends JavaPlugin implements Listener {
public static AxiomPaper PLUGIN; // tsk tsk tsk
public final Set<UUID> activeAxiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
@Override
public void onEnable() {
PLUGIN = this;
Bukkit.getPluginManager().registerEvents(this, this);
// Bukkit.getPluginManager().registerEvents(new WorldPropertiesExample(), this);
CompressedBlockEntity.initialize(this);
Messenger msg = Bukkit.getMessenger();
@ -38,12 +57,15 @@ public class AxiomPaper extends JavaPlugin implements Listener {
msg.registerOutgoingPluginChannel(this, "axiom:initialize_hotbars");
msg.registerOutgoingPluginChannel(this, "axiom:set_editor_views");
msg.registerOutgoingPluginChannel(this, "axiom:block_entities");
final Set<UUID> activeAxiomPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
msg.registerOutgoingPluginChannel(this, "axiom:register_world_properties");
msg.registerOutgoingPluginChannel(this, "axiom:set_world_property");
msg.registerOutgoingPluginChannel(this, "axiom:ack_world_properties");
msg.registerIncomingPluginChannel(this, "axiom:hello", new HelloPacketListener(this, activeAxiomPlayers));
msg.registerIncomingPluginChannel(this, "axiom:set_gamemode", new SetGamemodePacketListener());
msg.registerIncomingPluginChannel(this, "axiom:set_fly_speed", new SetFlySpeedPacketListener());
msg.registerIncomingPluginChannel(this, "axiom:set_world_time", new SetTimePacketListener());
msg.registerIncomingPluginChannel(this, "axiom:set_world_property", new SetWorldPropertyListener());
msg.registerIncomingPluginChannel(this, "axiom:set_block", new SetBlockPacketListener(this));
msg.registerIncomingPluginChannel(this, "axiom:set_hotbar_slot", new SetHotbarSlotPacketListener());
msg.registerIncomingPluginChannel(this, "axiom:switch_active_hotbar", new SwitchActiveHotbarPacketListener());
@ -94,12 +116,76 @@ public class AxiomPaper extends JavaPlugin implements Listener {
}, 20, 20);
}
private final WeakHashMap<World, ServerWorldPropertiesRegistry> worldProperties = new WeakHashMap<>();
public @Nullable ServerWorldPropertiesRegistry getWorldProperties(World world) {
if (worldProperties.containsKey(world)) {
return worldProperties.get(world);
} else {
ServerWorldPropertiesRegistry properties = createWorldProperties(world);
worldProperties.put(world, properties);
return properties;
}
}
@EventHandler
public void onFailMove(PlayerFailMoveEvent event) {
if (event.getPlayer().hasPermission("axiom.*") &&
event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) {
event.getFailReason() == PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY) {
event.setAllowed(true);
}
}
@EventHandler
public void onChangedWorld(PlayerChangedWorldEvent event) {
World world = event.getPlayer().getWorld();
ServerWorldPropertiesRegistry properties = getWorldProperties(world);
if (properties == null) {
event.getPlayer().sendPluginMessage(this, "axiom:register_world_properties", new byte[]{0});
} else {
properties.registerFor(this, event.getPlayer());
}
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
Bukkit.getScheduler().scheduleSyncDelayedTask(this, () -> {
World world = player.getWorld();
ServerWorldPropertiesRegistry properties = getWorldProperties(world);
if (properties == null) {
player.sendPluginMessage(this, "axiom:register_world_properties", new byte[]{0});
} else {
properties.registerFor(this, player);
}
}, 20); // Why does this need to be delayed?
}
@EventHandler
public void onGameRuleChanged(WorldGameRuleChangeEvent event) {
if (event.getGameRule() == GameRule.DO_WEATHER_CYCLE) {
ServerWorldPropertiesRegistry properties = getWorldProperties(event.getWorld());
if (properties != null) {
ServerWorldProperty<?> property = properties.getById(new ResourceLocation("axiom:pause_weather"));
if (property != null) {
((ServerWorldProperty<Boolean>)property).setValue(event.getWorld(), !Boolean.parseBoolean(event.getValue()));
}
}
}
}
private ServerWorldPropertiesRegistry createWorldProperties(World world) {
ServerWorldPropertiesRegistry registry = new ServerWorldPropertiesRegistry(world);
AxiomCreateWorldPropertiesEvent createEvent = new AxiomCreateWorldPropertiesEvent(world, registry);
Bukkit.getPluginManager().callEvent(createEvent);
if (createEvent.isCancelled()) return null;
return registry;
}
}

Datei anzeigen

@ -0,0 +1,55 @@
package com.moulberry.axiom;
import com.moulberry.axiom.event.AxiomCreateWorldPropertiesEvent;
import com.moulberry.axiom.world_properties.WorldPropertyCategory;
import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import net.kyori.adventure.text.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Unit;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.util.List;
public class WorldPropertiesExample implements Listener {
@EventHandler
public void onCreateWorldProperties(AxiomCreateWorldPropertiesEvent event) {
WorldPropertyCategory category = new WorldPropertyCategory("Examples", false);
World world = event.getWorld();
ServerWorldProperty<Boolean> checkbox = new ServerWorldProperty<>(new ResourceLocation("axiom:checkbox"),
"Checkbox",
false, WorldPropertyWidgetType.CHECKBOX, false, bool -> {
world.sendMessage(Component.text("Checkbox: " + bool)); // Do something with input
return true; // true to sync with client
});
ServerWorldProperty<Integer> slider = new ServerWorldProperty<>(new ResourceLocation("axiom:slider"),
"Slider",
false, new WorldPropertyWidgetType.Slider(0, 8), 4, integer -> {
world.sendMessage(Component.text("Slider: " + integer)); // Do something with input
return true; // true to sync with client
});
ServerWorldProperty<String> textbox = new ServerWorldProperty<>(new ResourceLocation("axiom:textbox"),
"Textbox",
false, WorldPropertyWidgetType.TEXTBOX, "Hello", string -> {
world.sendMessage(Component.text("Textbox: " + string)); // Do something with input
return true; // true to sync with client
});
ServerWorldProperty<Unit> button = new ServerWorldProperty<>(new ResourceLocation("axiom:button"),
"Button",
false, WorldPropertyWidgetType.BUTTON, Unit.INSTANCE, unit -> {
world.sendMessage(Component.text("Button pressed")); // Do something with input
return true; // true to sync with client
});
event.addCategory(category, List.of(checkbox, slider, textbox, button));
}
}

Datei anzeigen

@ -0,0 +1,56 @@
package com.moulberry.axiom.event;
import com.moulberry.axiom.world_properties.WorldPropertyCategory;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class AxiomCreateWorldPropertiesEvent extends Event implements Cancellable {
private static final HandlerList HANDLERS = new HandlerList();
private final World world;
private final ServerWorldPropertiesRegistry registry;
private boolean cancelled = false;
public AxiomCreateWorldPropertiesEvent(World world, ServerWorldPropertiesRegistry registry) {
this.world = world;
this.registry = registry;
}
public World getWorld() {
return world;
}
public void addCategory(WorldPropertyCategory category, List<ServerWorldProperty<?>> properties) {
this.registry.addCategory(category, properties);
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
}

Datei anzeigen

@ -0,0 +1,56 @@
package com.moulberry.axiom.event;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AxiomTimeChangeEvent extends Event implements Cancellable {
private static final HandlerList HANDLERS = new HandlerList();
private final Player player;
private final @Nullable Integer time;
private final @Nullable Boolean freezeTime;
private boolean cancelled = false;
public AxiomTimeChangeEvent(Player player, @Nullable Integer time, @Nullable Boolean freezeTime) {
this.player = player;
this.time = time;
this.freezeTime = freezeTime;
}
public @Nullable Integer getTime() {
return time;
}
public @Nullable Boolean isFreezeTime() {
return freezeTime;
}
public Player getPlayer() {
return this.player;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
@Override
public @NotNull HandlerList getHandlers() {
return HANDLERS;
}
}

Datei anzeigen

@ -4,6 +4,9 @@ import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
public class RegionProtection {
private final RegionProtectionWorldGuard worldGuard;
@ -16,8 +19,17 @@ public class RegionProtection {
}
}
public boolean canBuildInSection(int cx, int cy, int cz) {
if (this.worldGuard != null && !this.worldGuard.canBuildInSection(cx, cy, cz)) return false;
public SectionProtection getSection(int cx, int cy, int cz) {
List<SectionProtection> protections = new ArrayList<>();
if (this.worldGuard != null) {
return this.worldGuard.getSection(cx, cy, cz);
}
// todo: PlotSquared
return SectionProtection.ALLOW;
}
public boolean canBuild(int x, int y, int z) {
if (this.worldGuard != null && !this.worldGuard.canBuild(x, y, z)) return false;
// todo: PlotSquared
return true;
}

Datei anzeigen

@ -1,5 +1,7 @@
package com.moulberry.axiom.integration;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldguard.LocalPlayer;
@ -7,16 +9,21 @@ import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.sk89q.worldguard.internal.platform.WorldGuardPlatform;
import com.sk89q.worldguard.protection.ApplicableRegionSet;
import com.sk89q.worldguard.protection.FlagValueCalculator;
import com.sk89q.worldguard.protection.association.RegionAssociable;
import com.sk89q.worldguard.protection.flags.Flags;
import com.sk89q.worldguard.protection.flags.RegionGroup;
import com.sk89q.worldguard.protection.flags.StateFlag;
import com.sk89q.worldguard.protection.managers.RegionManager;
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import com.sk89q.worldguard.protection.regions.RegionQuery;
import com.sk89q.worldguard.protection.regions.*;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.google.common.base.Preconditions.checkNotNull;
public class RegionProtectionWorldGuard {
private final LocalPlayer player;
@ -32,12 +39,15 @@ public class RegionProtectionWorldGuard {
WorldGuardPlatform platform = WorldGuard.getInstance().getPlatform();
RegionContainer regionContainer = platform.getRegionContainer();
if (regionContainer == null) return null;
com.sk89q.worldedit.world.World worldEditWorld = BukkitAdapter.adapt(world);
LocalPlayer worldGuardPlayer = WorldGuardPlugin.inst().wrapPlayer(player);
// Don't do any protection if player has bypass
if (platform.getSessionManager().hasBypass(worldGuardPlayer, worldEditWorld)) {
return null;
// todo: enable bypass
// return null;
}
RegionManager regionManager = regionContainer.get(worldEditWorld);
@ -46,12 +56,128 @@ public class RegionProtectionWorldGuard {
return new RegionProtectionWorldGuard(worldGuardPlayer, regionManager);
}
public boolean canBuildInSection(int cx, int cy, int cz) {
public SectionProtection getSection(int cx, int cy, int cz) {
BlockVector3 min = BlockVector3.at(cx*16, cy*16, cz*16);
BlockVector3 max = BlockVector3.at(cx*16+15, cy*16+15, cz*16+15);
ProtectedRegion test = new ProtectedCuboidRegion("dummy", min, max);
ApplicableRegionSet regions = this.regionManager.getApplicableRegions(test, RegionQuery.QueryOption.COMPUTE_PARENTS);
return regions.testState(this.player, Flags.BUILD);
int minimumPriority = Integer.MIN_VALUE;
Map<ProtectedRegion, StateFlag.State> consideredValues = new HashMap<>();
Set<ProtectedRegion> ignoredParents = new HashSet<>();
for (ProtectedRegion region : regions) {
int priority = FlagValueCalculator.getPriorityOf(region);
// todo: this logic doesn't work for us in determining ALLOW, DENY, CHECK
if (priority < minimumPriority) {
break;
}
// todo: have to keep track of 2 booleans: partialAllow & partialDeny
if (ignoredParents.contains(region)) {
continue;
}
StateFlag.State value = FlagValueCalculator.getEffectiveFlagOf(region, Flags.BUILD, this.player);
if (value != null) {
minimumPriority = priority;
consideredValues.put(region, value);
}
addParents(ignoredParents, region);
// The BUILD flag is implicitly set on every region where
// PASSTHROUGH is not set to ALLOW
if (minimumPriority != priority && Flags.BUILD.implicitlySetWithMembership() &&
FlagValueCalculator.getEffectiveFlagOf(region, Flags.PASSTHROUGH, this.player) != StateFlag.State.ALLOW) {
minimumPriority = priority;
}
}
if (consideredValues.isEmpty()) {
if (Flags.BUILD.usesMembershipAsDefault()) {
// todo
// switch (getMembership(subject)) {
// case FAIL:
// return ImmutableList.of();
// case SUCCESS:
// return (Collection<V>) ImmutableList.of(StateFlag.State.ALLOW);
// }
}
System.out.println("returning default");
StateFlag.State fallback = Flags.BUILD.getDefault();
return fallback == StateFlag.State.DENY ? SectionProtection.DENY : SectionProtection.ALLOW;
}
boolean hasPartialDeny = false;
for (Map.Entry<ProtectedRegion, StateFlag.State> entry : consideredValues.entrySet()) {
ProtectedRegion region = entry.getKey();
if (entry.getValue() == StateFlag.State.DENY) {
System.out.println("found region with deny!");
if (region instanceof GlobalProtectedRegion) {
return SectionProtection.DENY;
} else if (region instanceof ProtectedCuboidRegion && doesRegionCompletelyContainSection(region, cx, cy, cz)) {
return SectionProtection.DENY;
}
hasPartialDeny = true;
}
}
if (hasPartialDeny) {
System.out.println("returning check!");
return new SectionProtection() {
@Override
public SectionState getSectionState() {
return SectionState.CHECK;
}
@Override
public boolean check(int wx, int wy, int wz) {
return true;
}
};
// return complex thing
}
System.out.println("returning allow!");
return SectionProtection.ALLOW;
}
private boolean doesRegionCompletelyContainSection(ProtectedRegion region, int cx, int cy, int cz) {
BlockVector3 regionMin = region.getMinimumPoint();
if (regionMin.getBlockX() > cx*16) return false;
if (regionMin.getBlockY() > cy*16) return false;
if (regionMin.getBlockZ() > cz*16) return false;
BlockVector3 regionMax = region.getMaximumPoint();
if (regionMax.getBlockX() < cx*16+15) return false;
if (regionMax.getBlockY() < cy*16+15) return false;
if (regionMax.getBlockZ() < cz*16+15) return false;
return true;
}
private void addParents(Set<ProtectedRegion> ignored, ProtectedRegion region) {
ProtectedRegion parent = region.getParent();
while (parent != null) {
ignored.add(parent);
parent = parent.getParent();
}
}
public boolean canBuild(int x, int y, int z) {
return this.regionManager.getApplicableRegions(BlockVector3.at(x, y, z)).testState(this.player, Flags.BUILD);
}
public boolean isAllowed(LocalPlayer player, ProtectedRegion protectedRegion) {
return protectedRegion.isOwner(player) || protectedRegion.isMember(player);
}
}

Datei anzeigen

@ -0,0 +1,38 @@
package com.moulberry.axiom.integration;
public interface SectionProtection {
SectionProtection ALLOW = new SectionProtection() {
@Override
public SectionState getSectionState() {
return SectionState.ALLOW;
}
@Override
public boolean check(int wx, int wy, int wz) {
return true;
}
};
SectionProtection DENY = new SectionProtection() {
@Override
public SectionState getSectionState() {
return SectionState.DENY;
}
@Override
public boolean check(int wx, int wy, int wz) {
return false;
}
};
enum SectionState {
ALLOW,
DENY,
CHECK
}
SectionState getSectionState();
boolean check(int wx, int wy, int wz);
}

Datei anzeigen

@ -60,7 +60,6 @@ public class HelloPacketListener implements PluginMessageListener {
// Enable
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeBoolean(true);
buf.writeByte(0); // todo: world properties
buf.writeInt(handshakeEvent.getMaxBufferSize()); // Max Buffer Size
buf.writeBoolean(false); // No source info
buf.writeBoolean(false); // No source settings

Datei anzeigen

@ -6,6 +6,7 @@ import com.moulberry.axiom.buffer.BlockBuffer;
import com.moulberry.axiom.buffer.CompressedBlockEntity;
import com.moulberry.axiom.event.AxiomModifyWorldEvent;
import com.moulberry.axiom.integration.RegionProtection;
import com.moulberry.axiom.integration.SectionProtection;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.World;
@ -38,6 +39,7 @@ import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
@ -135,9 +137,14 @@ public class SetBlockBufferPacketListener {
continue;
}
if (!regionProtection.canBuildInSection(cx, cy, cz)) {
continue;
}
SectionProtection sectionProtection = regionProtection.getSection(cx, cy, cz);
// switch (sectionProtection.getSectionState()) {
// case ALLOW -> sectionProtection = null;
// case DENY -> {
// continue;
// }
// case CHECK -> {}
// }
LevelChunk chunk = world.getChunk(cx, cz);
chunk.setUnsaved(true);
@ -170,10 +177,20 @@ public class SetBlockBufferPacketListener {
BlockState blockState = container.get(x, y, z);
if (blockState == emptyState) continue;
switch (sectionProtection.getSectionState()) {
case ALLOW -> {}
case DENY -> blockState = Blocks.REDSTONE_BLOCK.defaultBlockState();
case CHECK -> blockState = Blocks.DIAMOND_BLOCK.defaultBlockState();
}
int bx = cx*16 + x;
int by = cy*16 + y;
int bz = cz*16 + z;
// if (!regionProtection.canBuild(bx, by, bz)) {
// continue;
// }
blockPos.set(bx, by, bz);
if (hasOnlyAir && blockState.isAir()) {

Datei anzeigen

@ -8,6 +8,8 @@ import net.minecraft.core.SectionPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -16,9 +18,14 @@ import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.lighting.LightEngine;
import net.minecraft.world.phys.BlockHitResult;
import org.bukkit.Bukkit;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlock;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
@ -61,97 +68,130 @@ public class SetBlockPacketListener implements PluginMessageListener {
// Read packet
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
BlockPos blockPos = friendlyByteBuf.readBlockPos();
BlockState blockState = friendlyByteBuf.readById(Block.BLOCK_STATE_REGISTRY);
Map<BlockPos, BlockState> blocks = friendlyByteBuf.readMap(FriendlyByteBuf::readBlockPos, buf -> buf.readById(Block.BLOCK_STATE_REGISTRY));
boolean updateNeighbors = friendlyByteBuf.readBoolean();
int sequenceId = friendlyByteBuf.readInt();
int reason = friendlyByteBuf.readVarInt();
boolean breaking = friendlyByteBuf.readBoolean();
BlockHitResult blockHit = friendlyByteBuf.readBlockHitResult();
InteractionHand hand = friendlyByteBuf.readEnum(InteractionHand.class);
int sequenceId = friendlyByteBuf.readVarInt();
ServerPlayer player = ((CraftPlayer)bukkitPlayer).getHandle();
org.bukkit.inventory.ItemStack heldItem;
if (hand == InteractionHand.MAIN_HAND) {
heldItem = bukkitPlayer.getInventory().getItemInMainHand();
} else {
heldItem = bukkitPlayer.getInventory().getItemInOffHand();
}
org.bukkit.block.Block blockClicked = bukkitPlayer.getWorld().getBlockAt(blockHit.getBlockPos().getX(),
blockHit.getBlockPos().getY(), blockHit.getBlockPos().getZ());
BlockFace blockFace = CraftBlock.notchToBlockFace(blockHit.getDirection());
// Call interact event
PlayerInteractEvent playerInteractEvent = new PlayerInteractEvent(bukkitPlayer,
breaking ? Action.LEFT_CLICK_BLOCK : Action.RIGHT_CLICK_BLOCK, heldItem, blockClicked, blockFace);
if (!playerInteractEvent.callEvent()) {
if (sequenceId >= 0) {
player.connection.ackBlockChangesUpTo(sequenceId);
}
return;
}
// Update blocks
if (updateNeighbors) {
player.level().setBlock(blockPos, blockState, 3);
for (Map.Entry<BlockPos, BlockState> entry : blocks.entrySet()) {
player.level().setBlock(entry.getKey(), entry.getValue(), 3);
}
} else {
int bx = blockPos.getX();
int by = blockPos.getY();
int bz = blockPos.getZ();
int x = bx & 0xF;
int y = by & 0xF;
int z = bz & 0xF;
int cx = bx >> 4;
int cy = by >> 4;
int cz = bz >> 4;
for (Map.Entry<BlockPos, BlockState> entry : blocks.entrySet()) {
BlockPos blockPos = entry.getKey();
BlockState blockState = entry.getValue();
ServerLevel level = player.serverLevel();
LevelChunk chunk = level.getChunk(cx, cz);
chunk.setUnsaved(true);
int bx = blockPos.getX();
int by = blockPos.getY();
int bz = blockPos.getZ();
int x = bx & 0xF;
int y = by & 0xF;
int z = bz & 0xF;
int cx = bx >> 4;
int cy = by >> 4;
int cz = bz >> 4;
LevelChunkSection section = chunk.getSection(level.getSectionIndexFromSectionY(cy));
boolean hasOnlyAir = section.hasOnlyAir();
ServerLevel level = player.serverLevel();
LevelChunk chunk = level.getChunk(cx, cz);
chunk.setUnsaved(true);
Heightmap worldSurface = null;
Heightmap oceanFloor = null;
Heightmap motionBlocking = null;
Heightmap motionBlockingNoLeaves = null;
for (Map.Entry<Heightmap.Types, Heightmap> heightmap : chunk.getHeightmaps()) {
switch (heightmap.getKey()) {
case WORLD_SURFACE -> worldSurface = heightmap.getValue();
case OCEAN_FLOOR -> oceanFloor = heightmap.getValue();
case MOTION_BLOCKING -> motionBlocking = heightmap.getValue();
case MOTION_BLOCKING_NO_LEAVES -> motionBlockingNoLeaves = heightmap.getValue();
default -> {}
}
}
LevelChunkSection section = chunk.getSection(level.getSectionIndexFromSectionY(cy));
boolean hasOnlyAir = section.hasOnlyAir();
BlockState old = section.setBlockState(x, y, z, blockState, false);
if (blockState != old) {
Block block = blockState.getBlock();
motionBlocking.update(x, by, z, blockState);
motionBlockingNoLeaves.update(x, by, z, blockState);
oceanFloor.update(x, by, z, blockState);
worldSurface.update(x, by, z, blockState);
if (blockState.hasBlockEntity()) {
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
if (blockEntity == null) {
// There isn't a block entity here, create it!
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
} else if (blockEntity.getType().isValid(blockState)) {
// Block entity is here and the type is correct
// Just update the state and ticker and move on
blockEntity.setBlockState(blockState);
try {
this.updateBlockEntityTicker.invoke(chunk, blockEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
// Block entity type isn't correct, we need to recreate it
chunk.removeBlockEntity(blockPos);
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
Heightmap worldSurface = null;
Heightmap oceanFloor = null;
Heightmap motionBlocking = null;
Heightmap motionBlockingNoLeaves = null;
for (Map.Entry<Heightmap.Types, Heightmap> heightmap : chunk.getHeightmaps()) {
switch (heightmap.getKey()) {
case WORLD_SURFACE -> worldSurface = heightmap.getValue();
case OCEAN_FLOOR -> oceanFloor = heightmap.getValue();
case MOTION_BLOCKING -> motionBlocking = heightmap.getValue();
case MOTION_BLOCKING_NO_LEAVES -> motionBlockingNoLeaves = heightmap.getValue();
default -> {}
}
} else if (old.hasBlockEntity()) {
chunk.removeBlockEntity(blockPos);
}
level.getChunkSource().blockChanged(blockPos);
if (LightEngine.hasDifferentLightProperties(chunk, blockPos, old, blockState)) {
level.getChunkSource().getLightEngine().checkBlock(blockPos);
}
}
BlockState old = section.setBlockState(x, y, z, blockState, false);
if (blockState != old) {
Block block = blockState.getBlock();
motionBlocking.update(x, by, z, blockState);
motionBlockingNoLeaves.update(x, by, z, blockState);
oceanFloor.update(x, by, z, blockState);
worldSurface.update(x, by, z, blockState);
boolean nowHasOnlyAir = section.hasOnlyAir();
if (hasOnlyAir != nowHasOnlyAir) {
level.getChunkSource().getLightEngine().updateSectionStatus(SectionPos.of(cx, cy, cz), nowHasOnlyAir);
if (blockState.hasBlockEntity()) {
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
if (blockEntity == null) {
// There isn't a block entity here, create it!
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
} else if (blockEntity.getType().isValid(blockState)) {
// Block entity is here and the type is correct
// Just update the state and ticker and move on
blockEntity.setBlockState(blockState);
try {
this.updateBlockEntityTicker.invoke(chunk, blockEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
// Block entity type isn't correct, we need to recreate it
chunk.removeBlockEntity(blockPos);
blockEntity = ((EntityBlock)block).newBlockEntity(blockPos, blockState);
if (blockEntity != null) {
chunk.addAndRegisterBlockEntity(blockEntity);
}
}
} else if (old.hasBlockEntity()) {
chunk.removeBlockEntity(blockPos);
}
level.getChunkSource().blockChanged(blockPos);
if (LightEngine.hasDifferentLightProperties(chunk, blockPos, old, blockState)) {
level.getChunkSource().getLightEngine().checkBlock(blockPos);
}
}
boolean nowHasOnlyAir = section.hasOnlyAir();
if (hasOnlyAir != nowHasOnlyAir) {
level.getChunkSource().getLightEngine().updateSectionStatus(SectionPos.of(cx, cy, cz), nowHasOnlyAir);
}
}
}

Datei anzeigen

@ -20,7 +20,6 @@ public class SetFlySpeedPacketListener implements PluginMessageListener {
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
float flySpeed = friendlyByteBuf.readFloat();

Datei anzeigen

@ -0,0 +1,45 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.event.AxiomTimeChangeEvent;
import io.netty.buffer.Unpooled;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
public class SetTimePacketListener implements PluginMessageListener {
@Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
if (!player.hasPermission("axiom.*")) {
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
ResourceKey<Level> key = friendlyByteBuf.readResourceKey(Registries.DIMENSION);
Integer time = friendlyByteBuf.readNullable(FriendlyByteBuf::readInt);
Boolean freezeTime = friendlyByteBuf.readNullable(FriendlyByteBuf::readBoolean);
if (time == null && freezeTime == null) return;
ServerLevel level = ((CraftWorld)player.getWorld()).getHandle();
if (!level.dimension().equals(key)) return;
// Call event
AxiomTimeChangeEvent timeChangeEvent = new AxiomTimeChangeEvent(player, time, freezeTime);
Bukkit.getPluginManager().callEvent(timeChangeEvent);
if (timeChangeEvent.isCancelled()) return;
// Change time
if (time != null) level.setDayTime(time);
if (freezeTime != null) level.getGameRules().getRule(GameRules.RULE_DAYLIGHT).set(!freezeTime, null);
}
}

Datei anzeigen

@ -0,0 +1,53 @@
package com.moulberry.axiom.packet;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.event.AxiomTimeChangeEvent;
import com.moulberry.axiom.world_properties.WorldPropertyCategory;
import com.moulberry.axiom.world_properties.server.ServerWorldPropertiesRegistry;
import com.moulberry.axiom.world_properties.server.ServerWorldProperty;
import io.netty.buffer.Unpooled;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
public class SetWorldPropertyListener implements PluginMessageListener {
@Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, @NotNull byte[] message) {
if (!player.hasPermission("axiom.*")) {
return;
}
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(message));
ResourceLocation id = friendlyByteBuf.readResourceLocation();
int type = friendlyByteBuf.readVarInt();
byte[] data = friendlyByteBuf.readByteArray();
int updateId = friendlyByteBuf.readVarInt();
ServerWorldPropertiesRegistry registry = AxiomPaper.PLUGIN.getWorldProperties(player.getWorld());
if (registry == null) return;
ServerWorldProperty<?> property = registry.getById(id);
if (property != null && property.getType().getTypeId() == type) {
property.update(player.getWorld(), data);
}
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeVarInt(updateId);
player.sendPluginMessage(AxiomPaper.PLUGIN, "axiom:ack_world_properties",
buf.accessByteBufWithCorrectSize());
}
}

Datei anzeigen

@ -0,0 +1,19 @@
package com.moulberry.axiom.world_properties;
import net.minecraft.network.FriendlyByteBuf;
public record WorldPropertyCategory(String name, boolean localizeName) {
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeUtf(this.name);
friendlyByteBuf.writeBoolean(this.localizeName);
}
public static WorldPropertyCategory read(FriendlyByteBuf friendlyByteBuf) {
return new WorldPropertyCategory(
friendlyByteBuf.readUtf(),
friendlyByteBuf.readBoolean()
);
}
}

Datei anzeigen

@ -0,0 +1,129 @@
package com.moulberry.axiom.world_properties;
import io.netty.buffer.Unpooled;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.util.Unit;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.nio.charset.StandardCharsets;
public abstract class WorldPropertyDataType<T> {
public abstract int getTypeId();
public abstract byte[] serialize(T value);
public abstract T deserialize(byte[] bytes);
public static WorldPropertyDataType<Boolean> BOOLEAN = new WorldPropertyDataType<>() {
@Override
public int getTypeId() {
return 0;
}
@Override
public byte[] serialize(Boolean value) {
return new byte[] { value ? (byte)1 : (byte)0 };
}
@Override
public Boolean deserialize(byte[] bytes) {
return bytes[0] != 0;
}
};
public static WorldPropertyDataType<Integer> INTEGER = new WorldPropertyDataType<>() {
@Override
public int getTypeId() {
return 1;
}
@Override
public byte[] serialize(Integer value) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(8));
buf.writeVarInt(value);
return buf.accessByteBufWithCorrectSize();
}
@Override
public Integer deserialize(byte[] bytes) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(bytes));
return buf.readVarInt();
}
};
public static WorldPropertyDataType<String> STRING = new WorldPropertyDataType<>() {
@Override
public int getTypeId() {
return 2;
}
@Override
public byte[] serialize(String value) {
return value.getBytes(StandardCharsets.UTF_8);
}
@Override
public String deserialize(byte[] bytes) {
return new String(bytes, StandardCharsets.UTF_8);
}
};
public static WorldPropertyDataType<Item> ITEM = new WorldPropertyDataType<>() {
@Override
public int getTypeId() {
return 3;
}
@Override
public byte[] serialize(Item value) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(8));
buf.writeId(BuiltInRegistries.ITEM, value);
return buf.accessByteBufWithCorrectSize();
}
@Override
public Item deserialize(byte[] bytes) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(bytes));
return buf.readById(BuiltInRegistries.ITEM);
}
};
public static WorldPropertyDataType<Block> BLOCK = new WorldPropertyDataType<>() {
@Override
public int getTypeId() {
return 4;
}
@Override
public byte[] serialize(Block value) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(8));
buf.writeId(BuiltInRegistries.BLOCK, value);
return buf.accessByteBufWithCorrectSize();
}
@Override
public Block deserialize(byte[] bytes) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(bytes));
return buf.readById(BuiltInRegistries.BLOCK);
}
};
public static WorldPropertyDataType<Unit> EMPTY = new WorldPropertyDataType<>() {
@Override
public int getTypeId() {
return 5;
}
@Override
public byte[] serialize(Unit value) {
return new byte[0];
}
@Override
public Unit deserialize(byte[] bytes) {
return Unit.INSTANCE;
}
};
}

Datei anzeigen

@ -0,0 +1,90 @@
package com.moulberry.axiom.world_properties;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Unit;
import java.util.List;
public interface WorldPropertyWidgetType<T> {
WorldPropertyDataType<T> dataType();
void write(FriendlyByteBuf friendlyByteBuf);
WorldPropertyWidgetType<Boolean> CHECKBOX = new WorldPropertyWidgetType<>() {
@Override
public WorldPropertyDataType<Boolean> dataType() {
return WorldPropertyDataType.BOOLEAN;
}
@Override
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeVarInt(0);
}
};
record Slider(int min, int max) implements WorldPropertyWidgetType<Integer> {
@Override
public WorldPropertyDataType<Integer> dataType() {
return WorldPropertyDataType.INTEGER;
}
@Override
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeVarInt(1);
friendlyByteBuf.writeInt(this.min);
friendlyByteBuf.writeInt(this.max);
}
}
WorldPropertyWidgetType<String> TEXTBOX = new WorldPropertyWidgetType<>() {
@Override
public WorldPropertyDataType<String> dataType() {
return WorldPropertyDataType.STRING;
}
@Override
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeVarInt(2);
}
};
WorldPropertyWidgetType<Integer> TIME = new WorldPropertyWidgetType<>() {
@Override
public WorldPropertyDataType<Integer> dataType() {
return WorldPropertyDataType.INTEGER;
}
@Override
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeVarInt(3);
}
};
WorldPropertyWidgetType<Unit> BUTTON = new WorldPropertyWidgetType<>() {
@Override
public WorldPropertyDataType<Unit> dataType() {
return WorldPropertyDataType.EMPTY;
}
@Override
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeVarInt(4);
}
};
record ButtonArray(List<String> otherButtons) implements WorldPropertyWidgetType<Integer> {
@Override
public WorldPropertyDataType<Integer> dataType() {
return WorldPropertyDataType.INTEGER;
}
@Override
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeVarInt(5);
friendlyByteBuf.writeCollection(this.otherButtons, FriendlyByteBuf::writeUtf);
}
}
}

Datei anzeigen

@ -0,0 +1,102 @@
package com.moulberry.axiom.world_properties.server;
import com.moulberry.axiom.world_properties.WorldPropertyCategory;
import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.GameRules;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class ServerWorldPropertiesRegistry {
private final LinkedHashMap<WorldPropertyCategory, List<ServerWorldProperty<?>>> propertyList = new LinkedHashMap<>();
private final Map<ResourceLocation, ServerWorldProperty<?>> propertyMap = new HashMap<>();
public ServerWorldPropertiesRegistry(World world) {
this.registerDefault(world);
}
public ServerWorldProperty<?> getById(ResourceLocation resourceLocation) {
return propertyMap.get(resourceLocation);
}
public void addCategory(WorldPropertyCategory category, List<ServerWorldProperty<?>> properties) {
this.propertyList.put(category, properties);
for (ServerWorldProperty<?> property : properties) {
ResourceLocation id = property.getId();
if (this.propertyMap.containsKey(id)) {
throw new RuntimeException("Duplicate property: " + id);
}
this.propertyMap.put(id, property);
}
}
public void registerFor(Plugin plugin, Player bukkitPlayer) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeVarInt(this.propertyList.size());
for (Map.Entry<WorldPropertyCategory, List<ServerWorldProperty<?>>> entry : this.propertyList.entrySet()) {
entry.getKey().write(buf);
buf.writeCollection(entry.getValue(), (buffer, p) -> p.write(buffer));
}
bukkitPlayer.sendPluginMessage(plugin, "axiom:register_world_properties",
buf.accessByteBufWithCorrectSize());
}
public void registerDefault(World world) {
ServerLevel serverLevel = ((CraftWorld)world).getHandle();
// Time
WorldPropertyCategory timeCategory = new WorldPropertyCategory("axiom.editorui.window.world_properties.time", true);
ServerWorldProperty<Integer> time = new ServerWorldProperty<>(new ResourceLocation("axiom:time"),
"axiom.editorui.window.world_properties.time",
true, WorldPropertyWidgetType.TIME, 0, integer -> false
);
this.addCategory(timeCategory, List.of(time));
// Weather
WorldPropertyCategory weatherCategory = new WorldPropertyCategory("axiom.editorui.window.world_properties.weather",
true);
ServerWorldProperty<Boolean> pauseWeather = new ServerWorldProperty<>(new ResourceLocation("axiom:pause_weather"),
"axiom.editorui.window.world_properties.pause_weather",
true, WorldPropertyWidgetType.CHECKBOX, !world.getGameRuleValue(GameRule.DO_WEATHER_CYCLE), bool -> {
world.setGameRule(GameRule.DO_WEATHER_CYCLE, !bool);
return false;
});
ServerWorldProperty<Integer> weatherType = new ServerWorldProperty<>(new ResourceLocation("axiom:weather_type"),
"axiom.editorui.window.world_properties.clear_weather",
true, new WorldPropertyWidgetType.ButtonArray(
List.of("axiom.editorui.window.world_properties.rain_weather", "axiom.editorui.window.world_properties.thunder_weather")
), 0, index -> {
if (index == 0) {
serverLevel.setWeatherParameters(ServerLevel.RAIN_DELAY.sample(serverLevel.random), 0, false, false);
} else if (index == 1) {
serverLevel.setWeatherParameters(0, ServerLevel.RAIN_DURATION.sample(serverLevel.random), true, false);
} else if (index == 2) {
serverLevel.setWeatherParameters(0, ServerLevel.THUNDER_DURATION.sample(serverLevel.random), true, true);
}
return false;
});
this.addCategory(weatherCategory, List.of(pauseWeather, weatherType));
}
}

Datei anzeigen

@ -0,0 +1,80 @@
package com.moulberry.axiom.world_properties.server;
import com.moulberry.axiom.AxiomPaper;
import com.moulberry.axiom.world_properties.WorldPropertyDataType;
import com.moulberry.axiom.world_properties.WorldPropertyWidgetType;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.function.Predicate;
public class ServerWorldProperty<T> {
private final ResourceLocation id;
private final String name;
private final boolean localizeName;
private WorldPropertyWidgetType<T> widget;
private T value;
private Predicate<T> handler;
public ServerWorldProperty(ResourceLocation id, String name, boolean localizeName, WorldPropertyWidgetType<T> widget,
T value, Predicate<T> handler) {
this.id = id;
this.name = name;
this.localizeName = localizeName;
this.widget = widget;
this.value = value;
this.handler = handler;
}
public ResourceLocation getId() {
return this.id;
}
public WorldPropertyDataType<T> getType() {
return this.widget.dataType();
}
public void update(World world, byte[] data) {
this.value = this.widget.dataType().deserialize(data);
if (this.handler.test(this.value)) {
this.sync(world);
}
}
public void setValueWithoutSyncing(T value) {
this.value = value;
}
public void setValue(World world, T value) {
this.value = value;
this.sync(world);
}
public void sync(World world) {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
buf.writeResourceLocation(this.id);
buf.writeVarInt(this.widget.dataType().getTypeId());
buf.writeByteArray(this.widget.dataType().serialize(this.value));
byte[] message = buf.accessByteBufWithCorrectSize();
for (Player player : world.getPlayers()) {
if (AxiomPaper.PLUGIN.activeAxiomPlayers.contains(player.getUniqueId())) {
player.sendPluginMessage(AxiomPaper.PLUGIN, "axiom:set_world_property", message);
}
}
}
public void write(FriendlyByteBuf friendlyByteBuf) {
friendlyByteBuf.writeResourceLocation(this.id);
friendlyByteBuf.writeUtf(this.name);
friendlyByteBuf.writeBoolean(this.localizeName);
this.widget.write(friendlyByteBuf);
friendlyByteBuf.writeByteArray(this.widget.dataType().serialize(this.value));
}
}