Add SpigotCore module

This commit is contained in:
2024-08-04 21:37:50 +02:00
parent aeef0f6e49
commit b97890fe7a
110 changed files with 11957 additions and 1 deletions
@@ -0,0 +1,98 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.comphenix.tinyprotocol.Reflection;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.BaseComponent;
import net.minecraft.server.v1_8_R3.ChatComponentText;
import net.minecraft.server.v1_8_R3.MathHelper;
import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
import org.bukkit.Sound;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
public class BountifulWrapper8 implements BountifulWrapper.IBountifulWrapper {
@Override
public void playPling(Player player) {
player.playSound(player.getLocation(), Sound.ORB_PICKUP, 1, 1);
}
@Override
public void sendMessage(Player player, ChatMessageType type, BaseComponent... msg) {
((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(BaseComponent.toLegacyText(msg)), (byte)type.ordinal()));
}
@Override
public Object getDataWatcherObject(int index, Class<?> type) {
return index;
}
private static final Class<?> watchableObject = Reflection.getClass("{nms}.DataWatcher$WatchableObject");
private static final Reflection.ConstructorInvoker watchableObjectConstructor = Reflection.getConstructor(watchableObject, int.class, int.class, Object.class);
private static final Map<Class<?>, Integer> watchableDatatypes = new HashMap<>();
static {
watchableDatatypes.put(byte.class, 0);
watchableDatatypes.put(short.class, 1);
watchableDatatypes.put(int.class, 2);
watchableDatatypes.put(float.class, 3);
watchableDatatypes.put(String.class, 4);
}
@Override
public Object getDataWatcherItem(Object dwo, Object value) {
return watchableObjectConstructor.invoke(watchableDatatypes.get(value.getClass()), dwo, value);
}
@Override
public BountifulWrapper.PositionSetter getPositionSetter(Class<?> packetClass, int fieldOffset) {
Reflection.FieldAccessor<Integer> posX = Reflection.getField(packetClass, int.class, fieldOffset);
Reflection.FieldAccessor<Integer> posY = Reflection.getField(packetClass, int.class, fieldOffset +1);
Reflection.FieldAccessor<Integer> posZ = Reflection.getField(packetClass, int.class, fieldOffset +2);
return (packet, x, y, z) -> {
posX.set(packet, MathHelper.floor(x * 32));
posY.set(packet, MathHelper.floor(y * 32));
posZ.set(packet, MathHelper.floor(z * 32));
};
}
@Override
public BountifulWrapper.PositionSetter getRelMoveSetter(Class<?> packetClass) {
Reflection.FieldAccessor<?> moveX = Reflection.getField(packetClass, "b", byte.class);
Reflection.FieldAccessor<?> moveY = Reflection.getField(packetClass, "c", byte.class);
Reflection.FieldAccessor<?> moveZ = Reflection.getField(packetClass, "d", byte.class);
return (packet, x, y, z) -> {
moveX.set(packet, (byte)(x*32));
moveY.set(packet, (byte)(y*32));
moveZ.set(packet, (byte)(z*32));
};
}
@Override
public BountifulWrapper.UUIDSetter getUUIDSetter(Class<?> packetClass) {
return (packet, uuid) -> {};
}
}
@@ -0,0 +1,51 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.comphenix.tinyprotocol.Reflection;
import java.util.ArrayList;
import java.util.List;
public class ChatWrapper8 implements ChatWrapper {
private static final Reflection.ConstructorInvoker chatComponentConstructor = Reflection.getConstructor(Reflection.getClass("{nms.network.chat}.ChatComponentText"), String.class);
@Override
public Object stringToChatComponent(String text) {
return chatComponentConstructor.invoke(text);
}
private static final Class<?> metadataPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutEntityMetadata");
private static final Reflection.FieldAccessor<Integer> metadataEntity = Reflection.getField(metadataPacket, int.class, 0);
private static final Reflection.FieldAccessor<List> metadataMetadata = Reflection.getField(metadataPacket, List.class, 0);
@Override
public Object getDataWatcherPacket(int entityId, Object... dataWatcherKeyValues) {
Object packet = Reflection.newInstance(metadataPacket);
metadataEntity.set(packet, entityId);
ArrayList<Object> nativeWatchers = new ArrayList<>(1);
for(int i = 0; i < dataWatcherKeyValues.length; i+=2) {
nativeWatchers.add(BountifulWrapper.impl.getDataWatcherItem(dataWatcherKeyValues[i], dataWatcherKeyValues[i+1]));
}
metadataMetadata.set(packet, nativeWatchers);
return packet;
}
}
@@ -0,0 +1,33 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import net.minecraft.server.v1_8_R3.PacketPlayOutMapChunk;
import org.bukkit.craftbukkit.v1_8_R3.CraftChunk;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
public class CraftbukkitWrapper8 implements CraftbukkitWrapper.ICraftbukkitWrapper {
@Override
public void sendChunk(Player p, int chunkX, int chunkZ) {
((CraftPlayer)p).getHandle().playerConnection.sendPacket(new PacketPlayOutMapChunk(((CraftChunk)p.getWorld().getChunkAt(chunkX, chunkZ)).getHandle(), true, 65535));
}
}
@@ -0,0 +1,121 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.comphenix.tinyprotocol.Reflection;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.HashMap;
import java.util.Map;
public class FlatteningWrapper8 implements FlatteningWrapper.IFlatteningWrapper {
private static final Reflection.FieldAccessor<String> scoreboardName = Reflection.getField(FlatteningWrapper.scoreboardObjective, String.class, 1);
private static final Class<?> scoreActionEnum = Reflection.getClass("{nms}.PacketPlayOutScoreboardScore$EnumScoreboardAction");
private static final Reflection.FieldAccessor<?> scoreAction = Reflection.getField(FlatteningWrapper.scoreboardScore, scoreActionEnum, 0);
private static final Object scoreActionChange = scoreActionEnum.getEnumConstants()[0];
@Override
public void setScoreboardTitle(Object packet, String title) {
scoreboardName.set(packet, title);
}
@Override
public void setScoreAction(Object packet) {
scoreAction.set(packet, scoreActionChange);
}
@Override
public Material getMaterial(String material) {
try{
return Material.valueOf(material);
}catch(IllegalArgumentException e){
return Material.STONE;
}
}
@Override
public Material getDye(int colorCode) {
return Material.INK_SACK;
}
@Override
public ItemStack setSkullOwner(String player) {
ItemStack head = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
SkullMeta headmeta = (SkullMeta) head.getItemMeta();
headmeta.setOwner(player.startsWith(".") ? player.substring(1) : player);
headmeta.setDisplayName(player);
head.setItemMeta(headmeta);
return head;
}
@Override
public Object getPose(FlatteningWrapper.EntityPose pose) {
return Byte.valueOf((byte)(pose == FlatteningWrapper.EntityPose.SNEAKING ? 2 : 0));
}
private static final Class<?> dataWatcher = Reflection.getClass("{nms}.DataWatcher");
private static final Class<?> namedSpawnPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutNamedEntitySpawn");
private static final Reflection.FieldAccessor<?> namedSpawnDataWatcher = Reflection.getField(namedSpawnPacket, dataWatcher, 0);
private static final Class<?> entity = Reflection.getClass("{nms}.Entity");
private static final Reflection.ConstructorInvoker dataWatcherConstructor = Reflection.getConstructor(dataWatcher, entity);
@Override
public void setNamedSpawnPacketDataWatcher(Object packet) {
namedSpawnDataWatcher.set(packet, dataWatcherConstructor.invoke((Object) null));
}
@Override
public Object formatDisplayName(String displayName) {
return displayName != null ? displayName : "";
}
private static final Reflection.FieldAccessor<?> spawnType = Reflection.getField(ProtocolWrapper.spawnPacket, int.class, Core.getVersion() > 8 ? 6 : 9);
private static final Reflection.FieldAccessor<Integer> spawnLivingType = Reflection.getField(ProtocolWrapper.spawnLivingPacket, int.class, 1);
private static final Map<EntityType, Object> types = new HashMap<>();
static {
types.put(EntityType.PRIMED_TNT, 50);
types.put(EntityType.ARMOR_STAND, 30);
types.put(EntityType.ARROW, 60);
types.put(EntityType.FIREBALL, 63);
types.put(EntityType.ITEM_FRAME, 18);
types.put(EntityType.FALLING_BLOCK, 21);
}
@Override
public void setSpawnPacketType(Object packet, EntityType type) {
(type.isAlive() ? spawnLivingType : spawnType).set(packet, types.get(type));
}
@Override
public int getViewDistance(Player player) {
return 10;
}
private static final Reflection.MethodInvoker save = Reflection.getMethod("{obc}.CraftWorld", "save", boolean.class);
@Override
public void syncSave(World world) {
save.invoke(world, true);
}
}
@@ -0,0 +1,113 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;
public class IDConverter8 {
private final Map<String, Set<String>> availibleAttributes;
private final Map<String, Map<Set<String>, BlockTypeID>> map;
public IDConverter8() {
Map<String, Map<Set<String>, BlockTypeID>> map = new HashMap<>();
YamlConfiguration legacy = YamlConfiguration.loadConfiguration(new InputStreamReader(Objects.requireNonNull(IDConverter8.class.getClassLoader().getResourceAsStream("legacy.yml"))));
for(String blockString : legacy.getKeys(false)){
String[] legacyBlockId = legacy.getString(blockString).split(":");
map.computeIfAbsent(getBlockId(blockString), bId -> {
Map<Set<String>, BlockTypeID> attributeMap = new HashMap<>();
attributeMap.put(new HashSet<>(), new BlockTypeID(legacyBlockId[0], "0"));
return attributeMap;
}).put(getAttributes(blockString), new BlockTypeID(legacyBlockId[0], legacyBlockId[1]));
}
this.map = map;
Map<String, Set<String>> availableAttributes = new HashMap<>();
for (Map.Entry<String, Map<Set<String>, BlockTypeID>> entry : map.entrySet()) {
availableAttributes.put(entry.getKey(), entry.getValue().keySet().stream().flatMap(Collection::stream).collect(Collectors.toSet()));
}
this.availibleAttributes = availableAttributes;
}
public BlockTypeID getId(String blockString) {
String blockId = getBlockId(blockString);
Map<Set<String>, IDConverter8.BlockTypeID> attributeMap = map.get(blockId);
if(attributeMap == null) { // Block nonexistent pre-flattening
return new BlockTypeID("0", "0");
}
Set<String> attributes = getAttributes(blockString);
Set<String> knownAttributes = this.availibleAttributes.get(blockId);
attributes.removeIf(attribute -> !knownAttributes.contains(attribute));
long bestMatch = -1;
BlockTypeID blockID = null;
for(Map.Entry<Set<String>, BlockTypeID> entry : attributeMap.entrySet()) {
Set<String> attrs = entry.getKey();
if(attrs.size() <= bestMatch)
continue;
long matching = attributes.stream().filter(attrs::contains).count();
if(matching > bestMatch) {
blockID = entry.getValue();
bestMatch = matching;
if(bestMatch == attributes.size())
break;
}
}
return blockID;
}
private String getBlockId(String blockString) {
return blockString.split("\\[", 2)[0];
}
private Set<String> getAttributes(String blockString) {
Set<String> attributes = new HashSet<>();
if(blockString.contains("["))
Collections.addAll(attributes, blockString.split("\\[")[1].replace("]", "").split(","));
return attributes;
}
static class BlockTypeID{
private final int blockId;
private final byte dataId;
private BlockTypeID(String blockId, String dataId) {
this.blockId = Integer.parseInt(blockId);
this.dataId = Byte.parseByte(dataId);
}
int getBlockId() {
return blockId;
}
byte getDataId() {
return dataId;
}
}
}
@@ -0,0 +1,24 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
public class LocaleChangeWrapper8 implements LocaleChangeWrapper {
// Event not available in 1.8-1.10
}
@@ -0,0 +1,71 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.comphenix.tinyprotocol.Reflection;
import com.mojang.authlib.GameProfile;
import org.bukkit.GameMode;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
public class ProtocolWrapper8 implements ProtocolWrapper {
private static final Reflection.FieldAccessor<?> equipmentSlot;
static {
if(Core.getVersion() == 8) {
equipmentSlot = Reflection.getField(equipmentPacket, int.class, 1);
} else {
Class<?> enumItemSlot = Reflection.getClass("{nms.world.entity}.EnumItemSlot");
equipmentSlot = Reflection.getField(equipmentPacket, enumItemSlot, 0);
}
}
private static final Reflection.FieldAccessor<?> equipmentStack = Reflection.getField(equipmentPacket, itemStack, 0);
@Override
public void setEquipmentPacketStack(Object packet, Object slot, Object stack) {
equipmentSlot.set(packet, slot);
equipmentStack.set(packet, stack);
}
private static final Class<?> playerInfoPacket = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo");
private static final Class<?> playerInfoActionClass = Reflection.getClass("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
private static final Reflection.FieldAccessor<?> playerInfoAction = Reflection.getField(playerInfoPacket, playerInfoActionClass, 0);
private static final Reflection.FieldAccessor<List> playerInfoData = Reflection.getField(playerInfoPacket, List.class, 0);
private static final EnumMap<PlayerInfoAction, Object> actions = new EnumMap<>(PlayerInfoAction.class);
static {
Object[] nativeActions = playerInfoActionClass.getEnumConstants();
actions.put(PlayerInfoAction.ADD, nativeActions[0]);
actions.put(PlayerInfoAction.GAMEMODE, nativeActions[1]);
actions.put(PlayerInfoAction.REMOVE, nativeActions[4]);
}
private static final Class<?> iChatBaseComponent = Reflection.getClass("{nms.network.chat}.IChatBaseComponent");
private static final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor("{nms.network.protocol.game}.PacketPlayOutPlayerInfo$PlayerInfoData", playerInfoPacket, GameProfile.class, int.class, enumGamemode, iChatBaseComponent);
@Override
@SuppressWarnings("deprecation")
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
Object packet = Reflection.newInstance(playerInfoPacket);
playerInfoAction.set(packet, actions.get(action));
playerInfoData.set(packet, Collections.singletonList(playerInfoDataConstructor.invoke(packet, profile, 0, ProtocolWrapper.getGameModeById.invoke(null, mode.getValue()), null)));
return packet;
}
}
@@ -0,0 +1,24 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
public class RecipeDiscoverWrapper8 implements RecipeDiscoverWrapper {
// Event not available pre flattening
}
@@ -0,0 +1,265 @@
package de.steamwar.core;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.*;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.extension.input.ParserContext;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
import com.sk89q.worldedit.extent.clipboard.io.SchematicReader;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.registry.WorldData;
import de.steamwar.sql.NoClipboardException;
import org.bukkit.entity.Player;
import java.io.*;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class WorldEditWrapper8 implements WorldEditWrapper.IWorldEditWrapper {
@Override
public InputStream getPlayerClipboard(Player player, boolean schemFormat) {
ClipboardHolder clipboardHolder;
try {
clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard();
} catch (EmptyClipboardException e) {
throw new NoClipboardException();
}
Clipboard clipboard = clipboardHolder.getClipboard();
if(clipboard == null)
throw new NoClipboardException();
PipedOutputStream outputStream = new PipedOutputStream();
PipedInputStream inputStream;
try {
inputStream = new PipedInputStream(outputStream, 4096);
} catch (IOException e) {
throw new SecurityException("Could not init piped input stream", e);
}
new Thread(() -> {
try {
ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData());
} catch (IOException e) {
Core.getInstance().getLogger().log(Level.SEVERE, "Could not write schematic", e);
}
try {
outputStream.close();
} catch (IOException e) {
Core.getInstance().getLogger().log(Level.SEVERE, "Could not close schem writer", e);
}
}, "SchemWriter").start();
return inputStream;
}
@Override
public void setPlayerClipboard(Player player, InputStream is, boolean schemFormat) {
WorldData world = new BukkitWorld(player.getWorld()).getWorldData();
Clipboard clipboard;
try {
clipboard = getClipboard(is, schemFormat);
} catch (IOException e) {
throw new RuntimeException(e);
}
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard, world));
}
@Override
public Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException {
if(schemFormat)
return new SpongeSchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
else
return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
}
private static class SpongeSchematicReader implements ClipboardReader {
private final NBTInputStream inputStream;
private int schematicVersion = -1;
SpongeSchematicReader(NBTInputStream inputStream) {
Preconditions.checkNotNull(inputStream);
this.inputStream = inputStream;
}
@Override
public Clipboard read(WorldData worldData) throws IOException {
CompoundTag schematicTag = this.getBaseTag();
if (this.schematicVersion == 1) {
return this.readSchematic(schematicTag);
} else if (this.schematicVersion == 2) {
return this.readSchematic(schematicTag);
} else {
throw new IOException("This schematic version is currently not supported");
}
}
private CompoundTag getBaseTag() throws IOException {
NamedTag rootTag = this.inputStream.readNamedTag();
if (!rootTag.getName().equals("Schematic")) {
throw new IOException("Tag 'Schematic' does not exist or is not first");
} else {
CompoundTag schematicTag = (CompoundTag)rootTag.getTag();
Map<String, Tag> schematic = schematicTag.getValue();
this.schematicVersion = (requireTag(schematic, "Version", IntTag.class)).getValue();
return schematicTag;
}
}
private BlockArrayClipboard readSchematic(CompoundTag schematicTag) throws IOException {
IDConverter8 ids = new IDConverter8();
Map<String, Tag> schematic = schematicTag.getValue();
int width = (requireTag(schematic, "Width", ShortTag.class)).getValue();
int height = (requireTag(schematic, "Height", ShortTag.class)).getValue();
int length = (requireTag(schematic, "Length", ShortTag.class)).getValue();
IntArrayTag offsetTag = getTag(schematic, "Offset", IntArrayTag.class);
int[] offsetParts;
if (offsetTag != null) {
offsetParts = offsetTag.getValue();
if (offsetParts.length != 3)
throw new IOException("Invalid offset specified in schematic.");
} else {
offsetParts = new int[]{0, 0, 0};
}
BlockVector min = new BlockVector(offsetParts[0], offsetParts[1], offsetParts[2]);
CompoundTag metadataTag = getTag(schematic, "Metadata", CompoundTag.class);
Vector origin;
CuboidRegion region;
if (metadataTag != null && metadataTag.containsKey("WEOffsetX")) {
Map<String, Tag> metadata = metadataTag.getValue();
int offsetX = (requireTag(metadata, "WEOffsetX", IntTag.class)).getValue();
int offsetY = (requireTag(metadata, "WEOffsetY", IntTag.class)).getValue();
int offsetZ = (requireTag(metadata, "WEOffsetZ", IntTag.class)).getValue();
BlockVector offset = new BlockVector(offsetX, offsetY, offsetZ);
origin = min.subtract(offset);
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE));
} else {
origin = min;
region = new CuboidRegion(min, min.add(width, height, length).subtract(BlockVector.ONE));
}
IntTag paletteMaxTag = getTag(schematic, "PaletteMax", IntTag.class);
Map<String, Tag> paletteObject = requireTag(schematic, "Palette", CompoundTag.class).getValue();
if (paletteMaxTag != null && paletteObject.size() != paletteMaxTag.getValue())
throw new IOException("Block palette size does not match expected size.");
Map<Integer, BaseBlock> palette = new HashMap<>();
ParserContext parserContext = new ParserContext();
parserContext.setRestricted(false);
parserContext.setPreferringWildcard(false);
for(String palettePart : paletteObject.keySet()) {
IDConverter8.BlockTypeID blockID = ids.getId(palettePart);
palette.put(requireTag(paletteObject, palettePart, IntTag.class).getValue(), new BaseBlock(blockID.getBlockId(), blockID.getDataId()));
}
byte[] blocks = requireTag(schematic, "BlockData", ByteArrayTag.class).getValue();
Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<>();
ListTag tileEntities = getTag(schematic, "BlockEntities", ListTag.class);
if (tileEntities == null) {
tileEntities = getTag(schematic, "TileEntities", ListTag.class);
}
if (tileEntities != null) {
List<Map<String, Tag>> tileEntityTags = tileEntities.getValue().stream().map((tag) ->
(CompoundTag)tag
).map(CompoundTag::getValue).collect(Collectors.toList());
BlockVector pt;
Map<String, Tag> tileEntity;
for(Iterator<Map<String, Tag>> var20 = tileEntityTags.iterator(); var20.hasNext(); tileEntitiesMap.put(pt, tileEntity)) {
tileEntity = var20.next();
int[] pos = requireTag(tileEntity, "Pos", IntArrayTag.class).getValue();
pt = new BlockVector(pos[0], pos[1], pos[2]);
Map<String, Tag> values = Maps.newHashMap(tileEntity);
values.put("x", new IntTag(pt.getBlockX()));
values.put("y", new IntTag(pt.getBlockY()));
values.put("z", new IntTag(pt.getBlockZ()));
values.put("id", values.get("Id"));
values.remove("Id");
values.remove("Pos");
tileEntity = values;
}
}
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
clipboard.setOrigin(origin);
int index = 0;
for(int i = 0; i < blocks.length; ++index) {
int value = 0;
int varintLength = 0;
while(true) {
value |= (blocks[i] & 127) << varintLength++ * 7;
if (varintLength > 5) {
throw new IOException("VarInt too big (probably corrupted data)");
}
if ((blocks[i] & 128) != 128) {
++i;
int y = index / (width * length);
int z = index % (width * length) / width;
int x = index % (width * length) % width;
BaseBlock block = palette.get(value);
BlockVector pt = new BlockVector(x, y, z);
try {
if (tileEntitiesMap.containsKey(pt)) {
block.setNbtData(new CompoundTag(tileEntitiesMap.get(pt)));
clipboard.setBlock(clipboard.getMinimumPoint().add(pt), block);
} else {
clipboard.setBlock(clipboard.getMinimumPoint().add(pt), block);
}
break;
} catch (WorldEditException var30) {
throw new IOException("Failed to load a block in the schematic");
}
}
++i;
}
}
return clipboard;
}
private static <T extends Tag> T requireTag(Map<String, Tag> items, String key, Class<T> expected) throws IOException {
if (!items.containsKey(key)) {
throw new IOException("Schematic file is missing a \"" + key + "\" tag");
} else {
Tag tag = items.get(key);
if (!expected.isInstance(tag)) {
throw new IOException(key + " tag is not of tag type " + expected.getName());
} else {
return expected.cast(tag);
}
}
}
private static <T extends Tag> T getTag(Map<String, Tag> items, String key, Class<T> expected) {
if (!items.containsKey(key)) {
return null;
} else {
Tag test = items.get(key);
return !expected.isInstance(test) ? null : expected.cast(test);
}
}
}
}
@@ -0,0 +1,38 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2021 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.techhider;
import org.bukkit.Material;
import java.util.Collections;
import java.util.Set;
public class BlockIds8 implements BlockIds {
@Override
@SuppressWarnings("deprecation")
public int materialToId(Material material) {
return material.getId() << 4;
}
@Override
public Set<Integer> materialToAllIds(Material material) {
return Collections.singleton(materialToId(material));
}
}
@@ -0,0 +1,40 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2021 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.techhider;
import com.comphenix.tinyprotocol.Reflection;
import org.bukkit.entity.Player;
import java.util.Set;
import java.util.function.BiFunction;
public class ChunkHider8 implements ChunkHider {
protected static final Class<?> mapChunkPacket = Reflection.getClass("{nms}.PacketPlayOutMapChunk");
@Override
public Class<?> mapChunkPacket() {
return mapChunkPacket;
}
@Override
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider.LocationEvaluator locationEvaluator, int obfuscationTarget, Set<Integer> obfuscate, Set<String> hiddenBlockEntities) {
return (player, packet) -> packet;
}
}
@@ -0,0 +1,86 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2021 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.techhider;
import com.comphenix.tinyprotocol.Reflection;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Set;
import java.util.function.BiFunction;
public class ProtocolWrapper8 implements ProtocolWrapper {
private static final Class<?> chunkCoordinateIntPair = Reflection.getClass("{nms}.ChunkCoordIntPair");
private static final Reflection.FieldAccessor<?> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, chunkCoordinateIntPair, 0);
private static final Reflection.FieldAccessor<Integer> chunkCoordinateX = Reflection.getField(chunkCoordinateIntPair, int.class, 0);
private static final Reflection.FieldAccessor<Integer> chunkCoordinateZ = Reflection.getField(chunkCoordinateIntPair, int.class, 1);
private static final Class<?> multiBlockChangeInfo = Reflection.getClass("{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo");
private static final Reflection.ConstructorInvoker multiBlockChangeInfoConstructor = Reflection.getConstructor(multiBlockChangeInfo, TechHider.multiBlockChangePacket, short.class, TechHider.iBlockData);
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoBlock = Reflection.getField(multiBlockChangeInfo, TechHider.iBlockData, 0);
private static final Reflection.FieldAccessor<?> multiBlockChangeInfoPos = Reflection.getField(multiBlockChangeInfo, short.class, 0);
private static final Class<?> multiBlockChangeInfoArray = Reflection.getClass("[L{nms}.PacketPlayOutMultiBlockChange$MultiBlockChangeInfo;");
private static final Reflection.FieldAccessor<?> multiBlockChangeInfos = Reflection.getField(TechHider.multiBlockChangePacket, multiBlockChangeInfoArray, 0);
@Override
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator locationEvaluator) {
return (p, packet) -> {
Object chunkCoords = multiBlockChangeChunk.get(packet);
int chunkX = chunkCoordinateX.get(chunkCoords);
int chunkZ = chunkCoordinateZ.get(chunkCoords);
if(locationEvaluator.skipChunk(p, chunkX, chunkZ))
return packet;
packet = TechHider.multiBlockChangeCloner.apply(packet);
Object[] mbcis = (Object[]) multiBlockChangeInfos.get(packet);
ArrayList<Object> blockChangeInfos = new ArrayList<>(mbcis.length);
for(Object mbci : mbcis) {
short pos = (short) multiBlockChangeInfoPos.get(mbci);
switch(locationEvaluator.check(p, 16*chunkX + (pos >> 12 & 0xF), pos & 0xFF, 16*chunkZ + (pos >> 8 & 0xF))) {
case SKIP:
blockChangeInfos.add(mbci);
break;
case CHECK:
blockChangeInfos.add(TechHider.iBlockDataHidden(obfuscate, multiBlockChangeInfoBlock.get(mbci)) ? multiBlockChangeInfoConstructor.invoke(packet, pos, obfuscationTarget) : mbci);
break;
default:
break;
}
}
if(blockChangeInfos.isEmpty())
return null;
multiBlockChangeInfos.set(packet, blockChangeInfos.toArray((Object[])Array.newInstance(multiBlockChangeInfo, 0)));
return packet;
};
}
private static final Reflection.FieldAccessor<Integer> tileEntityDataAction = Reflection.getField(TechHider.tileEntityDataPacket, int.class, 0);
@Override
public boolean unfilteredTileEntityDataAction(Object packet) {
return tileEntityDataAction.get(packet) != 9;
}
@Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, Object obfuscationTarget, Set<Material> obfuscate, TechHider.LocationEvaluator bypass) {
return null;
}
}
File diff suppressed because it is too large Load Diff