Serverside block-connections!

This commit is contained in:
Gerrygames 2018-11-17 15:45:37 +01:00
parent 73dccbaf24
commit 9a13eb36b3
37 changed files with 78431 additions and 3 deletions

View File

@ -219,4 +219,19 @@ public class BukkitViaConfig extends Config implements ViaVersionConfig {
public boolean isDisable1_13AutoComplete() {
return getBoolean("disable-1_13-auto-complete", false);
}
@Override
public boolean isServersideBlockConnections() {
return getBoolean("serverside-blockconnections", false);
}
@Override
public String getBlockConnectionMethod() {
return getString("blockconnection-method", "world");
}
@Override
public boolean isRedueBlockStorageMemory() {
return getBoolean("reduce-blockstorage-memory", false);
}
}

View File

@ -15,12 +15,14 @@ import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
import us.myles.ViaVersion.api.protocol.ProtocolVersion;
import us.myles.ViaVersion.bukkit.listeners.UpdateListener;
import us.myles.ViaVersion.bukkit.listeners.protocol1_9to1_8.*;
import us.myles.ViaVersion.bukkit.providers.BukkitBlockConnectionProvider;
import us.myles.ViaVersion.bukkit.providers.BukkitInventoryQuickMoveProvider;
import us.myles.ViaVersion.bukkit.providers.BukkitViaBulkChunkTranslator;
import us.myles.ViaVersion.bukkit.providers.BukkitViaMovementTransmitter;
import us.myles.ViaVersion.bukkit.classgenerator.ClassGenerator;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.providers.InventoryQuickMoveProvider;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.HandItemProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider;
@ -92,6 +94,9 @@ public class BukkitViaLoader implements ViaPlatformLoader {
if (plugin.getConf().is1_12QuickMoveActionFix()) {
Via.getManager().getProviders().use(InventoryQuickMoveProvider.class, new BukkitInventoryQuickMoveProvider());
}
if (Via.getConfig().getBlockConnectionMethod().equalsIgnoreCase("world")) {
Via.getManager().getProviders().use(BlockConnectionProvider.class, new BukkitBlockConnectionProvider());
}
Via.getManager().getProviders().use(HandItemProvider.class, new HandItemProvider() {
@Override
public Item getHandItem(final UserConnection info) {

View File

@ -0,0 +1,41 @@
package us.myles.ViaVersion.bukkit.providers;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
import java.util.UUID;
public class BukkitBlockConnectionProvider extends BlockConnectionProvider {
private Chunk lastChunk;
@Override
public int getWorldBlockData(UserConnection user, Position position) {
UUID uuid = user.get(ProtocolInfo.class).getUuid();
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
World world = player.getWorld();
int x = (int) (position.getX() >> 4);
int z = (int) (position.getZ() >> 4);
if (world.isChunkLoaded(x, z)) {
Chunk c = getChunk(world, x, z);
Block b = c.getBlock(position.getX().intValue(), position.getY().intValue(), position.getZ().intValue());
return b.getTypeId() << 4 | b.getData();
}
}
return 0;
}
public Chunk getChunk(World world, int x, int z) {
if (lastChunk != null && lastChunk.getX() == x && lastChunk.getZ() == z) {
return lastChunk;
}
return lastChunk = world.getChunkAt(x, z);
}
}

View File

@ -10,7 +10,7 @@ import java.net.URL;
import java.util.*;
public class BungeeViaConfig extends Config implements ViaVersionConfig {
private static List<String> UNSUPPORTED = Arrays.asList("nms-player-ticking", "item-cache", "anti-xray-patch", "quick-move-action-fix", "velocity-ping-interval", "velocity-ping-save", "velocity-servers");
private static List<String> UNSUPPORTED = Arrays.asList("nms-player-ticking", "item-cache", "anti-xray-patch", "quick-move-action-fix", "velocity-ping-interval", "velocity-ping-save", "velocity-servers", "blockconnection-method");
public BungeeViaConfig(File configFile) {
super(new File(configFile, "config.yml"));
@ -272,4 +272,19 @@ public class BungeeViaConfig extends Config implements ViaVersionConfig {
public boolean isDisable1_13AutoComplete() {
return getBoolean("disable-1_13-auto-complete", false);
}
@Override
public boolean isServersideBlockConnections() {
return getBoolean("serverside-blockconnections", false);
}
@Override
public String getBlockConnectionMethod() {
return "packet";
}
@Override
public boolean isRedueBlockStorageMemory() {
return getBoolean("reduce-blockstorage-memory", false);
}
}

View File

@ -266,4 +266,25 @@ public interface ViaVersionConfig {
* @return True if enabled
*/
boolean isMinimizeCooldown();
/**
* Enable the serverside blockconnections for 1.13+ clients
*
* @return True if enabled
*/
boolean isServersideBlockConnections();
/**
* Get the type of block-connection provider which should be used
*
* @return String world for world-level or packet for packet-level
*/
String getBlockConnectionMethod();
/**
* When activated, only the most important blocks are saved in the BlockStorage.
*
* @return True if enabled
*/
boolean isRedueBlockStorageMemory();
}

View File

@ -0,0 +1,34 @@
package us.myles.ViaVersion.api.minecraft;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
@Getter
@AllArgsConstructor
public enum BlockFace {
NORTH(0, 0, -1, EnumAxis.Z), SOUTH(0, 0, 1, EnumAxis.Z), EAST(1, 0, 0, EnumAxis.X), WEST(-1, 0, 0, EnumAxis.X), TOP(0, 1, 0, EnumAxis.Y), BOTTOM(0, -1, 0, EnumAxis.Y);
private static Map<BlockFace, BlockFace> opposites = new HashMap<>();
static {
opposites.put(BlockFace.NORTH, BlockFace.SOUTH);
opposites.put(BlockFace.SOUTH, BlockFace.NORTH);
opposites.put(BlockFace.EAST, BlockFace.WEST);
opposites.put(BlockFace.WEST, BlockFace.EAST);
opposites.put(BlockFace.TOP, BlockFace.BOTTOM);
opposites.put(BlockFace.BOTTOM, BlockFace.TOP);
}
private int modX, modY, modZ;
private EnumAxis axis;
public BlockFace opposite() {
return opposites.get(this);
}
public enum EnumAxis {
X, Y, Z;
}
}

View File

@ -13,4 +13,15 @@ public class Position {
private Long x;
private Long y;
private Long z;
public Position getRelative(BlockFace face) {
return new Position(this.x + face.getModX(), this.y + face.getModY(), this.z + face.getModZ());
}
public Position shift(BlockFace face) {
this.x += face.getModX();
this.y += face.getModY();
this.z += face.getModZ();
return this;
}
}

View File

@ -17,6 +17,7 @@ import us.myles.ViaVersion.api.remapper.ValueCreator;
import us.myles.ViaVersion.api.remapper.ValueTransformer;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.ConnectionData;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.packets.EntityPackets;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.packets.InventoryPackets;
@ -484,6 +485,10 @@ public class Protocol1_13To1_12_2 extends Protocol {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
int dimensionId = wrapper.get(Type.INT, 0);
clientWorld.setEnvironment(dimensionId);
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.clearBlockStorage(wrapper.user());
}
}
});
handler(SEND_DECLARE_COMMANDS_AND_TAGS);

View File

@ -0,0 +1,60 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import lombok.Getter;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public abstract class AbstractFenceConnectionHandler extends ConnectionHandler {
private final String blockConnections;
@Getter
private Set<Integer> blockStates = new HashSet<>();
private Map<Byte, Integer> connectedBlockStates = new HashMap<>();
public AbstractFenceConnectionHandler(String blockConnections, String key){
this.blockConnections = blockConnections;
for (Map.Entry<String, Integer> blockState : ConnectionData.keyToId.entrySet()) {
if (key.equals(blockState.getKey().split("\\[")[0])) {
blockStates.add(blockState.getValue());
ConnectionData.connectionHandlerMap.put(blockState.getValue(), this);
WrappedBlockData blockData = WrappedBlockData.fromString(blockState.getKey());
connectedBlockStates.put(getStates(blockData), blockState.getValue());
}
}
}
protected byte getStates(WrappedBlockData blockData) {
byte states = 0;
if (blockData.getValue("east").equals("true")) states |= 1;
if (blockData.getValue("north").equals("true")) states |= 2;
if (blockData.getValue("south").equals("true")) states |= 4;
if (blockData.getValue("west").equals("true")) states |= 8;
if (blockData.hasData("waterlogged") && blockData.getValue("waterlogged").equals("true")) states |= 16;
return states;
}
protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = 0;
if (connects(BlockFace.EAST, getBlockData(user, position.getRelative(BlockFace.EAST)))) states |= 1;
if (connects(BlockFace.NORTH, getBlockData(user, position.getRelative(BlockFace.NORTH)))) states |= 2;
if (connects(BlockFace.SOUTH, getBlockData(user, position.getRelative(BlockFace.SOUTH)))) states |= 4;
if (connects(BlockFace.WEST, getBlockData(user, position.getRelative(BlockFace.WEST)))) states |= 8;
return states;
}
@Override
public int connect(UserConnection user, Position position, int blockState) {
final Integer newBlockState = connectedBlockStates.get(getStates(user, position, blockState));
return newBlockState == null ? blockState : newBlockState;
}
protected boolean connects(BlockFace side, int blockState) {
return blockStates.contains(blockState) || blockConnections != null && ConnectionData.blockConnectionData.containsKey(blockState) && ConnectionData.blockConnectionData.get(blockState).connectsTo(blockConnections, side.opposite());
}
}

View File

@ -0,0 +1,51 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class AbstractStempConnectionHandler extends ConnectionHandler {
private static final BlockFace[] BLOCK_FACES = {BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST};
private int baseStateId;
private Set<Integer> blockId = new HashSet<>();
private Map<BlockFace, Integer> stemps = new HashMap<>();
public AbstractStempConnectionHandler(String baseStateId, String blockId, String toKey) {
this.baseStateId = ConnectionData.getId(baseStateId);
for (Map.Entry<String, Integer> entry : ConnectionData.keyToId.entrySet()) {
String key = entry.getKey().split("\\[")[0];
if (entry.getValue() == this.baseStateId || blockId.equals(key)) {
if (entry.getValue() != this.baseStateId) {
this.blockId.add(entry.getValue());
}
ConnectionData.connectionHandlerMap.put(entry.getValue(), this);
}
if (key.equals(toKey)) {
WrappedBlockData data = WrappedBlockData.fromString(entry.getKey());
String facing = data.getValue("facing").toUpperCase();
stemps.put(BlockFace.valueOf(facing), entry.getValue());
}
}
}
@Override
public int connect(UserConnection user, Position position, int blockState) {
if (blockState != baseStateId) {
return blockState;
}
for (BlockFace blockFace : BLOCK_FACES) {
if (blockId.contains(getBlockData(user, position.getRelative(blockFace)))) {
return stemps.get(blockFace);
}
}
return baseStateId;
}
}

View File

@ -0,0 +1,17 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
public class BasicFenceConnectionHandler extends AbstractFenceConnectionHandler {
static void init() {
new BasicFenceConnectionHandler("fenceConnections", "minecraft:oak_fence");
new BasicFenceConnectionHandler("fenceConnections", "minecraft:birch_fence");
new BasicFenceConnectionHandler("fenceConnections", "minecraft:jungle_fence");
new BasicFenceConnectionHandler("fenceConnections", "minecraft:dark_oak_fence");
new BasicFenceConnectionHandler("fenceConnections", "minecraft:acacia_fence");
new BasicFenceConnectionHandler("fenceConnections", "minecraft:spruce_fence");
}
public BasicFenceConnectionHandler(String blockConnections, String key) {
super(blockConnections, key);
}
}

View File

@ -0,0 +1,19 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import java.util.HashMap;
import java.util.Map;
public class BlockData {
private Map<String, Boolean[]> connectData = new HashMap<>();
public void put(String key, Boolean[] booleans){
connectData.put(key, booleans);
}
public boolean connectsTo(String blockConnection, BlockFace face){
final Boolean[] booleans = connectData.get(blockConnection);
return booleans != null && booleans[face.ordinal()];
}
}

View File

@ -0,0 +1,61 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
class ChestConnectionHandler extends ConnectionHandler {
private static Map<Integer, BlockFace> chestFacings = new HashMap<>();
private static Map<Byte, Integer> connectedStates = new HashMap<>();
private static Set<Integer> trappedChests = new HashSet<>();
static void init() {
ChestConnectionHandler connectionHandler = new ChestConnectionHandler();
for (Map.Entry<String, Integer> blockState : ConnectionData.keyToId.entrySet()) {
String key = blockState.getKey().split("\\[")[0];
if (!key.equals("minecraft:chest") && !key.equals("minecraft:trapped_chest")) continue;
WrappedBlockData blockData = WrappedBlockData.fromString(blockState.getKey());
if (blockData.getValue("waterlogged").equals("true")) continue;
chestFacings.put(blockState.getValue(), BlockFace.valueOf(blockData.getValue("facing").toUpperCase()));
if (key.equalsIgnoreCase("minecraft:trapped_chest")) trappedChests.add(blockState.getValue());
connectedStates.put(getStates(blockData), blockState.getValue());
ConnectionData.connectionHandlerMap.put(blockState.getValue(), connectionHandler);
}
}
private static Byte getStates(WrappedBlockData blockData) {
byte states = 0;
String type = blockData.getValue("type");
if (type.equals("left")) states |= 1;
if (type.equals("right")) states |= 2;
states |= (BlockFace.valueOf(blockData.getValue("facing").toUpperCase()).ordinal() << 2);
if (blockData.getMinecraftKey().equals("minecraft:trapped_chest")) states |= 16;
return states;
}
@Override
public int connect(UserConnection user, Position position, int blockState) {
BlockFace facing = chestFacings.get(blockState);
byte states = 0;
states |= (facing.ordinal() << 2);
boolean trapped = trappedChests.contains(blockState);
if (trapped) states |= 16;
int relative;
if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.NORTH))) && trapped == trappedChests.contains(relative)) {
states |= facing == BlockFace.WEST ? 1 : 2;
} else if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.SOUTH))) && trapped == trappedChests.contains(relative)) {
states |= facing == BlockFace.EAST ? 1 : 2;
} else if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.WEST))) && trapped == trappedChests.contains(relative)) {
states |= facing == BlockFace.NORTH ? 2 : 1;
} else if (chestFacings.containsKey(relative = getBlockData(user, position.getRelative(BlockFace.EAST))) && trapped == trappedChests.contains(relative)) {
states |= facing == BlockFace.SOUTH ? 2 : 1;
}
Integer newBlockState = connectedStates.get(states);
return newBlockState == null ? blockState : newBlockState;
}
}

