Merge branch 'version/7.2.x'

This commit is contained in:
Madeline Miller 2023-06-14 19:21:59 +10:00
commit b079da7db0
No known key found for this signature in database
GPG Key ID: B8EA2E5693115D81
28 changed files with 1059 additions and 74 deletions

View File

@ -1,3 +1,14 @@
7.2.15
- [Forge] Fix a bug preventing Forge clients lacking WorldEdit from joining Forge servers with WorldEdit in some cases
- [Forge] Added Console and CommandBlock support
- [Fabric] Added Console and CommandBlock support
- [Sponge] Added CommandBlock support
- Update to 1.20 & 1.20.1
- Fixed extend select mode being lost after using `//paste -s`, `//paste -n`, and `//cli selectworld`
- Fixed convex selection type not correctly clearing selection within WECUI protocol
- Fixed an edge case where API usages of the BlockMap type can lead to multiple values at one location
- Fixed a bug breaking the `round` expression function
7.2.14
- Update to 1.19.4
- Fixed some data values not being returned correctly in expression query functions

View File

@ -210,8 +210,8 @@ public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException {
CraftServer.class.cast(Bukkit.getServer());
int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion();
if (dataVersion != 3463) {
throw new UnsupportedClassVersionError("Not 1.20!");
if (dataVersion != 3463 && dataVersion != 3465) {
throw new UnsupportedClassVersionError("Not 1.20(.1)!");
}
serverWorldsField = CraftServer.class.getDeclaredField("worlds");

View File

@ -42,7 +42,7 @@
"api"(project(":worldedit-libs:bukkit"))
// Technically this is api, but everyone should already have some form of the bukkit API
// Avoid pulling in another one, especially one so outdated.
"localImplementation"("org.spigotmc:spigot-api:1.17-R0.1-SNAPSHOT") {
"localImplementation"("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT") {
exclude("junit", "junit")
}
@ -52,7 +52,7 @@
"localImplementation"("org.apache.logging.log4j:log4j-api")
"compileOnly"("org.jetbrains:annotations:20.1.0")
"compileOnly"("io.papermc.paper:paper-api:1.17-R0.1-SNAPSHOT") {
"compileOnly"("io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT") {
exclude("org.slf4j", "slf4j-api")
exclude("junit", "junit")
}

View File

@ -20,11 +20,8 @@
package com.sk89q.worldedit.bukkit;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.AbstractNonPlayerActor;
import com.sk89q.worldedit.extension.platform.Locatable;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extension.platform.AbstractCommandBlockActor;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.formatting.WorldEditText;
import com.sk89q.worldedit.util.formatting.text.Component;
@ -42,22 +39,17 @@
import static com.google.common.base.Preconditions.checkNotNull;
public class BukkitBlockCommandSender extends AbstractNonPlayerActor implements Locatable {
private static final String UUID_PREFIX = "CMD";
public class BukkitBlockCommandSender extends AbstractCommandBlockActor {
private final BlockCommandSender sender;
private final WorldEditPlugin plugin;
private final Location location;
private final UUID uuid;
public BukkitBlockCommandSender(WorldEditPlugin plugin, BlockCommandSender sender) {
super(BukkitAdapter.adapt(checkNotNull(sender).getBlock().getLocation()));
checkNotNull(plugin);
checkNotNull(sender);
this.plugin = plugin;
this.sender = sender;
this.location = BukkitAdapter.adapt(sender.getBlock().getLocation());
this.uuid = UUID.nameUUIDFromBytes((UUID_PREFIX + sender.getName()).getBytes(StandardCharsets.UTF_8));
}
@ -108,21 +100,6 @@ public Locale getLocale() {
return WorldEdit.getInstance().getConfiguration().defaultLocale;
}
@Override
public Location getLocation() {
return this.location;
}
@Override
public boolean setLocation(Location location) {
return false;
}
@Override
public Extent getExtent() {
return this.location.getExtent();
}
@Override
public UUID getUniqueId() {
return uuid;

View File

@ -36,7 +36,6 @@
public class BukkitConfiguration extends YAMLConfiguration {
public boolean noOpPermissions = false;
public boolean commandBlockSupport = false;
public boolean unsupportedVersionEditing = false;
@Unreported private final WorldEditPlugin plugin;
@ -49,7 +48,6 @@ public BukkitConfiguration(YAMLProcessor config, WorldEditPlugin plugin) {
public void load() {
super.load();
noOpPermissions = config.getBoolean("no-op-permissions", false);
commandBlockSupport = config.getBoolean("command-block-support", false);
unsupportedVersionEditing = "I accept that I will receive no support with this flag enabled.".equals(
config.getString("allow-editing-on-unsupported-versions", "false"));
if (unsupportedVersionEditing) {

View File

@ -30,6 +30,7 @@
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.bukkit.profile.PlayerProfile;
import java.util.HashMap;
import java.util.HashSet;
@ -144,6 +145,11 @@ public UUID getUniqueId() {
return randomUuid;
}
@Override
public PlayerProfile getPlayerProfile() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public boolean isBanned() {
throw new UnsupportedOperationException("Not supported yet.");
@ -278,4 +284,9 @@ public void decrementStatistic(Statistic statistic, EntityType entityType, int a
public void setStatistic(Statistic statistic, EntityType entityType, int newValue) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Location getLastDeathLocation() {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@ -20,14 +20,20 @@
package com.sk89q.worldedit.blocks;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.internal.Constants;
import com.sk89q.worldedit.util.gson.GsonUtil;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Represents a sign block.
@ -64,6 +70,11 @@ public SignBlock(BlockState blockState, String[] text) {
this.text = text;
}
private boolean isLegacy() {
int dataVersion = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getDataVersion();
return dataVersion < Constants.DATA_VERSION_MC_1_20;
}
/**
* Get the text.
*
@ -100,10 +111,17 @@ public String getNbtId() {
@Deprecated
public CompoundTag getNbtData() {
Map<String, Tag<?, ?>> values = new HashMap<>();
values.put("Text1", new StringTag(text[0]));
values.put("Text2", new StringTag(text[1]));
values.put("Text3", new StringTag(text[2]));
values.put("Text4", new StringTag(text[3]));
if (isLegacy()) {
values.put("Text1", new StringTag(text[0]));
values.put("Text2", new StringTag(text[1]));
values.put("Text3", new StringTag(text[2]));
values.put("Text4", new StringTag(text[3]));
} else {
ListTag<?, ?> messages = new ListTag<>(StringTag.class, Arrays.stream(text).map(StringTag::new).collect(Collectors.toList()));
Map<String, Tag<?, ?>> frontTextTag = new HashMap<>();
frontTextTag.put("messages", messages);
values.put("front_text", new CompoundTag(frontTextTag));
}
return new CompoundTag(values);
}
@ -125,24 +143,33 @@ public void setNbtData(CompoundTag rootTag) {
throw new RuntimeException(String.format("'%s' tile entity expected", getNbtId()));
}
t = values.get("Text1");
if (t instanceof StringTag) {
text[0] = ((StringTag) t).getValue();
}
if (isLegacy()) {
t = values.get("Text1");
if (t instanceof StringTag) {
text[0] = ((StringTag) t).getValue();
}
t = values.get("Text2");
if (t instanceof StringTag) {
text[1] = ((StringTag) t).getValue();
}
t = values.get("Text2");
if (t instanceof StringTag) {
text[1] = ((StringTag) t).getValue();
}
t = values.get("Text3");
if (t instanceof StringTag) {
text[2] = ((StringTag) t).getValue();
}
t = values.get("Text3");
if (t instanceof StringTag) {
text[2] = ((StringTag) t).getValue();
}
t = values.get("Text4");
if (t instanceof StringTag) {
text[3] = ((StringTag) t).getValue();
t = values.get("Text4");
if (t instanceof StringTag) {
text[3] = ((StringTag) t).getValue();
}
} else {
CompoundTag frontTextTag = (CompoundTag) values.get("front_text");
ListTag<?, ?> messagesTag = frontTextTag.getListTag("messages");
for (int i = 0; i < messagesTag.getValue().size(); i++) {
StringTag tag = (StringTag) messagesTag.getValue().get(i);
text[i] = tag.getValue();
}
}
}

View File

@ -91,6 +91,7 @@ public abstract class LocalConfiguration {
public boolean allowSymlinks = false;
public boolean serverSideCUI = true;
public boolean extendedYLimit = false;
public boolean commandBlockSupport = false;
public String defaultLocaleName = "default";
public Locale defaultLocale = Locale.getDefault();

View File

@ -0,0 +1,49 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.util.Location;
public abstract class AbstractCommandBlockActor extends AbstractNonPlayerActor implements Locatable {
protected static final String UUID_PREFIX = "CMD";
private final Location location;
public AbstractCommandBlockActor(Location location) {
this.location = location;
}
@Override
public Location getLocation() {
return this.location;
}
@Override
public boolean setLocation(Location location) {
// Can't move a CommandBlock
return false;
}
@Override
public Extent getExtent() {
return this.location.getExtent();
}
}

View File

@ -133,6 +133,7 @@ public void load() {
serverSideCUI = getBool("server-side-cui", serverSideCUI);
extendedYLimit = getBool("extended-y-limit", extendedYLimit);
setDefaultLocaleName(getString("default-locale", defaultLocaleName));
commandBlockSupport = getBool("command-block-support", commandBlockSupport);
LocalSession.MAX_HISTORY_SIZE = Math.max(15, getInt("history-size", 15));

View File

@ -108,6 +108,9 @@ public boolean generate(EditSession editSession, BlockVector3 pos) throws MaxCha
return editSession.getWorld().generateTree(this, editSession, pos.subtract(0, 1, 0));
}
},
MANGROVE("Mangrove tree", "mangrove"),
TALL_MANGROVE("Tall mangrove tree", "tall_mangrove"),
CHERRY("Cherry blossom", "cherry"),
RANDOM("Random tree", "rand", "random") {
@Override
public boolean generate(EditSession editSession, BlockVector3 pos) throws MaxChangedBlocksException {

View File

@ -129,6 +129,8 @@ public void load() {
extendedYLimit = config.getBoolean("compat.extended-y-limit", false);
setDefaultLocaleName(config.getString("default-locale", defaultLocaleName));
commandBlockSupport = config.getBoolean("command-block-support", false);
}
public void unload() {

View File

@ -35,8 +35,6 @@
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.util.Substring;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.enginehub.piston.inject.InjectedValueStore;
import org.enginehub.piston.inject.Key;
import org.enginehub.piston.inject.MapBackedValueStore;
@ -46,7 +44,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import static com.sk89q.worldedit.fabric.FabricAdapter.adaptPlayer;
import static com.sk89q.worldedit.fabric.FabricAdapter.adaptCommandSource;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
@ -62,7 +60,7 @@ public static void register(CommandDispatcher<CommandSourceStack> dispatcher, or
Command<CommandSourceStack> commandRunner = ctx -> {
WorldEdit.getInstance().getEventBus().post(new com.sk89q.worldedit.event.platform.CommandEvent(
adaptPlayer(ctx.getSource().getPlayerOrException()),
adaptCommandSource(ctx.getSource()),
"/" + ctx.getInput()
));
return 0;
@ -82,12 +80,8 @@ public static void register(CommandDispatcher<CommandSourceStack> dispatcher, or
private static Predicate<CommandSourceStack> requirementsFor(org.enginehub.piston.Command mapping) {
return ctx -> {
final Entity entity = ctx.getEntity();
if (!(entity instanceof ServerPlayer)) {
return true;
}
final Actor actor = FabricAdapter.adaptPlayer(((ServerPlayer) entity));
InjectedValueStore store = MapBackedValueStore.create();
final Actor actor = FabricAdapter.adaptCommandSource(ctx);
store.injectValue(Key.of(Actor.class), context -> Optional.of(actor));
return mapping.getCondition().satisfied(store);
};
@ -96,7 +90,7 @@ private static Predicate<CommandSourceStack> requirementsFor(org.enginehub.pisto
private static CompletableFuture<Suggestions> suggest(CommandContext<CommandSourceStack> context,
SuggestionsBuilder builder) throws CommandSyntaxException {
CommandSuggestionEvent event = new CommandSuggestionEvent(
FabricAdapter.adaptPlayer(context.getSource().getPlayerOrException()),
FabricAdapter.adaptCommandSource(context.getSource()),
builder.getInput()
);
WorldEdit.getInstance().getEventBus().post(event);

View File

@ -20,6 +20,7 @@
package com.sk89q.worldedit.fabric;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.fabric.internal.FabricTransmogrifier;
import com.sk89q.worldedit.fabric.internal.NBTConverter;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
@ -37,6 +38,7 @@
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
@ -44,6 +46,7 @@
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BaseCommandBlock;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -260,4 +263,22 @@ public static FabricPlayer adaptPlayer(ServerPlayer player) {
checkNotNull(player);
return new FabricPlayer(player);
}
/**
* Get the WorldEdit proxy for the given command source.
*
* @param commandSourceStack the command source
* @return the WorldEdit actor
*/
public static Actor adaptCommandSource(CommandSourceStack commandSourceStack) {
checkNotNull(commandSourceStack);
if (commandSourceStack.isPlayer()) {
return adaptPlayer(commandSourceStack.getPlayer());
}
if (FabricWorldEdit.inst.getConfig().commandBlockSupport && commandSourceStack.source instanceof BaseCommandBlock commandBlock) {
return new FabricBlockCommandSender(commandBlock);
}
return new FabricCommandSender(commandSourceStack);
}
}

View File

@ -0,0 +1,177 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.fabric;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.AbstractCommandBlockActor;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.formatting.WorldEditText;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.BaseCommandBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class FabricBlockCommandSender extends AbstractCommandBlockActor {
private final BaseCommandBlock sender;
private final UUID uuid;
public FabricBlockCommandSender(BaseCommandBlock sender) {
super(new Location(FabricAdapter.adapt(checkNotNull(sender).getLevel()), FabricAdapter.adapt(sender.getPosition())));
this.sender = sender;
this.uuid = UUID.nameUUIDFromBytes((UUID_PREFIX + sender.getName()).getBytes(StandardCharsets.UTF_8));
}
@Override
public String getName() {
return sender.getName().getString();
}
@Override
@Deprecated
public void printRaw(String msg) {
for (String part : msg.split("\n")) {
sendMessage(net.minecraft.network.chat.Component.literal(part));
}
}
@Override
@Deprecated
public void printDebug(String msg) {
sendColorized(msg, ChatFormatting.GRAY);
}
@Override
@Deprecated
public void print(String msg) {
sendColorized(msg, ChatFormatting.LIGHT_PURPLE);
}
@Override
@Deprecated
public void printError(String msg) {
sendColorized(msg, ChatFormatting.RED);
}
@Override
public void print(Component component) {
sendMessage(net.minecraft.network.chat.Component.Serializer.fromJson(
GsonComponentSerializer.INSTANCE.serialize(WorldEditText.format(component, getLocale()))
));
}
private void sendColorized(String msg, ChatFormatting formatting) {
for (String part : msg.split("\n")) {
var component = net.minecraft.network.chat.Component.literal(part);
component.withStyle(formatting);
sendMessage(component);
}
}
private void sendMessage(net.minecraft.network.chat.Component textComponent) {
this.sender.sendSystemMessage(textComponent);
}
@Override
public Locale getLocale() {
return WorldEdit.getInstance().getConfiguration().defaultLocale;
}
@Override
public UUID getUniqueId() {
return uuid;
}
@Override
public String[] getGroups() {
return new String[0];
}
@Override
public void checkPermission(String permission) throws AuthorizationException {
if (!hasPermission(permission)) {
throw new AuthorizationException();
}
}
@Override
public boolean hasPermission(String permission) {
return true;
}
public BaseCommandBlock getSender() {
return this.sender;
}
@Override
public SessionKey getSessionKey() {
return new SessionKey() {
private volatile boolean active = true;
private void updateActive() {
BlockPos pos = new BlockPos((int) sender.getPosition().x, (int) sender.getPosition().y, (int) sender.getPosition().z);
int chunkX = SectionPos.blockToSectionCoord(pos.getX());
int chunkZ = SectionPos.blockToSectionCoord(pos.getZ());
if (!sender.getLevel().getChunkSource().hasChunk(chunkX, chunkZ)) {
active = false;
return;
}
Block type = sender.getLevel().getBlockState(pos).getBlock();
active = type == Blocks.COMMAND_BLOCK
|| type == Blocks.CHAIN_COMMAND_BLOCK
|| type == Blocks.REPEATING_COMMAND_BLOCK;
}
@Override
public String getName() {
return sender.getName().getString();
}
@Override
public boolean isActive() {
getSender().getLevel().getServer().execute(this::updateActive);
return active;
}
@Override
public boolean isPersistent() {
return true;
}
@Override
public UUID getUniqueId() {
return uuid;
}
};
}
}

View File

@ -0,0 +1,157 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.fabric;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.AbstractNonPlayerActor;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.formatting.WorldEditText;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import java.util.Locale;
import java.util.UUID;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class FabricCommandSender extends AbstractNonPlayerActor {
/**
* One time generated ID.
*/
private static final UUID DEFAULT_ID = UUID.fromString("a233eb4b-4cab-42cd-9fd9-7e7b9a3f74be");
private final CommandSourceStack sender;
public FabricCommandSender(CommandSourceStack sender) {
checkNotNull(sender);
checkArgument(!sender.isPlayer(), "Cannot wrap a player");
this.sender = sender;
}
@Override
public UUID getUniqueId() {
return DEFAULT_ID;
}
@Override
public String getName() {
return sender.getTextName();
}
@Override
@Deprecated
public void printRaw(String msg) {
for (String part : msg.split("\n")) {
sendMessage(net.minecraft.network.chat.Component.literal(part));
}
}
@Override
@Deprecated
public void printDebug(String msg) {
sendColorized(msg, ChatFormatting.GRAY);
}
@Override
@Deprecated
public void print(String msg) {
sendColorized(msg, ChatFormatting.LIGHT_PURPLE);
}
@Override
@Deprecated
public void printError(String msg) {
sendColorized(msg, ChatFormatting.RED);
}
@Override
public void print(Component component) {
sendMessage(net.minecraft.network.chat.Component.Serializer.fromJson(
GsonComponentSerializer.INSTANCE.serialize(WorldEditText.format(component, getLocale()))
));
}
private void sendColorized(String msg, ChatFormatting formatting) {
for (String part : msg.split("\n")) {
var component = net.minecraft.network.chat.Component.literal(part);
component.withStyle(formatting);
sendMessage(component);
}
}
private void sendMessage(net.minecraft.network.chat.Component textComponent) {
this.sender.sendSystemMessage(textComponent);
}
@Override
public String[] getGroups() {
return new String[0];
}
@Override
public boolean hasPermission(String perm) {
return true;
}
@Override
public void checkPermission(String permission) {
}
@Override
public Locale getLocale() {
return WorldEdit.getInstance().getConfiguration().defaultLocale;
}
public CommandSourceStack getSender() {
return this.sender;
}
@Override
public SessionKey getSessionKey() {
return new SessionKey() {
@Nullable
@Override
public String getName() {
return sender.getTextName();
}
@Override
public boolean isActive() {
return true;
}
@Override
public boolean isPersistent() {
return true;
}
@Override
public UUID getUniqueId() {
return DEFAULT_ID;
}
};
}
}

View File

@ -457,6 +457,9 @@ private List<CompletableFuture<ChunkAccess>> submitChunkLoadTasks(Region region,
case WARPED_FUNGUS -> TreeFeatures.WARPED_FUNGUS;
case CRIMSON_FUNGUS -> TreeFeatures.CRIMSON_FUNGUS;
case CHORUS_PLANT -> EndFeatures.CHORUS_PLANT;
case MANGROVE -> TreeFeatures.MANGROVE;
case TALL_MANGROVE -> TreeFeatures.TALL_MANGROVE;
case CHERRY -> TreeFeatures.CHERRY;
case RANDOM -> createTreeFeatureGenerator(TreeType.values()[ThreadLocalRandom.current().nextInt(TreeType.values().length)]);
default -> null;
};

View File

@ -1,2 +1,3 @@
accessWidener v2 named
accessible class net/minecraft/server/level/ServerChunkCache$MainThreadExecutor
accessible field net/minecraft/commands/CommandSourceStack source Lnet/minecraft/commands/CommandSource;

View File

@ -35,8 +35,6 @@
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.internal.util.Substring;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import org.enginehub.piston.inject.InjectedValueStore;
import org.enginehub.piston.inject.Key;
import org.enginehub.piston.inject.MapBackedValueStore;
@ -77,11 +75,7 @@ public static void register(CommandDispatcher<CommandSourceStack> dispatcher, or
private static Predicate<CommandSourceStack> requirementsFor(org.enginehub.piston.Command mapping) {
return ctx -> {
final Entity entity = ctx.getEntity();
if (!(entity instanceof ServerPlayer)) {
return true;
}
final Actor actor = ForgeAdapter.adaptPlayer(((ServerPlayer) entity));
final Actor actor = ForgeAdapter.adaptCommandSource(ctx);
InjectedValueStore store = MapBackedValueStore.create();
store.injectValue(Key.of(Actor.class), context -> Optional.of(actor));
return mapping.getCondition().satisfied(store);
@ -91,7 +85,7 @@ private static Predicate<CommandSourceStack> requirementsFor(org.enginehub.pisto
private static CompletableFuture<Suggestions> suggest(CommandContext<CommandSourceStack> context,
SuggestionsBuilder builder) throws CommandSyntaxException {
CommandSuggestionEvent event = new CommandSuggestionEvent(
ForgeAdapter.adaptPlayer(context.getSource().getPlayerOrException()),
ForgeAdapter.adaptCommandSource(context.getSource()),
builder.getInput()
);
WorldEdit.getInstance().getEventBus().post(event);

View File

@ -20,6 +20,7 @@
package com.sk89q.worldedit.forge;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.forge.internal.ForgeTransmogrifier;
import com.sk89q.worldedit.forge.internal.NBTConverter;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
@ -36,6 +37,7 @@
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
@ -44,6 +46,7 @@
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BaseCommandBlock;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
@ -247,4 +250,22 @@ public static ForgePlayer adaptPlayer(ServerPlayer player) {
checkNotNull(player);
return new ForgePlayer(player);
}
/**
* Get the WorldEdit proxy for the given command source.
*
* @param commandSourceStack the command source
* @return the WorldEdit actor
*/
public static Actor adaptCommandSource(CommandSourceStack commandSourceStack) {
checkNotNull(commandSourceStack);
if (commandSourceStack.isPlayer()) {
return adaptPlayer(commandSourceStack.getPlayer());
}
if (ForgeWorldEdit.inst.getConfig().commandBlockSupport && commandSourceStack.source instanceof BaseCommandBlock commandBlock) {
return new ForgeBlockCommandSender(commandBlock);
}
return new ForgeCommandSender(commandSourceStack);
}
}

View File

@ -0,0 +1,179 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.forge;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.AbstractCommandBlockActor;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.formatting.WorldEditText;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.BaseCommandBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.common.util.LogicalSidedProvider;
import net.minecraftforge.fml.LogicalSide;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class ForgeBlockCommandSender extends AbstractCommandBlockActor {
private final BaseCommandBlock sender;
private final UUID uuid;
public ForgeBlockCommandSender(BaseCommandBlock sender) {
super(new Location(ForgeAdapter.adapt(checkNotNull(sender).getLevel()), ForgeAdapter.adapt(sender.getPosition())));
this.sender = sender;
this.uuid = UUID.nameUUIDFromBytes((UUID_PREFIX + sender.getName()).getBytes(StandardCharsets.UTF_8));
}
@Override
public String getName() {
return sender.getName().getString();
}
@Override
@Deprecated
public void printRaw(String msg) {
for (String part : msg.split("\n")) {
sendMessage(net.minecraft.network.chat.Component.literal(part));
}
}
@Override
@Deprecated
public void printDebug(String msg) {
sendColorized(msg, ChatFormatting.GRAY);
}
@Override
@Deprecated
public void print(String msg) {
sendColorized(msg, ChatFormatting.LIGHT_PURPLE);
}
@Override
@Deprecated
public void printError(String msg) {
sendColorized(msg, ChatFormatting.RED);
}
@Override
public void print(Component component) {
sendMessage(net.minecraft.network.chat.Component.Serializer.fromJson(
GsonComponentSerializer.INSTANCE.serialize(WorldEditText.format(component, getLocale()))
));
}
private void sendColorized(String msg, ChatFormatting formatting) {
for (String part : msg.split("\n")) {
var component = net.minecraft.network.chat.Component.literal(part);
component.withStyle(formatting);
sendMessage(component);
}
}
private void sendMessage(net.minecraft.network.chat.Component textComponent) {
this.sender.sendSystemMessage(textComponent);
}
@Override
public Locale getLocale() {
return WorldEdit.getInstance().getConfiguration().defaultLocale;
}
@Override
public UUID getUniqueId() {
return uuid;
}
@Override
public String[] getGroups() {
return new String[0];
}
@Override
public void checkPermission(String permission) throws AuthorizationException {
if (!hasPermission(permission)) {
throw new AuthorizationException();
}
}
@Override
public boolean hasPermission(String permission) {
return true;
}
public BaseCommandBlock getSender() {
return this.sender;
}
@Override
public SessionKey getSessionKey() {
return new SessionKey() {
private volatile boolean active = true;
private void updateActive() {
BlockPos pos = new BlockPos((int) sender.getPosition().x, (int) sender.getPosition().y, (int) sender.getPosition().z);
int chunkX = SectionPos.blockToSectionCoord(pos.getX());
int chunkZ = SectionPos.blockToSectionCoord(pos.getZ());
if (!sender.getLevel().getChunkSource().hasChunk(chunkX, chunkZ)) {
active = false;
return;
}
Block type = sender.getLevel().getBlockState(pos).getBlock();
active = type == Blocks.COMMAND_BLOCK
|| type == Blocks.CHAIN_COMMAND_BLOCK
|| type == Blocks.REPEATING_COMMAND_BLOCK;
}
@Override
public String getName() {
return sender.getName().getString();
}
@Override
public boolean isActive() {
LogicalSidedProvider.WORKQUEUE.get(LogicalSide.SERVER).submitAsync(this::updateActive);
return active;
}
@Override
public boolean isPersistent() {
return true;
}
@Override
public UUID getUniqueId() {
return uuid;
}
};
}
}

View File

@ -0,0 +1,157 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.forge;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.AbstractNonPlayerActor;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.formatting.WorldEditText;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import java.util.Locale;
import java.util.UUID;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class ForgeCommandSender extends AbstractNonPlayerActor {
/**
* One time generated ID.
*/
private static final UUID DEFAULT_ID = UUID.fromString("a233eb4b-4cab-42cd-9fd9-7e7b9a3f74be");
private final CommandSourceStack sender;
public ForgeCommandSender(CommandSourceStack sender) {
checkNotNull(sender);
checkArgument(!sender.isPlayer(), "Cannot wrap a player");
this.sender = sender;
}
@Override
public UUID getUniqueId() {
return DEFAULT_ID;
}
@Override
public String getName() {
return sender.getTextName();
}
@Override
@Deprecated
public void printRaw(String msg) {
for (String part : msg.split("\n")) {
sendMessage(net.minecraft.network.chat.Component.literal(part));
}
}
@Override
@Deprecated
public void printDebug(String msg) {
sendColorized(msg, ChatFormatting.GRAY);
}
@Override
@Deprecated
public void print(String msg) {
sendColorized(msg, ChatFormatting.LIGHT_PURPLE);
}
@Override
@Deprecated
public void printError(String msg) {
sendColorized(msg, ChatFormatting.RED);
}
@Override
public void print(Component component) {
sendMessage(net.minecraft.network.chat.Component.Serializer.fromJson(
GsonComponentSerializer.INSTANCE.serialize(WorldEditText.format(component, getLocale()))
));
}
private void sendColorized(String msg, ChatFormatting formatting) {
for (String part : msg.split("\n")) {
var component = net.minecraft.network.chat.Component.literal(part);
component.withStyle(formatting);
sendMessage(component);
}
}
private void sendMessage(net.minecraft.network.chat.Component textComponent) {
this.sender.sendSystemMessage(textComponent);
}
@Override
public String[] getGroups() {
return new String[0];
}
@Override
public boolean hasPermission(String perm) {
return true;
}
@Override
public void checkPermission(String permission) {
}
@Override
public Locale getLocale() {
return WorldEdit.getInstance().getConfiguration().defaultLocale;
}
public CommandSourceStack getSender() {
return this.sender;
}
@Override
public SessionKey getSessionKey() {
return new SessionKey() {
@Nullable
@Override
public String getName() {
return sender.getTextName();
}
@Override
public boolean isActive() {
return true;
}
@Override
public boolean isPersistent() {
return true;
}
@Override
public UUID getUniqueId() {
return DEFAULT_ID;
}
};
}
}

View File

@ -442,6 +442,9 @@ private List<CompletableFuture<ChunkAccess>> submitChunkLoadTasks(Region region,
case WARPED_FUNGUS -> TreeFeatures.WARPED_FUNGUS;
case CRIMSON_FUNGUS -> TreeFeatures.CRIMSON_FUNGUS;
case CHORUS_PLANT -> EndFeatures.CHORUS_PLANT;
case MANGROVE -> TreeFeatures.MANGROVE;
case TALL_MANGROVE -> TreeFeatures.TALL_MANGROVE;
case CHERRY -> TreeFeatures.CHERRY;
case RANDOM -> createTreeFeatureGenerator(TreeType.values()[ThreadLocalRandom.current().nextInt(TreeType.values().length)]);
default -> null;
};

View File

@ -89,6 +89,7 @@
import java.util.function.Supplier;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.sk89q.worldedit.forge.ForgeAdapter.adaptCommandSource;
import static com.sk89q.worldedit.forge.ForgeAdapter.adaptPlayer;
import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME;
@ -332,10 +333,7 @@ public void onPlayerInteract(PlayerInteractEvent event) {
@SubscribeEvent
public void onCommandEvent(CommandEvent event) throws CommandSyntaxException {
ParseResults<CommandSourceStack> parseResults = event.getParseResults();
if (!(parseResults.getContext().getSource().getEntity() instanceof ServerPlayer player)) {
return;
}
if (player.level().isClientSide) {
if (parseResults.getContext().getSource().getEntity() instanceof ServerPlayer player && player.level().isClientSide) {
return;
}
if (parseResults.getContext().getCommand() != CommandWrapper.FAKE_COMMAND) {
@ -343,7 +341,7 @@ public void onCommandEvent(CommandEvent event) throws CommandSyntaxException {
}
event.setCanceled(true);
WorldEdit.getInstance().getEventBus().post(new com.sk89q.worldedit.event.platform.CommandEvent(
adaptPlayer(parseResults.getContext().getSource().getPlayerOrException()),
adaptCommandSource(parseResults.getContext().getSource()),
"/" + parseResults.getReader().getString()
));
}

View File

@ -0,0 +1,183 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.sponge;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.AbstractCommandBlockActor;
import com.sk89q.worldedit.session.SessionKey;
import com.sk89q.worldedit.util.auth.AuthorizationException;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.util.formatting.text.TextComponent;
import com.sk89q.worldedit.util.formatting.text.format.TextColor;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.block.BlockState;
import org.spongepowered.api.block.BlockType;
import org.spongepowered.api.block.BlockTypes;
import org.spongepowered.api.block.entity.CommandBlock;
import org.spongepowered.api.data.Keys;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.util.Ticks;
import org.spongepowered.math.vector.Vector3d;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class SpongeBlockCommandSender extends AbstractCommandBlockActor {
private final SpongeWorldEdit worldEdit;
private final CommandBlock sender;
private final UUID uuid;
public SpongeBlockCommandSender(SpongeWorldEdit worldEdit, CommandBlock sender) {
super(SpongeAdapter.adapt(checkNotNull(sender).serverLocation(), Vector3d.ZERO));
checkNotNull(worldEdit);
this.worldEdit = worldEdit;
this.sender = sender;
this.uuid = UUID.nameUUIDFromBytes((UUID_PREFIX + sender.name()).getBytes(StandardCharsets.UTF_8));
}
@Override
public String getName() {
return sender.name();
}
@Override
@Deprecated
public void printRaw(String msg) {
for (String part : msg.split("\n")) {
sendMessage(net.kyori.adventure.text.Component.text(part));
}
}
@Override
@Deprecated
public void print(String msg) {
for (String part : msg.split("\n")) {
print(TextComponent.of(part, TextColor.LIGHT_PURPLE));
}
}
@Override
@Deprecated
public void printDebug(String msg) {
for (String part : msg.split("\n")) {
print(TextComponent.of(part, TextColor.GRAY));
}
}
@Override
@Deprecated
public void printError(String msg) {
for (String part : msg.split("\n")) {
print(TextComponent.of(part, TextColor.RED));
}
}
@Override
public void print(Component component) {
sendMessage(SpongeTextAdapter.convert(component, getLocale()));
}
private void sendMessage(net.kyori.adventure.text.Component textComponent) {
this.sender.offer(Keys.LAST_COMMAND_OUTPUT, textComponent);
}
@Override
public Locale getLocale() {
return WorldEdit.getInstance().getConfiguration().defaultLocale;
}
@Override
public UUID getUniqueId() {
return uuid;
}
@Override
public String[] getGroups() {
return new String[0];
}
@Override
public void checkPermission(String permission) throws AuthorizationException {
if (!hasPermission(permission)) {
throw new AuthorizationException();
}
}
@Override
public boolean hasPermission(String permission) {
return sender.hasPermission(permission);
}
public CommandBlock getSender() {
return this.sender;
}
@Override
public SessionKey getSessionKey() {
return new SessionKey() {
private volatile boolean active = true;
private void updateActive() {
BlockState block = sender.block();
if (!sender.serverLocation().world().isChunkLoadedAtBlock(sender.blockPosition(), false)) {
active = false;
return;
}
BlockType type = block.type();
active = type == BlockTypes.COMMAND_BLOCK.get()
|| type == BlockTypes.CHAIN_COMMAND_BLOCK.get()
|| type == BlockTypes.REPEATING_COMMAND_BLOCK.get();
}
@Override
public String getName() {
return sender.name();
}
@Override
public boolean isActive() {
if (Sponge.server().onMainThread()) {
// we can update eagerly
updateActive();
} else {
// we should update it eventually
Task task = Task.builder().delay(Ticks.zero()).plugin(worldEdit.getPluginContainer()).execute(this::updateActive).build();
Sponge.server().scheduler().submit(task);
}
return active;
}
@Override
public boolean isPersistent() {
return true;
}
@Override
public UUID getUniqueId() {
return uuid;
}
};
}
}

View File

@ -316,6 +316,9 @@ public boolean regenerate(Region region, Extent extent, RegenOptions options) {
case WARPED_FUNGUS -> TreeFeatures.WARPED_FUNGUS;
case CRIMSON_FUNGUS -> TreeFeatures.CRIMSON_FUNGUS;
case CHORUS_PLANT -> EndFeatures.CHORUS_PLANT;
case MANGROVE -> TreeFeatures.MANGROVE;
case TALL_MANGROVE -> TreeFeatures.TALL_MANGROVE;
case CHERRY -> TreeFeatures.CHERRY;
case RANDOM -> createTreeFeatureGenerator(TreeGenerator.TreeType.values()[ThreadLocalRandom.current().nextInt(TreeGenerator.TreeType.values().length)]);
default -> null;
};

View File

@ -48,6 +48,8 @@
import org.spongepowered.api.block.BlockSnapshot;
import org.spongepowered.api.block.BlockType;
import org.spongepowered.api.block.BlockTypes;
import org.spongepowered.api.block.entity.BlockEntity;
import org.spongepowered.api.block.entity.CommandBlock;
import org.spongepowered.api.command.Command;
import org.spongepowered.api.command.CommandCause;
import org.spongepowered.api.command.CommandCompletion;
@ -67,6 +69,7 @@
import org.spongepowered.api.event.network.ServerSideConnectionEvent;
import org.spongepowered.api.registry.RegistryTypes;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.world.LocatableBlock;
import org.spongepowered.api.world.server.ServerLocation;
import org.spongepowered.api.world.server.ServerWorld;
import org.spongepowered.math.vector.Vector3d;
@ -376,6 +379,15 @@ public Actor wrapCommandCause(CommandCause cause) {
if (rootCause instanceof ServerPlayer) {
return SpongeAdapter.adapt((ServerPlayer) rootCause);
}
if (rootCause instanceof LocatableBlock locatableBlock) {
Optional<? extends BlockEntity> optionalBlockEntity = locatableBlock.world().blockEntity(locatableBlock.blockPosition());
if (optionalBlockEntity.isPresent()) {
BlockEntity blockEntity = optionalBlockEntity.get();
if (blockEntity instanceof CommandBlock commandBlock) {
return new SpongeBlockCommandSender(this, commandBlock);
}
}
}
if (rootCause instanceof Audience) {
return new SpongeCommandSender((Audience) rootCause);
}

View File

@ -143,5 +143,7 @@ public void load() {
extendedYLimit = node.node("compat", "extended-y-limit").getBoolean(false);
setDefaultLocaleName(node.node("default-locale").getString(defaultLocaleName));
commandBlockSupport = node.node("command-block-support").getBoolean(false);
}
}