Skip to content

Commit

Permalink
feat: support inventory preview on multi player server (#1)
Browse files Browse the repository at this point in the history
* 23w51b-SNAP 0.20.0-snap

* fix Block/Entity Reach overrides

* 1.20.x revert

* IntelliJ Changes

* IntelliJ Changes

* IntelliJ Changes

* IntelliJ Changes

* Update Configs.java

* IntelliJ Changes

* 24w03a - No changes

* 24w03b - Adds debugLogger() and attempts to replace the deprecated calls to isLiquid() -- Perhaps move to malilib if working.

* 24w03b - Adds debugLogger() and attempts to replace the deprecated calls to isLiquid() -- Perhaps move to malilib if working.

* 24w04a - Removed some non-working dev code

* 24w04a - gradle 8.6

* 24w05b - half works and runs, but some features are broken due to renderer issues.

* 24w06a - renderer issues nearly fixed.  Still need to check visual rotation values for flexibleBlockPlacement Grid under MaLiLib

* 24w06a - Matrix4f Renderer methods fixed & fully implemented.

* 24w06a - merge from 0.19.1 & 0.19.2, elytra swap fix & map preview required shift

* Update gradle.properties

* 24w06a - added option to disable Trial Vault particles

* 24w06a - some code cleanups

* 24w06a - remove lame Trial spawner / vault particle rules and cleaned up some wording for the Block / Entity reach overrides with the Single Player warnings.

* 24w07a - some mixin target fixes

* 24w07a - some debug loggers enabled to debug clicking issues for issue # 485 -- Mac Mouse / Trackpad issue from 0.16.1/3 + ?

* 24w07a - added debuggers for tweakFastBlockPlacement workflow

* 24w07a - api / loom version bump

* 24w09a basic

* 24w09a - mostly works, check the Shulker Box Preview code yet.

* 24w09a -- loom build 5

* 24w09a - loom build 6

* 24w09a -- loom build 8

* Update gradle.properties

* 24w09a - fabric api 0.96.6

* 24w09a - add Bundle Preview

* 24w10a - basic item attribute modifier getValue() -> value() rename

* 24w10a ghost bug hunting

* 24w10a - some cleanups and fixing TWEAK_GAMMA_OVERRIDE correctly this time.

* 24w10a - ModMenu 10.0.0-alpha.3 update

* 24w10a - fabric 0.96.9 yarn build 8

* 24w11a - yarn build 2

* 24w11a - fabric 0.96.10 and shulker box preview fix

* 24w11a - chasing ghost bugs for ClickSlot packets -- Not finding any issues.

* 24w12a - fix itemStack.getItem().isDamagable() --> not yet working

* 24w12a - fix itemStack.getItem().isDamagable() ->
itemStack.isDamageable()

* 24w12a - loom 1.6, yarn build 3, api 0.96.12

* 24w13a - chat Hud fix

* 24w13a - yarn build 7, api 0.96.13

* 24w13a - add ModMenu parent for malilib

* 24w13a - update ModMenu Dependency to a custom SNAPSHOT build, because 10.0.0-alpha.3 does not support 24w13a correctly.

* 24w14a - java 21, shulker box tooltip slight Mixin change

* 24w14a - some code cleanups

* 24w14a - some code cleanups

* 24w14a - Lava visibility math corrections

* 24w14a - gradle 8.7, yarn build 6

* 1.20.5-pre1 support -- rename mod_version to 0.20.0-beta.1

* 1.20.5-pre.1 - yarn build 4

* 1.20.5-pre.1 - yarn build 4

* merge from UPSTREAM: [QOL] Rehash Angel Block tweak using "floatato technology" (maruohon#496)

* some import cleanups

* 1.20.5-pre1-- code cleanups

* 1.20.5-pre.1 - code cleanups

* 1.20.5-pre.1 - code cleanups

* 1.20.5-pre.1 - code cleanups

* 1.20.5-pre.1 - code cleanups

* 1.20.5-pre.2 support

* 1.20.5-pre.3 support

* 1.20.5-pre.3 - Update lava visibility values to closer resemble the values under 1.20.4, with only a slight boost around maybe ~1.0-2.0f

* 1.20.5-pre.3 - Update lava visibility values to closer resemble the values under 1.20.4, with only a slight boost around maybe ~1.0-2.0f

* 1.20.5-pre.4 support

* 1.20.5-rc1 support

* 1.20.5-rc1 - code cleanups

* 1.20.5-rc1 - reach distance fix

* 1.20.5-rc1 - reach distance fix

* remove Network Reference call

* Fix code

* 1.20.5-rc.3

* code cleanups

* 1.20.5-DEV

* 1.20.5-DEV

* merge from PR

* "sakura.1" branding until official release

* "sakura.1" branding until official release

* DISABLE_WORLD_VIEW_BOB Mixin Conflict with Iris, lower priority fixes it.

* DISABLE_WORLD_VIEW_BOB Mixin Conflict with Iris, lower priority fixes it.

* 1.20.6 versioning

* 24w18a -- new Item Enchantments Components -->  Does not yet compile, no time to fix today.

* fixed enchantment level lookups, build 3, api 0.97.9

* fixed enchantment level lookups, build 3, api 0.97.9

* Update gradle.properties

* version bump

* fix shulker box / item container display.

* Update RenderHandler.java

* fix render call, add tickDelta fixes

* yarn build 3, waiting on FAPI

* FAPI / modmenu update

* Update fabric.mod.json

* Upgrade Shulker Box stacking code -- this allows non-modded players and non-modded servers to work with Tweakeroo stacked shulker boxes.

* Revert last Commit

* fix config minorly

* 24w21b - Waiting on FAPI

* yarn build 3

* FAPI update, and testing.  All seems to be working.

* getItemStackFromString check for null

* yard build 4

* yarn build 5

* yarn build 8

* 1.21-pre1

* 1.21-pre2 --> added tool / weapon swap parameters to check the item 'weight' with Rarity and enchantments versus the previous Item.  i.e.; a tool with more enchantments, or higher enchantment values is picked over the same item with lesser rarity.

* Merge from 1.20.6 for Shulker Boxes not dropping fix

* Create jitpack.yml

* Delete jitpack.yml

* ModMenu update

* 1.21-pre3 (Was toying with the Flat Presets code)

* ItemStack.getMaxCount() fix for mod compatibility if max_stack_size is configured.

* add jitpack

* add jitpack

* 1.21-pre4

* Create build.yml

* 1.21-rc1

* 1.21-rc1

* 1.21-rc1

* 1.21-rc1

* resync

* 1.21

* Disable Experimental Weapon / Tool swapping

* yarn build 2, modmenu beta2, version # bump

* feat: support inventory preview on multi player server

* fix

---------

Co-authored-by: Sakura Ryoko <[email protected]>
Co-authored-by: Sakura Ryoko <[email protected]>
  • Loading branch information
3 people authored Jun 20, 2024
1 parent 46c0ddf commit c980f89
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 18 deletions.
167 changes: 167 additions & 0 deletions src/main/java/fi/dy/masa/tweakeroo/data/ServerDataSyncer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package fi.dy.masa.tweakeroo.data;

import com.mojang.datafixers.util.Either;
import fi.dy.masa.tweakeroo.mixin.IMixinDataQueryHandler;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.ChestBlockEntity;
import net.minecraft.block.enums.ChestType;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.DataQueryHandler;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.inventory.DoubleInventory;
import net.minecraft.inventory.Inventory;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Pair;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@SuppressWarnings("deprecation")
public class ServerDataSyncer {
public static ServerDataSyncer INSTANCE;

/**
* key: BlockPos
* value: data, timestamp
*/
private final Map<BlockPos, Pair<BlockEntity, Long>> blockCache = new HashMap<>();
private final Map<Integer, Pair<Entity, Long>> entityCache = new HashMap<>();
private final Map<Integer, Either<BlockPos, Integer>> pendingQueries = new HashMap<>();
private final ClientWorld clientWorld;

public ServerDataSyncer(ClientWorld world) {
this.clientWorld = Objects.requireNonNull(world);
}

private @Nullable BlockEntity getCache(BlockPos pos) {
var data = blockCache.get(pos);
if (data != null && System.currentTimeMillis() - data.getRight() <= 1000) {
if (System.currentTimeMillis() - data.getRight() > 500) {
syncBlockEntity(clientWorld, pos);
}
return data.getLeft();
}

return null;
}

private @Nullable Entity getCache(int networkId) {
var data = entityCache.get(networkId);
if (data != null && System.currentTimeMillis() - data.getRight() <= 1000) {
if (System.currentTimeMillis() - data.getRight() > 500) {
syncEntity(networkId);
}
return data.getLeft();
}

return null;
}

public void handleQueryResponse(int transactionId, NbtCompound nbt) {
if (nbt == null) return;
if (pendingQueries.containsKey(transactionId)) {
Either<BlockPos, Integer> either = pendingQueries.remove(transactionId);
either.ifLeft(pos -> {
if (!clientWorld.isChunkLoaded(pos)) return;
BlockState state = clientWorld.getBlockState(pos);
if (state.getBlock() instanceof BlockEntityProvider provider) {
var be = provider.createBlockEntity(pos, state);
if (be != null) {
be.read(nbt, clientWorld.getRegistryManager());
blockCache.put(pos, new Pair<>(be, System.currentTimeMillis()));
}
}
}).ifRight(id -> {
Entity entity = clientWorld.getEntityById(id).getType().create(clientWorld);
if (entity != null) {
entity.readNbt(nbt);
entityCache.put(id, new Pair<>(entity, System.currentTimeMillis()));
}
});
}
if (blockCache.size() > 30) {
blockCache.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue().getRight() > 1000);
}
if (entityCache.size() > 30) {
entityCache.entrySet().removeIf(entry -> System.currentTimeMillis() - entry.getValue().getRight() > 1000);
}
}

public Inventory getBlockInventory(World world, BlockPos pos) {
if (!world.isChunkLoaded(pos)) return null;
var data = getCache(pos);
if (data instanceof Inventory inv) {
BlockState state = world.getBlockState(pos);
if (state.getBlock() instanceof ChestBlock && data instanceof ChestBlockEntity) {
ChestType type = state.get(ChestBlock.CHEST_TYPE);

if (type != ChestType.SINGLE) {
BlockPos posAdj = pos.offset(ChestBlock.getFacing(state));
if (!world.isChunkLoaded(posAdj)) return null;
BlockState stateAdj = world.getBlockState(posAdj);

var dataAdj = getCache(posAdj);
if (dataAdj == null) {
syncBlockEntity(world, posAdj);
}

if (stateAdj.getBlock() == state.getBlock() &&
dataAdj instanceof ChestBlockEntity inv2 &&
stateAdj.get(ChestBlock.CHEST_TYPE) != ChestType.SINGLE &&
stateAdj.get(ChestBlock.FACING) == state.get(ChestBlock.FACING)) {
Inventory invRight = type == ChestType.RIGHT ? inv : inv2;
Inventory invLeft = type == ChestType.RIGHT ? inv2 : inv;
inv = new DoubleInventory(invRight, invLeft);
}
}
}
return inv;
}

syncBlockEntity(world, pos);
return null;
}

public void syncBlockEntity(World world, BlockPos pos) {
if (MinecraftClient.getInstance().isIntegratedServerRunning()) {
BlockEntity blockEntity = MinecraftClient.getInstance().getServer().getWorld(world.getRegistryKey()).getWorldChunk(pos).getBlockEntity(pos, WorldChunk.CreationType.CHECK);
if (blockEntity != null) {
blockCache.put(pos, new Pair<>(blockEntity, System.currentTimeMillis()));
return;
}
}
Either<BlockPos, Integer> posEither = Either.left(pos);
if (MinecraftClient.getInstance().getNetworkHandler() != null && !pendingQueries.containsValue(posEither)) {
DataQueryHandler handler = MinecraftClient.getInstance().getNetworkHandler().getDataQueryHandler();
handler.queryBlockNbt(pos, it -> {});
pendingQueries.put(((IMixinDataQueryHandler) handler).currentTransactionId(), posEither);
}
}

public void syncEntity(int networkId) {
Either<BlockPos, Integer> idEither = Either.right(networkId);
if (MinecraftClient.getInstance().getNetworkHandler() != null && !pendingQueries.containsValue(idEither)) {
DataQueryHandler handler = MinecraftClient.getInstance().getNetworkHandler().getDataQueryHandler();
handler.queryEntityNbt(networkId, it -> {});
pendingQueries.put(((IMixinDataQueryHandler) handler).currentTransactionId(), idEither);
}
}

public @Nullable Entity getServerEntity(Entity entity) {
Entity serverEntity = getCache(entity.getId());
if (serverEntity == null) {
syncEntity(entity.getId());
return null;
}
return serverEntity;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package fi.dy.masa.tweakeroo.mixin;

import net.minecraft.client.network.DataQueryHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;

@Mixin(DataQueryHandler.class)
public interface IMixinDataQueryHandler {
@Accessor("expectedTransactionId")
int currentTransactionId();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package fi.dy.masa.tweakeroo.mixin;

import fi.dy.masa.tweakeroo.data.ServerDataSyncer;
import net.minecraft.client.network.DataQueryHandler;
import net.minecraft.nbt.NbtCompound;
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(DataQueryHandler.class)
public class MixinDataQueryHandler {
@Inject(
method = "handleQueryResponse",
at = @At("HEAD")
)
private void queryResponse(int transactionId, NbtCompound nbt, CallbackInfoReturnable<Boolean> cir) {
ServerDataSyncer.INSTANCE.handleQueryResponse(transactionId, nbt);
}
}
14 changes: 14 additions & 0 deletions src/main/java/fi/dy/masa/tweakeroo/mixin/MixinMinecraftClient.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package fi.dy.masa.tweakeroo.mixin;

import fi.dy.masa.tweakeroo.data.ServerDataSyncer;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
Expand Down Expand Up @@ -130,4 +131,17 @@ private void onProcessKeybindsPre(CallbackInfo ci)
}
}
}

@Inject(
method = "setWorld",
at = @At("HEAD")
)
private void onWorldChanged(ClientWorld world, CallbackInfo ci)
{
if (world == null) {
ServerDataSyncer.INSTANCE = null;
} else {
ServerDataSyncer.INSTANCE = new ServerDataSyncer(world);
}
}
}
43 changes: 25 additions & 18 deletions src/main/java/fi/dy/masa/tweakeroo/renderer/RenderUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