View File

@ -0,0 +1,46 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.Map;
public class ChorusPlantConnectionHandler extends AbstractFenceConnectionHandler {
private int endstone;
static void init() {
new ChorusPlantConnectionHandler("minecraft:chorus_plant");
}
public ChorusPlantConnectionHandler(String key) {
super(null, key);
endstone = ConnectionData.getId("minecraft:end_stone");
for (Map.Entry<String, Integer> entry : ConnectionData.keyToId.entrySet()) {
if (entry.getKey().split("\\[")[0].equals("minecraft:chorus_flower")) {
getBlockStates().add(entry.getValue());
}
}
}
@Override
protected byte getStates(WrappedBlockData blockData) {
byte states = super.getStates(blockData);
if (blockData.getValue("up").equals("true")) states |= 16;
if (blockData.getValue("down").equals("true")) states |= 32;
return states;
}
@Override
protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = super.getStates(user, position, blockState);
if (connects(BlockFace.TOP, getBlockData(user, position.getRelative(BlockFace.TOP)))) states |= 16;
if (connects(BlockFace.BOTTOM, getBlockData(user, position.getRelative(BlockFace.BOTTOM)))) states |= 32;
return states;
}
@Override
protected boolean connects(BlockFace side, int blockState) {
return getBlockStates().contains(blockState) || (side == BlockFace.BOTTOM && blockState == endstone);
}
}

