Как синхронизировать BlockState

Версия Minecraft
1.12.2
85
3
3
Здравствуйте ,как можно синхронизировать BlockState в мультиплеере ?

Я сделал переменную "open" и блок ящика ,через пакеты меняю значение переменной "open" ,это криво работает в мультиплеере.

BlockState делал по тутору из учебника.
 
7,099
324
1,510
World#setBlockState, почитай его javadoc, там написано про флаги, вызывай этот метод в флагом отправки на клиент
 
85
3
3
Отправка пакета
Java:
                    RayTraceResult objectMouseOver = Minecraft.getMinecraft().objectMouseOver;
                    if (objectMouseOver != null && objectMouseOver.typeOfHit == RayTraceResult.Type.BLOCK) {
                        BlockPos blockPos = objectMouseOver.getBlockPos();
                        blockpos = blockPos;
                        if ( world.getBlockState(pos).getValue(OPEN) == false ) {
                            NetworkHandler.network.sendToServer(new PacketStates(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
                            world.notifyBlockUpdate(pos, state, state, 3);
                            return true;
                        }
                    }


Сам пакет
Java:
public class PacketStates implements IMessage {

    private int GetX;
    private int GetY;
    private int GetZ;

    public PacketStates() { }

    public PacketStates(int GetX,int GetY,int GetZ) {
        this.GetX = GetX;
        this.GetY = GetY;
        this.GetZ = GetZ;
    }

    @Override
    public void fromBytes(ByteBuf buf) {
        GetX = buf.readInt();
        GetY = buf.readInt();
        GetZ = buf.readInt();
    }

    @Override
    public void toBytes(ByteBuf buf) {
        buf.writeInt(GetX);
        buf.writeInt(GetY);
        buf.writeInt(GetZ);
    }


    public static class Handler implements IMessageHandler<PacketStates, IMessage> {

        @Override
        public IMessage onMessage(PacketStates message, MessageContext ctx) {

            EntityPlayerMP player = ctx.getServerHandler().player;
            WorldServer world = ctx.getServerHandler().player.getServerWorld();

            BlockPos pos = new BlockPos(message.GetX,message.GetY,message.GetZ);
            IBlockState state = world.getBlockState(pos);

            System.out.println(pos);

            if ( state != null && state != Blocks.AIR )
                world.setBlockState(pos, state.withProperty(OPEN, true));
                world.notifyBlockUpdate(pos, state, state, 3);

            return null;
        }
    }
}

Вот что получаю в итоге
1566287494353.png

1566287535699.png

Кривая синхронизация ,что я делаю не так ?
Помогите пожалуйста.
 
5,018
47
783
Чего ты велосипеды городишь? Посмотри как умные люди сделали, в ванилле

блокстейты автоматом синхронизируются, если поставить нужный флаг. Зайди в BlockStateContainer(вроде) и почитай про флаги. Аналогично посмотри как работает дверь, люк...
Флаг 2 вроде как отправляет пакет синхрона на клиент, а флаг 4 на сервер. Но не помню точно, смотри в коде.
 
85
3
3
Чего ты велосипеды городишь? Посмотри как умные люди сделали, в ванилле

блокстейты автоматом синхронизируются, если поставить нужный флаг. Зайди в BlockStateContainer(вроде) и почитай про флаги. Аналогично посмотри как работает дверь, люк...
Флаг 2 вроде как отправляет пакет синхрона на клиент, а флаг 4 на сервер. Но не помню точно, смотри в коде.

Да уже понял ,переделал ,сделал по вашему туториалу -Блоки с метадатой.
Но тут появилась одна проблема с синхронизацией...
1566486792183.png

Не понимаю как ее решить.
 
7,099
324
1,510
Типо один игрок кликает для смены стэйта, а у другого не отображается?
 
85
3
3
Java:
public class MCreatorTest {

   public static BlockPos blockpos;

   public static class BlockCustom extends Block implements ITileEntityProvider {

      public static final PropertyDirection FACING = BlockHorizontal.FACING;
      public static final PropertyBool OPENED = PropertyBool.create("open");
      public static final PropertyEnum<BlockCustom.EnumType> VARIANT = PropertyEnum.<BlockCustom.EnumType>create("variant", BlockCustom.EnumType.class);

      public BlockCustom(String name, float hardness, float resistanse , SoundType soundtype) {
         super(Material.WOOD);

         setRegistryName(name);
         setUnlocalizedName(name);
         setSoundType(soundtype);
         setHarvestLevel("axe", 1);
         setHardness(hardness);
         setResistance(resistanse);

         setCreativeTab(CreativeTabs.FOOD);
         this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH).withProperty(OPENED, Boolean.FALSE).withProperty(VARIANT, BlockCustom.EnumType.COBBLE_ANDESITE));
      }




      public boolean isOpaqueCube(IBlockState state)
      {
         return false;
      }

      public boolean isFullCube(IBlockState state)
      {
         return false;
      }

      public int damageDropped(IBlockState state)
      {
         return ((BlockCustom.EnumType)state.getValue(VARIANT)).getMetadata();
      }
      //метод позволяющий нам указать, откуда брать субтипы.
      public void getSubBlocks(CreativeTabs itemIn, NonNullList<ItemStack> items)
      {
         for (BlockCustom.EnumType blockstone$enumtype : BlockCustom.EnumType.values())
         {
            items.add(new ItemStack(this, 1, blockstone$enumtype.getMetadata()));
         }
      }

      public IBlockState getStateFromMeta(int meta)
      {
         return this.getDefaultState().withProperty(VARIANT, BlockCustom.EnumType.byMetadata(meta));
      }

      public int getMetaFromState(IBlockState state)
      {
         return ((BlockCustom.EnumType)state.getValue(VARIANT)).getMetadata();
      }

      //обязательно создаем контейнер стейтов
      protected BlockStateContainer createBlockState()
      {
         return new BlockStateContainer(this, new IProperty[] {FACING, OPENED, VARIANT});
      }


      @Override
      public IBlockState getStateForPlacement(World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer) {
         return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite());
      }

      //перечисление наших подтипов
      public static enum EnumType implements IStringSerializable {
         COBBLE_GRANITE(0, MapColor.DIRT, "granite", true),
         COBBLE_DIORITE(1, MapColor.QUARTZ, "diorite", true),
         COBBLE_ANDESITE(2, MapColor.STONE, "andesite", true);

         private static final BlockCustom.EnumType[] META_LOOKUP = new BlockCustom.EnumType[values().length];
         private final int meta;
         private final String name;
         private final String unlocalizedName;
         private final MapColor mapColor;
         private final boolean isNatural;

         private EnumType(int meta, MapColor mapColor, String name, boolean isNatural)
         {
            this(meta, mapColor, name, name, isNatural);
         }

         private EnumType(int meta, MapColor mapColor, String name, String Uname, boolean isNatural)
         {
            this.meta = meta;
            this.name = name;
            this.unlocalizedName = Uname;
            this.mapColor = mapColor;
            this.isNatural = isNatural;
         }

         public int getMetadata()
         {
            return this.meta;
         }

         public MapColor getMapColor()
         {
            return this.mapColor;
         }

         public String toString()
         {
            return this.name;
         }

         public static BlockCustom.EnumType byMetadata(int meta)
         {
            if (meta < 0 || meta >= META_LOOKUP.length)
            {
               meta = 0;
            }

            return META_LOOKUP[meta];
         }

         public String getName()
         {
            return this.name;
         }

         public String getUnlocalizedName()
         {
            return this.unlocalizedName;
         }

         public boolean isNatural()
         {
            return this.isNatural;
         }

         static
         {
            for (BlockCustom.EnumType blockstone$enumtype : values())
            {
               META_LOOKUP[blockstone$enumtype.getMetadata()] = blockstone$enumtype;
            }
         }
      }








      @Override
      public TileEntity createNewTileEntity(World worldIn, int meta) {
         return new TileEntityCustom();
      }


      @Override
      public void breakBlock(World world, BlockPos pos, IBlockState state) {
         TileEntity tileentity = world.getTileEntity(pos);
         InventoryHelper.dropInventoryItems(world, pos, (TileEntityCustom) tileentity);
         world.removeTileEntity(pos);
         super.breakBlock(world, pos, state);
      }

      @Override
      public boolean hasComparatorInputOverride(IBlockState state) {
         return true;
      }

      @Override
      public int getComparatorInputOverride(IBlockState blockState, World worldIn, BlockPos pos) {
         TileEntity tileentity = worldIn.getTileEntity(pos);
         if (tileentity instanceof TileEntityCustom) {
            return Container.calcRedstoneFromInventory((TileEntityCustom) tileentity);
         } else {
            return 0;
         }
      }

      @Override
      public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer entity, EnumHand hand, EnumFacing side,
                              float hitX, float hitY, float hitZ) {
         super.onBlockActivated(world, pos, state, entity, hand, side, hitX, hitY, hitZ);
         int x = pos.getX();
         int y = pos.getY();
         int z = pos.getZ();

         Block block = this;
         {
            java.util.HashMap<String, Object> $_dependencies = new java.util.HashMap<>();
            $_dependencies.put("entity", entity);
            $_dependencies.put("x", x);
            $_dependencies.put("y", y);
            $_dependencies.put("z", z);
            $_dependencies.put("world", world);

            blockpos = pos;

            if( !entity.isSneaking() )
            {
               TileEntity tileEntity = world.getTileEntity(pos);
               if( state.getValue(OPENED) )
               {
                  NetworkHandler.network.sendToServer(new PacketClose(pos.getX(), pos.getY(), pos.getZ()));
               }
               else
               {
                  ((EntityPlayer) entity).openGui(instance, MCreatorGui.GUIID, world, x, y, z);
                  NetworkHandler.network.sendToServer(new PacketStates(pos.getX(), pos.getY(), pos.getZ()));
               }
               world.setBlockState(pos, state.withProperty(OPENED, !state.getValue(OPENED)));
               if(tileEntity != null)
               {
                  tileEntity.validate();
                  world.setTileEntity(pos, tileEntity);
               }
            }

            return true;
         }
      }










      public static class TileEntityCustom extends TileEntityLockableLoot {

         private NonNullList<ItemStack> stacks = NonNullList.<ItemStack>withSize(9, ItemStack.EMPTY);
         private int type = 0;

         @Override
         public int getSizeInventory() {
            return 9;
         }

         @Override
         public boolean isEmpty() {
            for (ItemStack itemstack : this.stacks)
               if (!itemstack.isEmpty())
                  return false;
            return true;
         }

         @Override
         public boolean isItemValidForSlot(int index, ItemStack stack) {
            return true;
         }

         @Override
         public ItemStack getStackInSlot(int slot) {
            return stacks.get(slot);
         }

         @Override
         public String getName() {
            return this.hasCustomName() ? this.customName : "container.test";
         }











         public void setType(int type)
         {
            this.type = type;
         }

         public int getType()
         {
            return type;
         }

         @Override
         public void readFromNBT(NBTTagCompound compound) {
            super.readFromNBT(compound);

            this.type = compound.getInteger("type");

            this.stacks = NonNullList.<ItemStack>withSize(this.getSizeInventory(), ItemStack.EMPTY);
            if (!this.checkLootAndRead(compound))
               ItemStackHelper.loadAllItems(compound, this.stacks);
            if (compound.hasKey("CustomName", 8))
               this.customName = compound.getString("CustomName");
         }

         @Override
         public NBTTagCompound writeToNBT(NBTTagCompound compound) {
            super.writeToNBT(compound);

            compound.setInteger("type", type);

            if (!this.checkLootAndWrite(compound))
               ItemStackHelper.saveAllItems(compound, this.stacks);
            if (this.hasCustomName())
               compound.setString("CustomName", this.customName);
            return compound;
         }

         @Override
         public int getInventoryStackLimit() {
            return 64;
         }

         @Override
         public String getGuiID() {
            return "roleplay:test";
         }

         @Override
         public Container createContainer(InventoryPlayer playerInventory, EntityPlayer playerIn) {
            this.fillWithLoot(playerIn);
            return new ContainerDispenser(playerInventory, this);
         }

         @Override
         protected NonNullList<ItemStack> getItems() {
            return this.stacks;
         }







         @Override
         public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt)
         {
            readFromNBT(pkt.getNbtCompound());
         }

         @Override
         public SPacketUpdateTileEntity getUpdatePacket()
         {
            return new SPacketUpdateTileEntity(pos, getBlockMetadata(), getUpdateTag());
         }

         @Override
         public NBTTagCompound getUpdateTag()
         {
            return writeToNBT(new NBTTagCompound());
         }







         public void sync()
         {
            TileEntityUtil.syncToClient(this);
         }

      }
   }
}