import java.util.Set;
import com.mojang.blaze3d.systems.RenderSystem;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;
import fi.dy.masa.malilib.util.EntityUtils;
import fi.dy.masa.malilib.util.GuiUtils;
import fi.dy.masa.tweakeroo.config.Configs;
import fi.dy.masa.tweakeroo.data.ServerDataSyncer;
import fi.dy.masa.tweakeroo.mixin.IMixinAbstractHorseEntity;
import fi.dy.masa.tweakeroo.util.MiscUtils;
import fi.dy.masa.tweakeroo.util.RayTraceUtils;
import fi.dy.masa.tweakeroo.util.SnapAimMode;

import net.minecraft.block.Block;
import net.minecraft.block.ShulkerBoxBlock;
Expand Down Expand Up @@ -31,14 +37,10 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import org.joml.Matrix4f;
import org.joml.Matrix4fStack;

import fi.dy.masa.malilib.util.EntityUtils;
import fi.dy.masa.malilib.util.GuiUtils;
import fi.dy.masa.tweakeroo.config.Configs;
import fi.dy.masa.tweakeroo.mixin.IMixinAbstractHorseEntity;
import fi.dy.masa.tweakeroo.util.MiscUtils;
import fi.dy.masa.tweakeroo.util.RayTraceUtils;
import fi.dy.masa.tweakeroo.util.SnapAimMode;
import java.util.Set;