View File

@ -0,0 +1,209 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.minecraft.chunks.Chunk;
import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers.PacketBlockConnectionProvider;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class ConnectionData {
static Map<Integer, String> idToKey = new HashMap<>();
static Map<String, Integer> keyToId = new HashMap<>();
static Map<Integer, ConnectionHandler> connectionHandlerMap = new HashMap<>();
static Map<Integer, BlockData> blockConnectionData = new HashMap<>();
static Set<Integer> occludingStates = new HashSet<>();
public static void update(UserConnection user, Position position) {
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
for (int y = -1; y <= 1; y++) {
if (Math.abs(x) + Math.abs(y) + Math.abs(z) != 1) continue;
Position pos = new Position(position.getX() + x, position.getY() + y, position.getZ() + z);
int blockState = Via.getManager().getProviders().get(BlockConnectionProvider.class).getBlockdata(user, pos);
if (!connects(blockState)) continue;
int newBlockState = connect(user, pos, blockState);
if (newBlockState == blockState) continue;
PacketWrapper blockUpdatePacket = new PacketWrapper(0x0B, null, user);
blockUpdatePacket.write(Type.POSITION, pos);
blockUpdatePacket.write(Type.VAR_INT, newBlockState);
try {
blockUpdatePacket.send(Protocol1_13To1_12_2.class, true, false);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
public static BlockConnectionProvider getProvider() {
return Via.getManager().getProviders().get(BlockConnectionProvider.class);
}
public static void updateBlockStorage(UserConnection userConnection, Position position, int blockState) {
if (!needStoreBlocks()) return;
if (ConnectionData.isWelcome(blockState)) {
ConnectionData.getProvider().storeBlock(userConnection, position, blockState);
} else {
ConnectionData.getProvider().removeBlock(userConnection, position);
}
}
public static void clearBlockStorage(UserConnection connection) {
if (!needStoreBlocks()) return;
getProvider().clearStorage(connection);
}
public static boolean needStoreBlocks() {
return getProvider().storesBlocks();
}
public static void connectBlocks(UserConnection user, Chunk chunk) {
long xOff = chunk.getX() << 4;
long zOff = chunk.getZ() << 4;
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue;
boolean willConnect = false;
for (int p = 0; p < section.getPaletteSize(); p++) {
int id = section.getPaletteEntry(p);
if (ConnectionData.connects(id)) {
willConnect = true;
break;
}
}
if (!willConnect) continue;
long yOff = i << 4;
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
int block = section.getFlatBlock(x, y, z);
if (ConnectionData.connects(block)) {
block = ConnectionData.connect(user, new Position(xOff + x, yOff + y, zOff + z), block);
section.setFlatBlock(x, y, z, block);
}
if (x == 0) {
update(user, new Position(xOff - 1, yOff + y, zOff + z));
} else if (x == 15) {
update(user, new Position(xOff + 16, yOff + y, zOff + z));
}
if (z == 0) {
update(user, new Position(xOff + x, yOff + y, zOff - 1));
} else if (z == 15) {
update(user, new Position(xOff + x, yOff + y, zOff + 16));
}
}
}
}
}
}
public static void init() {
if (!Via.getConfig().isServersideBlockConnections()) return;
Via.getPlatform().getLogger().info("Loading block connection mappings ...");
JsonObject mapping1_13 = MappingData.loadData("mapping-1.13.json");
JsonObject blocks1_13 = mapping1_13.getAsJsonObject("blocks");
for (Entry<String, JsonElement> blockState : blocks1_13.entrySet()) {
Integer id = Integer.parseInt(blockState.getKey());
String key = blockState.getValue().getAsString();
idToKey.put(id, key);
keyToId.put(key, id);
}
if (!Via.getConfig().isRedueBlockStorageMemory()) {
JsonObject mappingBlockConnections = MappingData.loadData("blockConnections.json");
for (Entry<String, JsonElement> entry : mappingBlockConnections.entrySet()) {
int id = keyToId.get(entry.getKey());
BlockData blockData = new BlockData();
for (Entry<String, JsonElement> type : entry.getValue().getAsJsonObject().entrySet()) {
String name = type.getKey();
JsonObject object = type.getValue().getAsJsonObject();
Boolean[] data = new Boolean[6];
for (BlockFace value : BlockFace.values()) {
String face = value.toString().toLowerCase();
if (object.has(face)) {
data[value.ordinal()] = object.getAsJsonPrimitive(face).getAsBoolean();
} else {
data[value.ordinal()] = false;
}
}
blockData.put(name, data);
}
blockConnectionData.put(id, blockData);
}
}
JsonObject blockData = MappingData.loadData("blockData.json");
JsonArray occluding = blockData.getAsJsonArray("occluding");
for (JsonElement jsonElement : occluding) {
occludingStates.add(keyToId.get(jsonElement.getAsString()));
}
PumpkinConnectionHandler.init();
MelonConnectionHandler.init();
BasicFenceConnectionHandler.init();
NetherFenceConnectionHandler.init();
WallConnectionHandler.init();
MelonConnectionHandler.init();
GlassConnectionHandler.init();
ChestConnectionHandler.init();
DoorConnectionHandler.init();
RedstoneConnectionHandler.init();
StairConnectionHandler.init();
FlowerConnectionHandler.init();
ChorusPlantConnectionHandler.init();
if (Via.getConfig().getBlockConnectionMethod().equalsIgnoreCase("packet")) {
Via.getManager().getProviders().register(BlockConnectionProvider.class, new PacketBlockConnectionProvider());
}
}
public static boolean isWelcome(int blockState) {
return blockConnectionData.containsKey(blockState) || connectionHandlerMap.containsKey(blockState);
}
public static boolean connects(int blockState) {
return connectionHandlerMap.containsKey(blockState);
}
public static int connect(UserConnection user, Position position, int blockState) {
if (connectionHandlerMap.containsKey(blockState)) {
ConnectionHandler handler = connectionHandlerMap.get(blockState);
return handler.connect(user, position, blockState);
} else {
return blockState;
}
}
public static int getId(String key) {
return keyToId.containsKey(key) ? keyToId.get(key) : -1;
}
public static String getKey(int id) {
return idToKey.get(id);
}
}

