ArchitecturyBlockEntity to sync data to client (#10)

* ArchitecturyBlockEntity to sync data to client

* Add remap = false to @Shadow

* Upgrade gradle plugins

* Fix ArchitecturyBlockEntity on Forge

* Rename ArchitecturyBlockEntity to BlockEntityExtension
This commit is contained in:
shedaniel
2021-01-16 01:39:03 +08:00
committed by GitHub
parent e28f43db87
commit 50352219e5
9 changed files with 348 additions and 4 deletions

View File

@@ -0,0 +1,54 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package me.shedaniel.architectury.extensions;
import me.shedaniel.architectury.hooks.BlockEntityHooks;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Extensions to {@link net.minecraft.world.level.block.entity.BlockEntity}, implement this on to your class.
*/
public interface BlockEntityExtension {
/**
* Handles data sent by {@link BlockEntityExtension#saveClientData(CompoundTag)} on the server.
*/
@Environment(EnvType.CLIENT)
void loadClientData(@NotNull BlockState pos, @NotNull CompoundTag tag);
/**
* Writes data to sync to the client.
*/
@NotNull
CompoundTag saveClientData(@NotNull CompoundTag tag);
/**
* Sync data to the clients by {@link BlockEntityExtension#saveClientData(CompoundTag)} and {@link BlockEntityExtension#loadClientData(BlockState, CompoundTag)}.
*/
@ApiStatus.NonExtendable
default void syncData() {
BlockEntityHooks.syncData((BlockEntity) this);
}
}

View File

@@ -0,0 +1,35 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package me.shedaniel.architectury.hooks;
import me.shedaniel.architectury.ExpectPlatform;
import net.minecraft.world.level.block.entity.BlockEntity;
public class BlockEntityHooks {
private BlockEntityHooks() {}
/**
* Sync data to the clients.
*/
@ExpectPlatform
public static void syncData(BlockEntity entity) {
throw new AssertionError();
}
}

View File

@@ -0,0 +1,58 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package me.shedaniel.architectury.hooks.fabric;
import com.google.common.base.Preconditions;
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import java.util.Objects;
public class BlockEntityHooksImpl {
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public static void syncData(BlockEntity entity) {
if (entity instanceof BlockEntityClientSerializable) {
((BlockEntityClientSerializable) entity).sync();
} else {
Level world = Objects.requireNonNull(entity.getLevel());
if (!(world instanceof ServerLevel)) {
throw new IllegalStateException("Cannot call sync() on the logical client! Did you check world.isClient first?");
} else {
((ServerLevel) world).getChunkSource().blockChanged(entity.getBlockPos());
}
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package me.shedaniel.architectury.mixin.fabric;
import me.shedaniel.architectury.extensions.BlockEntityExtension;
import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(BlockEntityExtension.class)
public interface MixinBlockEntityExtension extends BlockEntityClientSerializable {
@Shadow(remap = false)
@NotNull
CompoundTag saveClientData(@NotNull CompoundTag tag);
@Shadow(remap = false)
void loadClientData(@NotNull BlockState pos, @NotNull CompoundTag tag);
@Override
default void fromClientTag(CompoundTag tag) {
BlockEntity entity = (BlockEntity) this;
if (entity.hasLevel()) {
entity.setLevelAndPosition(entity.getLevel(), new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z")));
loadClientData(entity.getBlockState(), tag);
}
}
@Override
default CompoundTag toClientTag(CompoundTag tag) {
return saveClientData(tag);
}
}

View File

@@ -9,9 +9,9 @@
"client.MixinTextureAtlas"
],
"mixins": [
"ExplosionPreInvoker", "LivingDeathInvoker", "MixinBlockItem", "MixinCommands", "MixinDedicatedServer", "MixinExplosion", "MixinFurnaceResultSlot",
"MixinItemEntity", "MixinLivingEntity", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot", "MixinServerGamePacketListenerImpl",
"MixinServerLevel", "MixinServerPlayer", "MixinServerPlayerGameMode", "PlayerAttackInvoker"
"ExplosionPreInvoker", "LivingDeathInvoker", "MixinBlockEntityExtension", "MixinBlockItem", "MixinCommands", "MixinDedicatedServer", "MixinExplosion",
"MixinFurnaceResultSlot", "MixinItemEntity", "MixinLivingEntity", "MixinPlayer", "MixinPlayerAdvancements", "MixinPlayerList", "MixinResultSlot",
"MixinServerGamePacketListenerImpl", "MixinServerLevel", "MixinServerPlayer", "MixinServerPlayerGameMode", "PlayerAttackInvoker"
],
"injectors": {
"defaultRequire": 1

View File

@@ -0,0 +1,52 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package me.shedaniel.architectury.hooks.forge;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import java.util.Objects;
public class BlockEntityHooksImpl {
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public static void syncData(BlockEntity entity) {
Level world = Objects.requireNonNull(entity.getLevel());
if (!(world instanceof ServerLevel)) {
throw new IllegalStateException("Cannot call sync() on the logical client! Did you check world.isClient first?");
} else {
((ServerLevel) world).getChunkSource().blockChanged(entity.getBlockPos());
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package me.shedaniel.architectury.mixin.forge;
import me.shedaniel.architectury.extensions.BlockEntityExtension;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(BlockEntity.class)
public abstract class MixinBlockEntity {
@Inject(method = "getUpdatePacket", at = @At("HEAD"), cancellable = true)
public void getUpdatePacket(CallbackInfoReturnable<ClientboundBlockEntityDataPacket> cir) {
if (this instanceof BlockEntityExtension) {
BlockEntityExtension entity = (BlockEntityExtension) this;
BlockEntity be = (BlockEntity) entity;
cir.setReturnValue(new ClientboundBlockEntityDataPacket(be.getBlockPos(), 10, be.getUpdateTag()));
cir.cancel();
}
}
@Inject(method = "getUpdateTag", at = @At("HEAD"), cancellable = true)
public void getUpdateTag(CallbackInfoReturnable<CompoundTag> cir) {
if (this instanceof BlockEntityExtension) {
BlockEntityExtension entity = (BlockEntityExtension) this;
BlockEntity be = (BlockEntity) entity;
cir.setReturnValue(entity.saveClientData(new CompoundTag()));
cir.cancel();
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 shedaniel
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package me.shedaniel.architectury.mixin.forge;
import me.shedaniel.architectury.extensions.BlockEntityExtension;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.extensions.IForgeTileEntity;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(BlockEntityExtension.class)
public interface MixinBlockEntityExtension extends IForgeTileEntity {
@Shadow(remap = false)
void loadClientData(@NotNull BlockState pos, @NotNull CompoundTag tag);
@Override
default void handleUpdateTag(BlockState state, CompoundTag tag) {
loadClientData(state, tag);
}
}

View File

@@ -5,7 +5,7 @@
"minVersion": "0.8",
"client": [
],
"mixins": ["BiomeGenerationSettingsBuilderAccessor", "MobSpawnSettingsBuilderAccessor"],
"mixins": ["BiomeGenerationSettingsBuilderAccessor", "MixinBlockEntity", "MixinBlockEntityExtension", "MobSpawnSettingsBuilderAccessor"],
"injectors": {
"defaultRequire": 1
}