Вот код моего блока ,мне нужно синхронизировать 3 переменные -
FACING ,OPENED ,VARIANT

На клиенте всё идеально отображается ,а у других игроков какая-то каша..
 
85
3
3
Вообщем немного полазил и заметил то что криво работают вместе - FACING и VARIANT
Java:
        public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
        {
            int metad = placer.getActiveItemStack().getItem().getMetadata(stack);
            worldIn.setBlockState(pos, state.withProperty(VARIANT, MCreatorTest.BlockCustom.EnumType.byMetadata( metad )).withProperty(FACING ,placer.getHorizontalFacing()), 2);
        }

У серверных игроков каша...
1566504965167.png
 
3,005
192
592
У тебя проперти 3 шт, а в гет стейт и гет мета - у тебя только 1 обрабатывается
 
7,099
324
1,510
85
3
3
Нужно реализовать getMetaFromState и getStateFromMeta с учетом нужных пропертисов


Java:
        public IBlockState getStateFromMeta(int meta)
        {
            EnumFacing enumfacing = EnumFacing.getFront(meta);
            IBlockState iblockstate = this.getDefaultState().withProperty(VARIANT, BlockCustom.EnumType.byMetadata((meta & 3) % 4));

            if (enumfacing.getAxis() == EnumFacing.Axis.Y)
            {
                enumfacing = EnumFacing.NORTH;
                iblockstate = iblockstate.withProperty(FACING, enumfacing);
            }

            return iblockstate;
        }

        public int getMetaFromState(IBlockState state)
        {
            int i = 0;
            i = i | ((BlockCustom.EnumType)state.getValue(VARIANT)).getMetadata();

            return ((EnumFacing)state.getValue(FACING)).getIndex() & i;
        }

        public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state)
        {
            this.checkForSurroundingChests(worldIn, pos, state);
        }

        public IBlockState onBlockPlaced(World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer)
        {
            return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing());
        }

        public void onBlockPlacedBy(World worldIn, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack)
        {
            EnumFacing enumfacing = EnumFacing.getHorizontal(MathHelper.floor((double)(placer.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3).getOpposite();

            state = state.withProperty(FACING, enumfacing);

            worldIn.setBlockState(pos, state, 3);
        }

У мультиплеерного игрока ставится нужный блок и через миллисекунду пропадает и появляется другой.
У игрока который открыл мир для сети ,всё нормально работает.
 
76
1
44
Чепуху написали с конвертацией в метаданные. Вариаций досок 6, направлений у вас 4. Немного математики и выясняется что всё это дело не помещается в 16 возможных значений метадаты.
Делайте по блоку на каждый вид дерева. И с портированием на 1,13 легче будет и метаданные освободите для FACING и OPEN. Еще одна булева проперти влезет при желании.
 
Последнее редактирование модератором:
5,018
47
783
В 1.13 разве есть ограничение по стейтам? Там вроде как вообще нету меты
 
Сверху