View File

@ -0,0 +1,19 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
public abstract class ConnectionHandler {
public abstract int connect(UserConnection connection, Position position, int blockState);
public int getBlockData(UserConnection connection, Position position) {
return Via.getManager().getProviders().get(BlockConnectionProvider.class).getBlockdata(connection, position);
}
public boolean canConnect(int id) {
ConnectionHandler handler = ConnectionData.connectionHandlerMap.get(id);
return handler != null && handler == this;
}
}

View File

@ -0,0 +1,100 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class DoorConnectionHandler extends ConnectionHandler {
private static Map<Integer, DoorData> doorDataMap = new HashMap<>();
private static Map<Short, Integer> connectedStates = new HashMap<>();
static void init() {
List<String> baseDoors = new LinkedList<>();
baseDoors.add("minecraft:oak_door");
baseDoors.add("minecraft:birch_door");
baseDoors.add("minecraft:jungle_door");
baseDoors.add("minecraft:dark_oak_door");
baseDoors.add("minecraft:acacia_door");
baseDoors.add("minecraft:spruce_door");
baseDoors.add("minecraft:iron_door");
DoorConnectionHandler connectionHandler = new DoorConnectionHandler();
for (Map.Entry<String, Integer> blockState : ConnectionData.keyToId.entrySet()) {
String key = blockState.getKey().split("\\[")[0];
int type = baseDoors.indexOf(key);
if (type == -1) continue;
WrappedBlockData blockData = WrappedBlockData.fromString(blockState.getKey());
int id = blockState.getValue();
DoorData doorData = new DoorData(
blockData.getValue("half").equals("lower"),
blockData.getValue("hinge").equals("right"),
blockData.getValue("powered").equals("true"),
blockData.getValue("open").equals("true"),
BlockFace.valueOf(blockData.getValue("facing").toUpperCase()),
type
);
doorDataMap.put(id, doorData);
connectedStates.put(getStates(doorData), id);
ConnectionData.connectionHandlerMap.put(id, connectionHandler);
}
}
private static short getStates(DoorData doorData) {
short s = 0;
if (doorData.isLower()) s |= 1;
if (doorData.isOpen()) s |= 2;
if (doorData.isPowered()) s |= 4;
if (doorData.isRightHinge()) s |= 8;
s |= doorData.getFacing().ordinal() << 4;
s |= (doorData.getType() & 0x7) << 6;
return s;
}
@Override
public int connect(UserConnection user, Position position, int blockState) {
DoorData doorData = doorDataMap.get(blockState);
if (doorData == null) return blockState;
short s = 0;
s |= (doorData.getType() & 0x7) << 6;
if (doorData.isLower()) {
DoorData upperHalf = doorDataMap.get(getBlockData(user, position.getRelative(BlockFace.TOP)));
if (upperHalf == null) return blockState;
s |= 1;
if (doorData.isOpen()) s |= 2;
if (upperHalf.isPowered()) s |= 4;
if (upperHalf.isRightHinge()) s |= 8;
s |= doorData.getFacing().ordinal() << 4;
} else {
DoorData lowerHalf = doorDataMap.get(getBlockData(user, position.getRelative(BlockFace.BOTTOM)));
if (lowerHalf == null) return blockState;
if (lowerHalf.isOpen()) s |= 2;
if (doorData.isPowered()) s |= 4;
if (doorData.isRightHinge()) s |= 8;
s |= lowerHalf.getFacing().ordinal() << 4;
}
Integer newBlockState = connectedStates.get(s);
return newBlockState == null ? blockState : newBlockState;
}
@AllArgsConstructor
@Getter
@ToString
private static class DoorData {
private final boolean lower, rightHinge, powered, open;
private final BlockFace facing;
private int type;
}
}

View File