public class RenderUtils
{
Expand Down Expand Up @@ -133,13 +135,8 @@ public static void renderInventoryOverlay(MinecraftClient mc, DrawContext drawCo

HitResult trace = RayTraceUtils.getRayTraceFromEntity(world, cameraEntity, false);

if (trace == null)
{
return;
}

Inventory inv = null;
ShulkerBoxBlock block = null;
ShulkerBoxBlock shulkerBoxBlock = null;
LivingEntity entityLivingBase = null;

if (trace.getType() == HitResult.Type.BLOCK)
Expand All @@ -149,14 +146,24 @@ public static void renderInventoryOverlay(MinecraftClient mc, DrawContext drawCo

if (blockTmp instanceof ShulkerBoxBlock)
{
block = (ShulkerBoxBlock) blockTmp;
shulkerBoxBlock = (ShulkerBoxBlock) blockTmp;
}

inv = fi.dy.masa.malilib.util.InventoryUtils.getInventory(world, pos);
if (world instanceof ServerWorld realWorld) {
inv = fi.dy.masa.malilib.util.InventoryUtils.getInventory(realWorld, pos);
} else {
inv = ServerDataSyncer.INSTANCE.getBlockInventory(world, pos);
}
}
else if (trace.getType() == HitResult.Type.ENTITY)
{
Entity entity = ((EntityHitResult) trace).getEntity();
if (entity.getWorld().isClient) {
Entity serverEntity = ServerDataSyncer.INSTANCE.getServerEntity(entity);
if (serverEntity != null) {
entity = serverEntity;
}
}

if (entity instanceof LivingEntity)
{
Expand Down Expand Up @@ -207,7 +214,7 @@ else if (entity instanceof AbstractHorseEntity)
yInv = Math.min(yInv, yCenter - 92);
}

fi.dy.masa.malilib.render.RenderUtils.setShulkerboxBackgroundTintColor(block, Configs.Generic.SHULKER_DISPLAY_BACKGROUND_COLOR.getBooleanValue());
fi.dy.masa.malilib.render.RenderUtils.setShulkerboxBackgroundTintColor(shulkerBoxBlock, Configs.Generic.SHULKER_DISPLAY_BACKGROUND_COLOR.getBooleanValue());

if (isHorse)
{
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/fi/dy/masa/tweakeroo/util/InventoryUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,9 @@ private static void repairModeHandleSlot(PlayerEntity player, EquipmentSlot type
}
}

/**
* Adds the enchantment checks for Tools or Weapons
*/
private static int findRepairableItemNotInRepairableSlot(Slot targetSlot, PlayerEntity player)
{
ScreenHandler containerPlayer = player.currentScreenHandler;
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/mixins.tweakeroo.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"IMixinClientWorld",
"IMixinCommandBlockExecutor",
"IMixinCustomizeFlatLevelScreen",
"IMixinDataQueryHandler",
"IMixinShovelItem",
"IMixinSimpleOption",
"MixinAbstractClientPlayerEntity",
Expand All @@ -36,6 +37,7 @@
"MixinCloneCommand",
"MixinCommandBlockScreen",
"MixinCreativeInventoryScreen",
"MixinDataQueryHandler",
"MixinDimensionEffects_Nether",
"MixinEntity",
"MixinEntityRenderDispatcher",
Expand Down

0 comments on commit c980f89

Please sign in to comment.