@ -0,0 +1,48 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class FlowerConnectionHandler extends ConnectionHandler {
private static Set<String> baseFlower = new HashSet<>();
private static Map<Integer, Integer> flowers = new HashMap<>();
static void init() {
baseFlower.add("minecraft:rose_bush");
baseFlower.add("minecraft:sunflower");
baseFlower.add("minecraft:peony");
baseFlower.add("minecraft:tall_grass");
baseFlower.add("minecraft:large_fern");
baseFlower.add("minecraft:lilac");
FlowerConnectionHandler handler = new FlowerConnectionHandler();
for (Map.Entry<String, Integer> blockState : ConnectionData.keyToId.entrySet()) {
WrappedBlockData data = WrappedBlockData.fromString(blockState.getKey());
if (baseFlower.contains(data.getMinecraftKey())) {
ConnectionData.connectionHandlerMap.put(blockState.getValue(), handler);
if (data.getValue("half").equals("lower")) {
data.set("half", "upper");
flowers.put(blockState.getValue(), data.getBlockStateId());
}
}
}
}
@Override
public int connect(UserConnection user, Position position, int blockState) {
int blockBelowId = getBlockData(user, position.getRelative(BlockFace.BOTTOM));
if (flowers.containsKey(blockBelowId) && !flowers.containsKey(getBlockData(user, position.getRelative(BlockFace.TOP)))) {
return flowers.get(blockBelowId);
}
return blockState;
}
}

View File

@ -0,0 +1,39 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
public class GlassConnectionHandler extends AbstractFenceConnectionHandler {
static void init() {
new GlassConnectionHandler("paneConnections", "minecraft:white_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:orange_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:magenta_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:light_blue_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:yellow_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:lime_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:pink_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:gray_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:light_gray_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:cyan_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:purple_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:blue_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:brown_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:green_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:red_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:black_stained_glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:glass_pane");
new GlassConnectionHandler("paneConnections", "minecraft:iron_bars");
}
public GlassConnectionHandler(String blockConnections, String key) {
super(blockConnections, key);
}
@Override
protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = super.getStates(user, position, blockState);
return states == 0 && (ProtocolRegistry.SERVER_PROTOCOL <= 47 && ProtocolRegistry.SERVER_PROTOCOL != -1) ? 0xF : states;
}
}

View File

@ -0,0 +1,12 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
public class MelonConnectionHandler extends AbstractStempConnectionHandler {
public MelonConnectionHandler(String baseStateId, String blockId, String toKey) {
super(baseStateId, blockId, toKey);
}
static void init() {
new MelonConnectionHandler("minecraft:melon_stem[age=7]", "minecraft:melon", "minecraft:attached_melon_stem");
}
}

View File

@ -0,0 +1,12 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
public class NetherFenceConnectionHandler extends AbstractFenceConnectionHandler {
static void init() {
new NetherFenceConnectionHandler("netherFenceConnections", "minecraft:nether_brick_fence");
}
public NetherFenceConnectionHandler(String blockConnections, String key) {
super(blockConnections, key);
}
}

View File

@ -0,0 +1,12 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
public class PumpkinConnectionHandler extends AbstractStempConnectionHandler {
static void init() {
new PumpkinConnectionHandler("minecraft:pumpkin_stem[age=7]", "minecraft:carved_pumpkin", "minecraft:attached_pumpkin_stem");
}
public PumpkinConnectionHandler(String baseStateId, String blockId, String toKey) {
super(baseStateId, blockId, toKey);
}
}

View File

@ -0,0 +1,83 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class RedstoneConnectionHandler extends ConnectionHandler {
private static Set<Integer> redstone = new HashSet<>();
private static Map<Short, Integer> connectedBlockStates = new HashMap<>();
private static Map<Integer, Integer> powerMappings = new HashMap<>();
static void init() {
RedstoneConnectionHandler connectionHandler = new RedstoneConnectionHandler();
String redstoneKey = "minecraft:redstone_wire";
for (Map.Entry<String, Integer> blockState : ConnectionData.keyToId.entrySet()) {
String key = blockState.getKey().split("\\[")[0];
if (!redstoneKey.equals(key)) continue;
redstone.add(blockState.getValue());
ConnectionData.connectionHandlerMap.put(blockState.getValue(), connectionHandler);
WrappedBlockData blockData = WrappedBlockData.fromStateId(blockState.getValue());
connectedBlockStates.put(getStates(blockData), blockData.getBlockStateId());
powerMappings.put(blockData.getBlockStateId(), Integer.valueOf(blockData.getValue("power")));
}
}
private static short getStates(WrappedBlockData data) {
short b = 0;
b |= getState(data.getValue("east"));
b |= getState(data.getValue("north")) << 2;
b |= getState(data.getValue("south")) << 4;
b |= getState(data.getValue("west")) << 6;
b |= Integer.valueOf(data.getValue("power")) << 8;
return b;
}
private static int getState(String value) {
switch (value){
case "none": return 0;
case "side" : return 1;
case "up" : return 2;
default: return 0;
}
}
@Override
public int connect(UserConnection user, Position position, int blockState) {
short b = 0;
b |= connects(user, position, BlockFace.EAST);
b |= connects(user, position, BlockFace.NORTH) << 2;
b |= connects(user, position, BlockFace.SOUTH) << 4;
b |= connects(user, position, BlockFace.WEST) << 6;
b |= powerMappings.get(blockState) << 8;
final Integer newBlockState = connectedBlockStates.get(b);
return newBlockState == null ? blockState : newBlockState;
}
private int connects(UserConnection user, Position position, BlockFace side) {
final Position relative = position.getRelative(side);
int blockState = getBlockData(user, relative);
if (connects(side, blockState)) {
return 1; //side
}
int up = getBlockData(user, relative.getRelative(BlockFace.TOP));
if (redstone.contains(up) && !ConnectionData.occludingStates.contains(getBlockData(user, position.getRelative(BlockFace.TOP)))) {
return 2; //"up"
}
int down = getBlockData(user, relative.getRelative(BlockFace.BOTTOM));
if (redstone.contains(down) && !ConnectionData.occludingStates.contains(getBlockData(user, relative))) {
return 1; //side
}
return 0; //none
}
private boolean connects(BlockFace side, int blockState) {
final BlockData blockData = ConnectionData.blockConnectionData.get(blockState);
return blockData != null && blockData.connectsTo("redstoneConnections", side.opposite());
}
}

View File

@ -0,0 +1,145 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class StairConnectionHandler extends ConnectionHandler {
private static Map<Integer, StairData> stairDataMap = new HashMap<>();
private static Map<Short, Integer> connectedBlocks = new HashMap<>();
static void init() {
List<String> baseStairs = new LinkedList<>();
baseStairs.add("minecraft:oak_stairs");
baseStairs.add("minecraft:cobblestone_stairs");
baseStairs.add("minecraft:brick_stairs");
baseStairs.add("minecraft:stone_brick_stairs");
baseStairs.add("minecraft:nether_brick_stairs");
baseStairs.add("minecraft:sandstone_stairs");
baseStairs.add("minecraft:spruce_stairs");
baseStairs.add("minecraft:birch_stairs");
baseStairs.add("minecraft:jungle_stairs");
baseStairs.add("minecraft:quartz_stairs");
baseStairs.add("minecraft:acacia_stairs");
baseStairs.add("minecraft:dark_oak_stairs");
baseStairs.add("minecraft:red_sandstone_stairs");
baseStairs.add("minecraft:purpur_stairs");
baseStairs.add("minecraft:prismarine_stairs");
baseStairs.add("minecraft:prismarine_brick_stairs");
baseStairs.add("minecraft:dark_prismarine_stairs");
StairConnectionHandler connectionHandler = new StairConnectionHandler();
for (Map.Entry<String, Integer> blockState : ConnectionData.keyToId.entrySet()) {
String key = blockState.getKey().split("\\[")[0];
int type = baseStairs.indexOf(key);
if (type == -1) continue;
WrappedBlockData blockData = WrappedBlockData.fromString(blockState.getKey());
if (blockData.getValue("waterlogged").equals("true")) continue;
byte shape;
switch (blockData.getValue("shape")) {
case "straight": shape = 0; break;
case "inner_left": shape = 1; break;
case "inner_right": shape = 2; break;
case "outer_left": shape = 3; break;
case "outer_right": shape = 4; break;
default: continue;
}
StairData stairData = new StairData(
blockData.getValue("half").equals("bottom"),
shape, (byte) type,
BlockFace.valueOf(blockData.getValue("facing").toUpperCase())
);
stairDataMap.put(blockState.getValue(), stairData);
connectedBlocks.put(getStates(stairData), blockState.getValue());
ConnectionData.connectionHandlerMap.put(blockState.getValue(), connectionHandler);
}
}
private static short getStates(StairData stairData) {
short s = 0;
if (stairData.isBottom()) s |= 1;
s |= stairData.getShape() << 1;
s |= stairData.getType() << 4;
s |= stairData.getFacing().ordinal() << 9;
return s;
}
@Override
public int connect(UserConnection user, Position position, int blockState) {
StairData stairData = stairDataMap.get(blockState);
if (stairData == null) return blockState;
short s = 0;
if (stairData.isBottom()) s |= 1;
s |= getShape(user, position, stairData) << 1;
s |= stairData.getType() << 4;
s |= stairData.getFacing().ordinal() << 9;
Integer newBlockState = connectedBlocks.get(s);
return newBlockState == null ? blockState : newBlockState;
}
private int getShape(UserConnection user, Position position, StairData stair) {
BlockFace facing = stair.getFacing();
StairData relativeStair = stairDataMap.get(getBlockData(user, position.getRelative(facing)));
if (relativeStair != null && relativeStair.isBottom() == stair.isBottom()) {
BlockFace facing2 = relativeStair.getFacing();
if (facing.getAxis() != facing2.getAxis() && checkOpposite(user, stair, position, facing2.opposite())){
return facing2 == rotateAntiClockwise(facing) ? 3 : 4; // outer_left : outer_right
}
}
relativeStair = stairDataMap.get(getBlockData(user, position.getRelative(facing.opposite())));
if(relativeStair != null && relativeStair.isBottom() == stair.isBottom()) {
BlockFace facing2 = relativeStair.getFacing();
if (facing.getAxis() != facing2.getAxis() && checkOpposite(user, stair, position, facing2)){
return facing2 == rotateAntiClockwise(facing) ? 1 : 2; // inner_left : inner_right
}
}
return 0; // straight
}
private boolean checkOpposite(UserConnection user, StairData stair, Position position, BlockFace face) {
StairData relativeStair = stairDataMap.get(getBlockData(user, position.getRelative(face)));
return relativeStair == null || relativeStair.getFacing() != stair.getFacing() || relativeStair.isBottom() != stair.isBottom();
}
private BlockFace rotateAntiClockwise(BlockFace face) {
switch (face) {
case NORTH:
return BlockFace.WEST;
case SOUTH:
return BlockFace.EAST;
case EAST:
return BlockFace.NORTH;
case WEST:
return BlockFace.SOUTH;
default:
return face;
}
}
@AllArgsConstructor
@Getter
@ToString
private static class StairData {
private final boolean bottom;
private final byte shape, type;
private final BlockFace facing;
}
}

View File

@ -0,0 +1,57 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.BlockFace;
import us.myles.ViaVersion.api.minecraft.Position;
public class WallConnectionHandler extends AbstractFenceConnectionHandler {
private static final BlockFace[] BLOCK_FACES = { BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST };
private static final int[] OPPOSITES = { 3, 2, 1, 0 };
static void init() {
new WallConnectionHandler("cobbleWallConnections", "minecraft:cobblestone_wall");
new WallConnectionHandler("cobbleWallConnections", "minecraft:mossy_cobblestone_wall");
}
public WallConnectionHandler(String blockConnections, String key) {
super(blockConnections, key);
}
@Override
protected byte getStates(WrappedBlockData blockData) {
byte states = super.getStates(blockData);
if (blockData.getValue("up").equals("true")) states |= 32;
return states;
}
protected byte getStates(UserConnection user, Position position, int blockState) {
byte states = super.getStates(user, position, blockState);
if (up(user, position)) states |= 32;
return states;
}
public boolean up(UserConnection user, Position position) {
if(isWall(getBlockData(user, position.getRelative(BlockFace.BOTTOM))) || isWall(getBlockData(user, position.getRelative(BlockFace.TOP))))return true;
int blockFaces = getBlockFaces(user, position);
if (blockFaces == 0 || blockFaces == 0xF) return true;
for (int i = 0; i < BLOCK_FACES.length; i++) {
if ((blockFaces & (1 << i)) != 0 && (blockFaces & (1 << OPPOSITES[i])) == 0) return true;
}
return false;
}
private int getBlockFaces(UserConnection user, Position position) {
int blockFaces = 0;
for (int i = 0; i < BLOCK_FACES.length; i++) {
if (isWall(getBlockData(user, position.getRelative(BLOCK_FACES[i])))) {
blockFaces |= 1 << i;
}
}
return blockFaces;
}
private boolean isWall(int id){
return getBlockStates().contains(id);
}
}

View File

@ -0,0 +1,68 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections;
import lombok.Getter;
import us.myles.ViaVersion.api.Via;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class WrappedBlockData {
@Getter
private String minecraftKey;
private LinkedHashMap<String, String> blockData = new LinkedHashMap<>();
public static WrappedBlockData fromString(String s) {
String[] array = s.split("\\[");
String key = array[0];
WrappedBlockData wrappedBlockdata = new WrappedBlockData(key);
if (array.length > 1) {
String blockData = array[1];
blockData = blockData.replace("]", "");
String[] data = blockData.split(",");
for (String d : data) {
String[] a = d.split("=");
wrappedBlockdata.blockData.put(a[0], a[1]);
}
}
return wrappedBlockdata;
}
public static WrappedBlockData fromStateId(int id) {
String blockData = ConnectionData.getKey(id);
if (blockData != null) {
return fromString(blockData);
}
Via.getPlatform().getLogger().info("Unable to get blockdata from " + id);
return fromString("minecraft:air");
}
private WrappedBlockData(String key) {
minecraftKey = key;
}
public String toString() {
StringBuilder sb = new StringBuilder(minecraftKey + "[");
for (Entry<String, String> entry : blockData.entrySet()) {
sb.append(entry.getKey()).append('=').append(entry.getValue()).append(',');
}
return sb.substring(0, sb.length()-1) + "]";
}
public int getBlockStateId() {
return ConnectionData.getId(toString());
}
public WrappedBlockData set(String data, Object value) {
if (!hasData(data)) throw new UnsupportedOperationException("No blockdata found for " + data + " at " + minecraftKey);
blockData.put(data, value.toString());
return this;
}
public String getValue(String data) {
return blockData.get(data);
}
public boolean hasData(String key) {
return blockData.containsKey(key);
}
}

View File

@ -0,0 +1,34 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.api.platform.providers.Provider;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData;
public class BlockConnectionProvider implements Provider {
public int getBlockdata(UserConnection connection, Position position) {
int oldId = getWorldBlockData(connection, position);
return MappingData.blockMappings.getNewBlock(oldId);
}
public int getWorldBlockData(UserConnection connection, Position position) {
return -1;
}
public void storeBlock(UserConnection connection, Position position, int blockState) {};
public void removeBlock(UserConnection connection, Position position) {};
public void storeBlock(UserConnection connection, long x, long y, long z, int blockState) {
storeBlock(connection, new Position(x, y, z), blockState);
}
public void clearStorage(UserConnection connection) {};
public void unloadChunk(UserConnection connection, int x, int z) {};
public boolean storesBlocks(){
return false;
}
}

View File

@ -0,0 +1,38 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage.BlockConnectionStorage;
public class PacketBlockConnectionProvider extends BlockConnectionProvider {
@Override
public void storeBlock(UserConnection connection, Position position, int blockState) {
connection.get(BlockConnectionStorage.class).store(position, blockState);
}
@Override
public void removeBlock(UserConnection connection, Position position) {
connection.get(BlockConnectionStorage.class).remove(position);
}
@Override
public int getBlockdata(UserConnection connection, Position position) {
return connection.get(BlockConnectionStorage.class).get(position);
}
@Override
public void clearStorage(UserConnection connection) {
connection.get(BlockConnectionStorage.class).clear();
}
@Override
public void unloadChunk(UserConnection connection, int x, int z) {
connection.get(BlockConnectionStorage.class).unloadChunk(x, z);
}
@Override
public boolean storesBlocks() {
return true;
}
}

View File

@ -16,6 +16,7 @@ import us.myles.ViaVersion.api.remapper.PacketRemapper;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.packets.State;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.ConnectionData;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.NamedSoundRewriter;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.Particle;
@ -173,6 +174,17 @@ public class WorldPackets {
Position position = wrapper.get(Type.POSITION, 0);
int newId = toNewId(wrapper.get(Type.VAR_INT, 0));
if (Via.getConfig().isServersideBlockConnections()) {
UserConnection userConnection = wrapper.user();
if (ConnectionData.connects(newId)) {
newId = ConnectionData.connect(userConnection, position, newId);
}
ConnectionData.updateBlockStorage(userConnection, position, newId);
ConnectionData.update(userConnection, position);
}
wrapper.set(Type.VAR_INT, 0, checkStorage(wrapper.user(), position, newId));
}
});
@ -191,6 +203,7 @@ public class WorldPackets {
public void handle(PacketWrapper wrapper) throws Exception {
int chunkX = wrapper.get(Type.INT, 0);
int chunkZ = wrapper.get(Type.INT, 1);
UserConnection userConnection = wrapper.user();
// Convert ids
for (BlockChangeRecord record : wrapper.get(Type.BLOCK_CHANGE_RECORD_ARRAY, 0)) {
int newBlock = toNewId(record.getBlockId());
@ -198,13 +211,52 @@ public class WorldPackets {
(long) (record.getHorizontal() >> 4 & 15) + (chunkX * 16),
(long) record.getY(),
(long) (record.getHorizontal() & 15) + (chunkZ * 16));
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position, newBlock);
}
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
}
for (BlockChangeRecord record : wrapper.get(Type.BLOCK_CHANGE_RECORD_ARRAY, 0)) {
int blockState = record.getBlockId();
Position position = new Position(
(long) (record.getHorizontal() >> 4 & 15) + (chunkX * 16),
(long) record.getY(),
(long) (record.getHorizontal() & 15) + (chunkZ * 16));
if (Via.getConfig().isServersideBlockConnections()) {
if (ConnectionData.connects(blockState)) {
blockState = ConnectionData.connect(userConnection, position, blockState);
record.setBlockId(blockState);
}
ConnectionData.update(userConnection, position);
}
}
}
});
}
});
// Unload Chunk
protocol.registerOutgoing(State.PLAY, 0x1D, 0x1F, new PacketRemapper() {
@Override
public void registerMap() {
if(Via.getConfig().isServersideBlockConnections()){
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int x = wrapper.passthrough(Type.INT);
int z = wrapper.passthrough(Type.INT);
ConnectionData.getProvider().unloadChunk(wrapper.user(), x, z);
}
});
}
}
});
// Named Sound Effect TODO String -> Identifier? Check if identifier is present?
protocol.registerOutgoing(State.PLAY, 0x19, 0x1A, new PacketRemapper() {
@Override
@ -245,7 +297,7 @@ public class WorldPackets {
for (int p = 0; p < section.getPaletteSize(); p++) {
int old = section.getPaletteEntry(p);
int newId = toNewId(old);
if (storage.isWelcome(newId)) {
if (storage.isWelcome(newId) || (Via.getConfig().isServersideBlockConnections() && ConnectionData.needStoreBlocks() && ConnectionData.isWelcome(newId))) {
willStoreAnyBlock = true;
}
section.setPaletteEntry(p, newId);
@ -263,12 +315,22 @@ public class WorldPackets {
(long) (z + (chunk.getZ() << 4))
), block);
}
if (Via.getConfig().isServersideBlockConnections() && ConnectionData.isWelcome(block)) {
ConnectionData.getProvider().storeBlock(wrapper.user(), (long) (x + (chunk.getX() << 4)),
(long) (y + (i << 4)),
(long) (z + (chunk.getZ() << 4)),
block);
}
}
}
}
}
}
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.connectBlocks(wrapper.user(), chunk);
}
// Rewrite biome id 255 to plains
if (chunk.isBiomeData()) {
int latestBiomeWarn = Integer.MIN_VALUE;

View File

@ -0,0 +1,75 @@
package us.myles.ViaVersion.protocols.protocol1_13to1_12_2.storage;
import lombok.Data;
import lombok.EqualsAndHashCode;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.data.StoredObject;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import java.util.HashMap;
import java.util.Map;
public class BlockConnectionStorage extends StoredObject {
private Map<Pair<Integer, Integer>, Map<BlockPositon, Integer>> blockStorage = new HashMap<>();
public BlockConnectionStorage(UserConnection user) {
super(user);
}
public void store(Position position, int blockState) {
Pair pair = getPair(position);
Map<BlockPositon, Integer> map = getChunkMap(pair);
map.put(new BlockPositon(position), blockState);
}
public int get(Position position) {
Pair pair = getPair(position);
Map<BlockPositon, Integer> map = getChunkMap(pair);
BlockPositon blockPositon = new BlockPositon(position);
return map.containsKey(blockPositon) ? map.get(blockPositon) : 0;
}
public void remove(Position position) {
Pair pair = getPair(position);
Map<BlockPositon, Integer> map = getChunkMap(pair);
map.remove(new BlockPositon(position));
if(map.isEmpty()){
blockStorage.remove(pair);
}
}
public void clear(){
blockStorage.clear();
}
public void unloadChunk(int x, int z){
blockStorage.remove(new Pair<>(x, z));
}
private Map<BlockPositon, Integer> getChunkMap(Pair pair){
Map<BlockPositon, Integer> map = blockStorage.get(pair);
if(map == null){
map = new HashMap<>();
blockStorage.put(pair, map);
}
return map;
}
private Pair<Integer, Integer> getPair(Position position){
int chunkX = (int) (position.getX() >> 4);
int chunkZ = (int) (position.getZ() >> 4);
return new Pair<>(chunkX, chunkZ);
}
@EqualsAndHashCode
@Data
private class BlockPositon {
int x,y,z;
public BlockPositon(Position position){
x = position.getX().intValue();
y = position.getY().intValue();
z = position.getZ().intValue();
}
}
}

View File

@ -151,4 +151,10 @@ replacement-piston-id: 0
# Force the string -> json transform
force-json-transform: false
# Minimize the cooldown animation in 1.8 servers
minimize-cooldown: true
minimize-cooldown: true
# Enable serverside block-connections for 1.13+ clients
serverside-blockconnections: false
# Sets the method for the block connections (world for world-level or packet for packet-level)
blockconnection-method: world
# When activated, only the most important blocks are stored in the blockstorage. (fences, glass panes etc. won't connect to solid blocks)
reduce-blockstorage-memory: false

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -225,4 +225,19 @@ public class SpongeViaConfig extends Config implements ViaVersionConfig {
public boolean isDisable1_13AutoComplete() {
return getBoolean("disable-1_13-auto-complete", false);
}
@Override
public boolean isServersideBlockConnections() {
return getBoolean("serverside-blockconnections", false);
}
@Override
public String getBlockConnectionMethod() {
return getString("blockconnection-method", "world");
}
@Override
public boolean isRedueBlockStorageMemory() {
return getBoolean("reduce-blockstorage-memory", false);
}
}

View File

@ -8,6 +8,7 @@ import us.myles.ViaVersion.api.minecraft.item.Item;
import us.myles.ViaVersion.api.platform.TaskId;
import us.myles.ViaVersion.api.platform.ViaPlatformLoader;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.HandItemProvider;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider;
@ -18,6 +19,7 @@ import us.myles.ViaVersion.sponge.listeners.protocol1_9to1_8.DeathListener;
import us.myles.ViaVersion.sponge.listeners.protocol1_9to1_8.HandItemCache;
import us.myles.ViaVersion.sponge.listeners.protocol1_9to1_8.sponge4.Sponge4ArmorListener;
import us.myles.ViaVersion.sponge.listeners.protocol1_9to1_8.sponge5.Sponge5ArmorListener;
import us.myles.ViaVersion.sponge.providers.SpongeBlockConnectionProvider;
import us.myles.ViaVersion.sponge.providers.SpongeViaBulkChunkTranslator;
import us.myles.ViaVersion.sponge.providers.SpongeViaMovementTransmitter;
@ -78,6 +80,9 @@ public class SpongeViaLoader implements ViaPlatformLoader {
}
}
});
if (Via.getConfig().getBlockConnectionMethod().equalsIgnoreCase("world")) {
Via.getManager().getProviders().use(BlockConnectionProvider.class, new SpongeBlockConnectionProvider());
}
}
public void unload() {

View File

@ -0,0 +1,57 @@
package us.myles.ViaVersion.sponge.providers;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.block.BlockState;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.world.Chunk;
import org.spongepowered.api.world.World;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.minecraft.Position;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.blockconnections.providers.BlockConnectionProvider;
import us.myles.ViaVersion.util.ReflectionUtil;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class SpongeBlockConnectionProvider extends BlockConnectionProvider {
private static Class block;
private static Map<Object, Integer> blockStateIds;
static {
try {
block = Class.forName("net.minecraft.block.Block");
blockStateIds = ReflectionUtil.get(
ReflectionUtil.getStatic(block, "field_176229_d", Object.class),
"field_148749_a", Map.class);
} catch (ClassNotFoundException e) {
Via.getPlatform().getLogger().warning("net.minecraft.block.Block not found! Are you using Lantern?");
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
@Override
public int getWorldBlockData(UserConnection user, Position position) {
if (blockStateIds != null) {
UUID uuid = user.get(ProtocolInfo.class).getUuid();
Optional<Player> player = Sponge.getServer().getPlayer(uuid);
if (player.isPresent()) {
World world = player.get().getWorld();
Optional<Chunk> chunk = world.getChunkAtBlock(position.getX().intValue(), position.getY().intValue(), position.getZ().intValue());
if (chunk.isPresent()) {
BlockState b = chunk.get().getBlock(position.getX().intValue(), position.getY().intValue(), position.getZ().intValue());
Integer id = blockStateIds.get(b);
if (id == null) {
System.out.println("id not found");
} else {
return id;
}
}
}
}
return 0;
}
}

View File

@ -277,4 +277,19 @@ public class VelocityViaConfig extends Config implements ViaVersionConfig {
public boolean isDisable1_13AutoComplete() {
return getBoolean("disable-1_13-auto-complete", false);
}
@Override
public boolean isServersideBlockConnections() {
return getBoolean("serverside-blockconnections", false);
}
@Override
public String getBlockConnectionMethod() {
return "packet";
}
@Override
public boolean isRedueBlockStorageMemory() {
return getBoolean("reduce-blockstorage-memory", false);
}
}