From 804a389aaf0386339f44e86e15e54315afdd959c Mon Sep 17 00:00:00 2001 From: ArkoSammy12 Date: Thu, 15 Aug 2024 04:20:42 -0500 Subject: [PATCH] Send clients with mod installed extra metadata for the custom info lines included in the sent item stacks to allow for item stack syncing. Add new player filter feature for database querying --- .../client/ClientPlayNetworkHandlerMixin.java | 55 +++++++++++++++ .../mixin/client/HandledScreenMixin.java | 25 +++++++ .../PublicEnderChestClient.kt | 6 +- .../publicenderchest.client.mixins.json | 6 +- .../mixin/ItemStackMixin.java | 15 ++++ .../mixin/ScreenHandlerMixin.java | 45 ------------ .../mixin/ServerPlayerEntityMixin.java | 13 ++++ .../ServerPlayerEntitySyncHandlerMixin.java | 69 +++++++++++++++++++ .../util/ducks/ItemStackDuck.java | 5 ++ .../util/ducks/ServerPlayerEntityDuck.java | 4 ++ .../publicenderchest/PublicEnderChest.kt | 6 +- .../commands/DatabaseCommands.kt | 29 +++++++- .../logging/InventoryDatabaseManager.kt | 15 ++-- .../publicenderchest/logging/QueryContext.kt | 3 +- .../networking/C2SHandshakeResponsePayload.kt | 21 ++++++ .../networking/S2CHandshakeRequestPayload.kt | 21 ++++++ .../arkosammy/publicenderchest/util/Events.kt | 29 ++++++-- .../resources/publicenderchest.mixins.json | 3 +- 18 files changed, 309 insertions(+), 61 deletions(-) create mode 100644 src/client/java/xd/arkosammy/publicenderchest/mixin/client/ClientPlayNetworkHandlerMixin.java create mode 100644 src/client/java/xd/arkosammy/publicenderchest/mixin/client/HandledScreenMixin.java create mode 100644 src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntitySyncHandlerMixin.java create mode 100644 src/main/kotlin/xd/arkosammy/publicenderchest/networking/C2SHandshakeResponsePayload.kt create mode 100644 src/main/kotlin/xd/arkosammy/publicenderchest/networking/S2CHandshakeRequestPayload.kt diff --git a/src/client/java/xd/arkosammy/publicenderchest/mixin/client/ClientPlayNetworkHandlerMixin.java b/src/client/java/xd/arkosammy/publicenderchest/mixin/client/ClientPlayNetworkHandlerMixin.java new file mode 100644 index 0000000..26cb6a5 --- /dev/null +++ b/src/client/java/xd/arkosammy/publicenderchest/mixin/client/ClientPlayNetworkHandlerMixin.java @@ -0,0 +1,55 @@ +package xd.arkosammy.publicenderchest.mixin.client; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.LoreComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import xd.arkosammy.publicenderchest.PublicEnderChestKt; +import xd.arkosammy.publicenderchest.util.ducks.ItemStackDuck; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +@Mixin(ClientPlayNetworkHandler.class) +public abstract class ClientPlayNetworkHandlerMixin { + + @WrapOperation(method = "onScreenHandlerSlotUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/ScreenHandlerSlotUpdateS2CPacket;getStack()Lnet/minecraft/item/ItemStack;")) + private ItemStack restoreOriginalItemStackAndSaveCustomInfo(ScreenHandlerSlotUpdateS2CPacket instance, Operation original) { + ItemStack itemStack = original.call(instance); + LoreComponent loreComponent = itemStack.get(DataComponentTypes.LORE); + if (loreComponent == null) { + return itemStack; + } + String expectedMetadata = PublicEnderChestKt.formatInfoTextMetadata(instance.getSyncId(), instance.getRevision()); + List originalLines = new ArrayList<>(loreComponent.lines()); + List originalStyledLines = new ArrayList<>(loreComponent.styledLines()); + ItemStack actualItemStack = itemStack.copy(); + Predicate lineMetadataFilter = (line) -> { + boolean containsMetadata = line.getString().contains(expectedMetadata); + if (containsMetadata) { + Text customInfoLine = Text.literal(line.getString().replace(expectedMetadata, "")).formatted(Formatting.ITALIC, Formatting.DARK_PURPLE); + List currentCustomInfoLines = ((ItemStackDuck) (Object) actualItemStack).publicenderchest$getCustomInfoLines(); + if (!currentCustomInfoLines.contains(customInfoLine)) { + ((ItemStackDuck) (Object) actualItemStack).publicenderchest$addCustomInfoLine(customInfoLine); + } + return false; + } else { + return true; + } + }; + List actualLines = originalLines.stream().filter(lineMetadataFilter).toList(); + List actualStyledLines = originalStyledLines.stream().filter(lineMetadataFilter).toList(); + LoreComponent actualLoreComponent = new LoreComponent(actualLines, actualStyledLines); + actualItemStack.set(DataComponentTypes.LORE, actualLoreComponent); + return actualItemStack; + } + +} diff --git a/src/client/java/xd/arkosammy/publicenderchest/mixin/client/HandledScreenMixin.java b/src/client/java/xd/arkosammy/publicenderchest/mixin/client/HandledScreenMixin.java new file mode 100644 index 0000000..a4d37cd --- /dev/null +++ b/src/client/java/xd/arkosammy/publicenderchest/mixin/client/HandledScreenMixin.java @@ -0,0 +1,25 @@ +package xd.arkosammy.publicenderchest.mixin.client; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import xd.arkosammy.publicenderchest.util.ducks.ItemStackDuck; + +import java.util.ArrayList; +import java.util.List; + +@Mixin(HandledScreen.class) +public abstract class HandledScreenMixin { + + @ModifyReturnValue(method = "getTooltipFromItem", at = @At("RETURN")) + private List addCustomInfoLines(List original, ItemStack stack) { + List originalTooltipLines = new ArrayList<>(original); + List customInfoLines = ((ItemStackDuck) (Object) stack).publicenderchest$getCustomInfoLines(); + originalTooltipLines.addAll(customInfoLines); + return originalTooltipLines; + } + +} diff --git a/src/client/kotlin/xd/arkosammy/publicenderchest/PublicEnderChestClient.kt b/src/client/kotlin/xd/arkosammy/publicenderchest/PublicEnderChestClient.kt index 4789d0a..0ace898 100644 --- a/src/client/kotlin/xd/arkosammy/publicenderchest/PublicEnderChestClient.kt +++ b/src/client/kotlin/xd/arkosammy/publicenderchest/PublicEnderChestClient.kt @@ -2,13 +2,17 @@ package xd.arkosammy.publicenderchest import net.fabricmc.api.ClientModInitializer import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking +import xd.arkosammy.publicenderchest.networking.C2SHandshakeResponsePayload import xd.arkosammy.publicenderchest.networking.OpenPublicInventoryPayload +import xd.arkosammy.publicenderchest.networking.S2CHandshakeRequestPayload import java.util.* object PublicEnderChestClient : ClientModInitializer { override fun onInitializeClient() { - + ClientPlayNetworking.registerGlobalReceiver(S2CHandshakeRequestPayload.PACKET_ID) { payload, context -> + context.responseSender().sendPacket(C2SHandshakeResponsePayload(UUID.randomUUID())) + } } @JvmStatic diff --git a/src/client/resources/publicenderchest.client.mixins.json b/src/client/resources/publicenderchest.client.mixins.json index f72b60e..7a9a34b 100644 --- a/src/client/resources/publicenderchest.client.mixins.json +++ b/src/client/resources/publicenderchest.client.mixins.json @@ -3,9 +3,11 @@ "package": "xd.arkosammy.publicenderchest.mixin.client", "compatibilityLevel": "JAVA_21", "client": [ - "ClientPlayerInteractionManagerMixin" + "ClientPlayerInteractionManagerMixin", + "ClientPlayNetworkHandlerMixin", + "HandledScreenMixin" ], "injectors": { "defaultRequire": 1 - } + } } \ No newline at end of file diff --git a/src/main/java/xd/arkosammy/publicenderchest/mixin/ItemStackMixin.java b/src/main/java/xd/arkosammy/publicenderchest/mixin/ItemStackMixin.java index 4bb7eae..74baaf9 100644 --- a/src/main/java/xd/arkosammy/publicenderchest/mixin/ItemStackMixin.java +++ b/src/main/java/xd/arkosammy/publicenderchest/mixin/ItemStackMixin.java @@ -8,6 +8,8 @@ import xd.arkosammy.publicenderchest.util.ducks.ItemStackDuck; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; @Mixin(ItemStack.class) public class ItemStackMixin implements ItemStackDuck { @@ -20,6 +22,9 @@ public class ItemStackMixin implements ItemStackDuck { @Nullable private LocalDateTime insertedTime; + @Unique + private final List customInfoLines = new ArrayList<>(); + @Override public void publicenderchest$setInserterName(Text name) { this.inserterName = name; @@ -40,4 +45,14 @@ public class ItemStackMixin implements ItemStackDuck { return this.insertedTime; } + @Override + public void publicenderchest$addCustomInfoLine(Text text) { + this.customInfoLines.add(text); + } + + @Override + public List publicenderchest$getCustomInfoLines() { + return this.customInfoLines; + } + } diff --git a/src/main/java/xd/arkosammy/publicenderchest/mixin/ScreenHandlerMixin.java b/src/main/java/xd/arkosammy/publicenderchest/mixin/ScreenHandlerMixin.java index 72e0baf..a6464ce 100644 --- a/src/main/java/xd/arkosammy/publicenderchest/mixin/ScreenHandlerMixin.java +++ b/src/main/java/xd/arkosammy/publicenderchest/mixin/ScreenHandlerMixin.java @@ -1,25 +1,13 @@ package xd.arkosammy.publicenderchest.mixin; -import com.google.common.collect.ImmutableList; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.LoreComponent; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.screen.ScreenHandler; -import net.minecraft.screen.ScreenHandlerSyncHandler; -import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import xd.arkosammy.publicenderchest.inventory.CustomGenericContainerScreenHandler; import xd.arkosammy.publicenderchest.inventory.PublicInventory; -import xd.arkosammy.publicenderchest.inventory.PublicInventoryKt; -import xd.arkosammy.publicenderchest.util.CustomMutableText; - -import java.util.ArrayList; -import java.util.List; import java.util.function.Supplier; @Mixin(ScreenHandler.class) @@ -41,37 +29,4 @@ private boolean forceSlotUpdateForPublicInventory(boolean original, int slot, It return false; } - @WrapOperation(method = "checkSlotUpdates", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/ScreenHandlerSyncHandler;updateSlot(Lnet/minecraft/screen/ScreenHandler;ILnet/minecraft/item/ItemStack;)V")) - private void modifySentItemStack(ScreenHandlerSyncHandler instance, ScreenHandler screenHandler, int slot, ItemStack itemStack, Operation original) { - if (!(screenHandler instanceof CustomGenericContainerScreenHandler customScreenHandler)) { - original.call(instance, screenHandler, slot, itemStack); - return; - } - Inventory inventory = customScreenHandler.getInventory(); - if (!(inventory instanceof PublicInventory publicInventory)) { - original.call(instance, screenHandler, slot, itemStack); - return; - } - List customInfoLines = PublicInventoryKt.getCustomInfoLines(publicInventory.getStack(slot)); - if (customInfoLines.isEmpty()) { - original.call(instance, screenHandler, slot, itemStack); - return; - } - LoreComponent loreComponent = itemStack.get(DataComponentTypes.LORE); - List newLines = new ArrayList<>(); - if (loreComponent != null) { - newLines.addAll(loreComponent.lines()); - } - newLines.addAll(customInfoLines); - LoreComponent newLoreComponent; - if (loreComponent != null) { - newLoreComponent = new LoreComponent(ImmutableList.copyOf(newLines), ImmutableList.copyOf(loreComponent.styledLines())); - } else { - newLoreComponent = new LoreComponent(ImmutableList.copyOf(newLines)); - } - ItemStack sentItemStack = itemStack.copy(); - sentItemStack.set(DataComponentTypes.LORE, newLoreComponent); - original.call(instance, screenHandler, slot, sentItemStack); - } - } diff --git a/src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntityMixin.java b/src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntityMixin.java index 4846ee0..c6f19d5 100644 --- a/src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntityMixin.java @@ -38,10 +38,23 @@ public abstract class ServerPlayerEntityMixin extends PlayerEntity implements Se @Unique private int pageIndex = 0; + @Unique + private boolean hasMod = false; + public ServerPlayerEntityMixin(World world, BlockPos pos, float yaw, GameProfile gameProfile) { super(world, pos, yaw, gameProfile); } + @Override + public void publicenderchest$setHasMod(boolean hasMod) { + this.hasMod = hasMod; + } + + @Override + public boolean publicenderchest$hasMod() { + return this.hasMod; + } + @Override public void publicenderchest$setPageIndex(int pageIndex) { this.pageIndex = pageIndex; diff --git a/src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntitySyncHandlerMixin.java b/src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntitySyncHandlerMixin.java new file mode 100644 index 0000000..46da0ce --- /dev/null +++ b/src/main/java/xd/arkosammy/publicenderchest/mixin/ServerPlayerEntitySyncHandlerMixin.java @@ -0,0 +1,69 @@ +package xd.arkosammy.publicenderchest.mixin; + +import com.google.common.collect.ImmutableList; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.LoreComponent; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import xd.arkosammy.publicenderchest.PublicEnderChestKt; +import xd.arkosammy.publicenderchest.inventory.CustomGenericContainerScreenHandler; +import xd.arkosammy.publicenderchest.inventory.PublicInventory; +import xd.arkosammy.publicenderchest.inventory.PublicInventoryKt; +import xd.arkosammy.publicenderchest.util.CustomMutableText; +import xd.arkosammy.publicenderchest.util.ducks.ServerPlayerEntityDuck; + +import java.util.ArrayList; +import java.util.List; + +@Mixin(targets = "net.minecraft.server.network.ServerPlayerEntity$1") +public abstract class ServerPlayerEntitySyncHandlerMixin { + + @Shadow @Final private ServerPlayerEntity field_29182; + + @WrapOperation(method = "updateSlot", at = @At(value = "NEW", target = "net/minecraft/network/packet/s2c/play/ScreenHandlerSlotUpdateS2CPacket")) + private ScreenHandlerSlotUpdateS2CPacket modifySentItemStack(int syncId, int revision, int slot, ItemStack itemStack, Operation original, ScreenHandler screenHandler) { + ServerPlayerEntity player = this.field_29182; + if (!(screenHandler instanceof CustomGenericContainerScreenHandler customScreenHandler)) { + return original.call(syncId, revision, slot, itemStack); + } + Inventory inventory = customScreenHandler.getInventory(); + if (!(inventory instanceof PublicInventory publicInventory)) { + return original.call(syncId, revision, slot, itemStack); + } + List customInfoLines = PublicInventoryKt.getCustomInfoLines(publicInventory.getStack(slot)); + if (customInfoLines.isEmpty()) { + return original.call(syncId, revision, slot, itemStack); + } + if (((ServerPlayerEntityDuck) player).publicenderchest$hasMod()) { + for (CustomMutableText line : customInfoLines) { + line.getText().append(" " + PublicEnderChestKt.formatInfoTextMetadata(syncId, revision)); + } + } + LoreComponent loreComponent = itemStack.get(DataComponentTypes.LORE); + List sentLines = new ArrayList<>(); + if (loreComponent != null) { + sentLines.addAll(loreComponent.lines()); + } + sentLines.addAll(customInfoLines); + LoreComponent sentLoreComponent; + if (loreComponent != null) { + sentLoreComponent = new LoreComponent(ImmutableList.copyOf(sentLines), ImmutableList.copyOf(loreComponent.styledLines())); + } else { + sentLoreComponent = new LoreComponent(ImmutableList.copyOf(sentLines)); + } + ItemStack sentItemStack = itemStack.copy(); + sentItemStack.set(DataComponentTypes.LORE, sentLoreComponent); + return original.call(syncId, revision, slot, sentItemStack); + } + +} diff --git a/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ItemStackDuck.java b/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ItemStackDuck.java index 4df2b24..5c30ed7 100644 --- a/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ItemStackDuck.java +++ b/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ItemStackDuck.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable; import java.time.LocalDateTime; +import java.util.List; public interface ItemStackDuck { @@ -17,4 +18,8 @@ public interface ItemStackDuck { @Nullable LocalDateTime publicenderchest$getInsertedTime(); + void publicenderchest$addCustomInfoLine(Text text); + + List publicenderchest$getCustomInfoLines(); + } diff --git a/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ServerPlayerEntityDuck.java b/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ServerPlayerEntityDuck.java index 75119c6..0d82091 100644 --- a/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ServerPlayerEntityDuck.java +++ b/src/main/java/xd/arkosammy/publicenderchest/util/ducks/ServerPlayerEntityDuck.java @@ -18,4 +18,8 @@ public interface ServerPlayerEntityDuck { void publicenderchest$showPage(); + void publicenderchest$setHasMod(boolean hasMod); + + boolean publicenderchest$hasMod(); + } diff --git a/src/main/kotlin/xd/arkosammy/publicenderchest/PublicEnderChest.kt b/src/main/kotlin/xd/arkosammy/publicenderchest/PublicEnderChest.kt index 3d21d55..dc9eeca 100644 --- a/src/main/kotlin/xd/arkosammy/publicenderchest/PublicEnderChest.kt +++ b/src/main/kotlin/xd/arkosammy/publicenderchest/PublicEnderChest.kt @@ -83,4 +83,8 @@ fun ItemStack.isEnderChest() : Boolean { return false } return item.block is EnderChestBlock -} \ No newline at end of file +} + +@JvmStatic +fun formatInfoTextMetadata(syncId: Int, revision: Int) : String = + "publicenderchest:CustomText[syncId=$syncId, revision=$revision]" diff --git a/src/main/kotlin/xd/arkosammy/publicenderchest/commands/DatabaseCommands.kt b/src/main/kotlin/xd/arkosammy/publicenderchest/commands/DatabaseCommands.kt index 2c08e1c..eb8e388 100644 --- a/src/main/kotlin/xd/arkosammy/publicenderchest/commands/DatabaseCommands.kt +++ b/src/main/kotlin/xd/arkosammy/publicenderchest/commands/DatabaseCommands.kt @@ -164,6 +164,32 @@ object DatabaseCommands { } .build() + val playerNameArgumentNode: ArgumentCommandNode = CommandManager + .argument("playerName", StringArgumentType.word()) + .requires { src -> src.hasPermissionLevel(4) } + .suggests { context, builder -> + CommandSource.suggestMatching( + context.source.server.playerNames, + builder + ) + } + .executes { ctx -> + val playerName: String = StringArgumentType.getString(ctx, "playerName") + val player: ServerPlayerEntity = ctx.source.playerOrThrow + val queryTypeName: String = StringArgumentType.getString(ctx, "timeQueryType") + val queryType: TimeQueryType = TimeQueryType.getFromCommandNodeName(queryTypeName) ?: return@executes Command.SINGLE_SUCCESS.also { + player.sendMessage(Text.literal("Time query type \"$queryTypeName\" does not exist!").formatted(Formatting.RED)) + } + val days: Int = IntegerArgumentType.getInteger(ctx, "days") + val hours: Int = IntegerArgumentType.getInteger(ctx, "hours") + val minutes: Int = IntegerArgumentType.getInteger(ctx, "minutes") + val seconds: Int = IntegerArgumentType.getInteger(ctx, "seconds") + val queryContext = QueryContext(queryType, days, hours, minutes, seconds, playerName) + (player as ServerPlayerEntityDuck).`publicenderchest$showLogs`(queryContext) + Command.SINGLE_SUCCESS + } + .build() + rootNode.addChild(databaseNode) databaseNode.addChild(purgeNode) @@ -180,6 +206,8 @@ object DatabaseCommands { queryWithHours.addChild(queryWithMinutes) queryWithMinutes.addChild(queryWithSeconds) + queryWithSeconds.addChild(playerNameArgumentNode) + } private fun sendMessageToPlayerOrConsole(player: ServerPlayerEntity?, text: MutableText, logType: LogType) { @@ -190,7 +218,6 @@ object DatabaseCommands { LogType.NORMAL -> PublicEnderChest.LOGGER.info(text.string) LogType.WARNING -> PublicEnderChest.LOGGER.warn(text.string) LogType.ERROR -> PublicEnderChest.LOGGER.error(text.string) - } } } diff --git a/src/main/kotlin/xd/arkosammy/publicenderchest/logging/InventoryDatabaseManager.kt b/src/main/kotlin/xd/arkosammy/publicenderchest/logging/InventoryDatabaseManager.kt index addcb9d..1bde9e4 100644 --- a/src/main/kotlin/xd/arkosammy/publicenderchest/logging/InventoryDatabaseManager.kt +++ b/src/main/kotlin/xd/arkosammy/publicenderchest/logging/InventoryDatabaseManager.kt @@ -62,17 +62,22 @@ class InventoryDatabaseManager(server: MinecraftServer) { fun query(server: MinecraftServer, queryContext: QueryContext) : List { val url: String = getDatabaseFileUrl(server) val queryTimeStamp: Timestamp = Timestamp.valueOf(LocalDateTime.now().minusDays(queryContext.days.toLong()).minusHours(queryContext.hours.toLong()).minusMinutes(queryContext.minutes.toLong()).minusSeconds(queryContext.seconds.toLong())) - val query: String = when (queryContext.timeQueryType) { - TimeQueryType.BEFORE -> "SELECT * FROM ${MAIN_TABLE_NAME} WHERE timestamp < ?" - TimeQueryType.AFTER -> "SELECT * FROM ${MAIN_TABLE_NAME} WHERE timestamp > ?" + var query: String = when (queryContext.timeQueryType) { + TimeQueryType.BEFORE -> "SELECT * FROM $MAIN_TABLE_NAME WHERE timestamp < ?" + TimeQueryType.AFTER -> "SELECT * FROM $MAIN_TABLE_NAME WHERE timestamp > ?" + } + val playerName: String? = queryContext.playerName + if (playerName != null) { + query += " AND player=?" } - val results: MutableList = mutableListOf() - try { DriverManager.getConnection(url).use { connection -> connection.prepareStatement(query).use { statement -> statement.setTimestamp(1, queryTimeStamp) + if (playerName != null) { + statement.setString(2, playerName) + } statement.executeQuery().use { resultSet -> while (resultSet.next()) { val playerName: String = resultSet.getString("player") diff --git a/src/main/kotlin/xd/arkosammy/publicenderchest/logging/QueryContext.kt b/src/main/kotlin/xd/arkosammy/publicenderchest/logging/QueryContext.kt index 5bc988a..7820869 100644 --- a/src/main/kotlin/xd/arkosammy/publicenderchest/logging/QueryContext.kt +++ b/src/main/kotlin/xd/arkosammy/publicenderchest/logging/QueryContext.kt @@ -5,5 +5,6 @@ data class QueryContext @JvmOverloads constructor( val days: Int, val hours: Int = 0, val minutes: Int = 0, - val seconds: Int = 0 + val seconds: Int = 0, + val playerName: String? = null, ) diff --git a/src/main/kotlin/xd/arkosammy/publicenderchest/networking/C2SHandshakeResponsePayload.kt b/src/main/kotlin/xd/arkosammy/publicenderchest/networking/C2SHandshakeResponsePayload.kt new file mode 100644 index 0000000..63c21fe --- /dev/null +++ b/src/main/kotlin/xd/arkosammy/publicenderchest/networking/C2SHandshakeResponsePayload.kt @@ -0,0 +1,21 @@ +package xd.arkosammy.publicenderchest.networking + +import net.minecraft.network.PacketByteBuf +import net.minecraft.network.codec.PacketCodec +import net.minecraft.network.packet.CustomPayload +import net.minecraft.network.packet.CustomPayload.Id +import net.minecraft.util.Identifier +import net.minecraft.util.Uuids +import xd.arkosammy.publicenderchest.PublicEnderChest +import java.util.UUID + +data class C2SHandshakeResponsePayload(val uuid: UUID) : CustomPayload { + + override fun getId(): Id = PACKET_ID + + companion object { + val PACKET_ID: Id = Id(Identifier.of(PublicEnderChest.MOD_ID, "c2s_handshake_response")) + val PACKET_CODEC: PacketCodec = Uuids.PACKET_CODEC.xmap({ uuid -> C2SHandshakeResponsePayload(uuid) }, { payload -> payload.uuid }).cast() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/xd/arkosammy/publicenderchest/networking/S2CHandshakeRequestPayload.kt b/src/main/kotlin/xd/arkosammy/publicenderchest/networking/S2CHandshakeRequestPayload.kt new file mode 100644 index 0000000..2243240 --- /dev/null +++ b/src/main/kotlin/xd/arkosammy/publicenderchest/networking/S2CHandshakeRequestPayload.kt @@ -0,0 +1,21 @@ +package xd.arkosammy.publicenderchest.networking + +import net.minecraft.network.PacketByteBuf +import net.minecraft.network.codec.PacketCodec +import net.minecraft.network.packet.CustomPayload +import net.minecraft.network.packet.CustomPayload.Id +import net.minecraft.util.Identifier +import net.minecraft.util.Uuids +import xd.arkosammy.publicenderchest.PublicEnderChest +import java.util.UUID + +data class S2CHandshakeRequestPayload(val uuid: UUID) : CustomPayload { + + override fun getId(): Id = PACKET_ID + + companion object { + val PACKET_ID: Id = Id(Identifier.of(PublicEnderChest.MOD_ID, "s2c_handshake_request")) + val PACKET_CODEC: PacketCodec = Uuids.PACKET_CODEC.xmap({ uuid -> S2CHandshakeRequestPayload(uuid) }, { payload -> payload.uuid }).cast() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/xd/arkosammy/publicenderchest/util/Events.kt b/src/main/kotlin/xd/arkosammy/publicenderchest/util/Events.kt index 3a64bff..fcc3a1d 100644 --- a/src/main/kotlin/xd/arkosammy/publicenderchest/util/Events.kt +++ b/src/main/kotlin/xd/arkosammy/publicenderchest/util/Events.kt @@ -4,13 +4,16 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents import net.fabricmc.fabric.api.event.player.UseBlockCallback import net.fabricmc.fabric.api.event.player.UseItemCallback +import net.fabricmc.fabric.api.networking.v1.PacketSender import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.Context import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack import net.minecraft.network.packet.CustomPayload import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerPlayNetworkHandler import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.util.ActionResult import net.minecraft.util.Hand @@ -19,8 +22,11 @@ import net.minecraft.util.hit.BlockHitResult import net.minecraft.world.World import xd.arkosammy.publicenderchest.PublicEnderChest import xd.arkosammy.publicenderchest.commands.CommandManager +import xd.arkosammy.publicenderchest.networking.C2SHandshakeResponsePayload import xd.arkosammy.publicenderchest.networking.OpenPublicInventoryPayload +import xd.arkosammy.publicenderchest.networking.S2CHandshakeRequestPayload import xd.arkosammy.publicenderchest.util.ducks.ServerPlayerEntityDuck +import java.util.UUID object Events { @@ -29,14 +35,26 @@ object Events { UseBlockCallback.EVENT.register(::onBlockInteracted) UseItemCallback.EVENT.register(::onItemInteracted) ServerTickEvents.END_SERVER_TICK.register(::onServerTick) + PayloadTypeRegistry.playS2C().register(S2CHandshakeRequestPayload.PACKET_ID, S2CHandshakeRequestPayload.PACKET_CODEC) + PayloadTypeRegistry.playC2S().register(C2SHandshakeResponsePayload.PACKET_ID, C2SHandshakeResponsePayload.PACKET_CODEC) PayloadTypeRegistry.playC2S().register(OpenPublicInventoryPayload.PACKET_ID, OpenPublicInventoryPayload.PACKET_CODEC) ServerPlayNetworking.registerGlobalReceiver(OpenPublicInventoryPayload.PACKET_ID, ::handleOpenInventoryPayload) + ServerPlayNetworking.registerGlobalReceiver(C2SHandshakeResponsePayload.PACKET_ID, ::handleHandshakeResponse) + ServerPlayConnectionEvents.JOIN.register(::onPlayerJoin) } private fun onServerTick(server: MinecraftServer) { PublicEnderChest.INVENTORY_MANAGER.tick(server) } + private fun onBlockInteracted(player: PlayerEntity, world: World, hand: Hand, hitResult: BlockHitResult) : ActionResult { + return PublicEnderChest.INVENTORY_MANAGER.onBlockInteractedListener(player, world, hand, hitResult) + } + + private fun onItemInteracted(player: PlayerEntity, world: World, hand: Hand): TypedActionResult { + return PublicEnderChest.INVENTORY_MANAGER.onItemInteractedListener(player, world, hand) + } + private fun handleOpenInventoryPayload(payload: T, context: Context) { val player: ServerPlayerEntity = context.player() val isUsingPublicInventory: Boolean = player.getAttached(PublicEnderChest.USING_PUBLIC_INVENTORY) ?: true @@ -49,12 +67,15 @@ object Events { (player as ServerPlayerEntityDuck).`publicenderchest$openInventory`(PublicEnderChest.PUBLIC_INVENTORY_NAME, PublicEnderChest.INVENTORY_MANAGER.publicInventory) } - private fun onBlockInteracted(player: PlayerEntity, world: World, hand: Hand, hitResult: BlockHitResult) : ActionResult { - return PublicEnderChest.INVENTORY_MANAGER.onBlockInteractedListener(player, world, hand, hitResult) + private fun handleHandshakeResponse(payload: T, context: Context) { + val player: ServerPlayerEntity = context.player() + (player as ServerPlayerEntityDuck).`publicenderchest$setHasMod`(true) } - private fun onItemInteracted(player: PlayerEntity, world: World, hand: Hand): TypedActionResult { - return PublicEnderChest.INVENTORY_MANAGER.onItemInteractedListener(player, world, hand) + private fun onPlayerJoin(handler: ServerPlayNetworkHandler, sender: PacketSender, server: MinecraftServer) { + if (ServerPlayNetworking.canSend(handler, S2CHandshakeRequestPayload.PACKET_ID)) { + handler.sendPacket(ServerPlayNetworking.createS2CPacket(S2CHandshakeRequestPayload(UUID.randomUUID()))) + } } } \ No newline at end of file diff --git a/src/main/resources/publicenderchest.mixins.json b/src/main/resources/publicenderchest.mixins.json index 35cb10e..2e39440 100644 --- a/src/main/resources/publicenderchest.mixins.json +++ b/src/main/resources/publicenderchest.mixins.json @@ -5,7 +5,8 @@ "mixins": [ "ItemStackMixin", "ScreenHandlerMixin", - "ServerPlayerEntityMixin" + "ServerPlayerEntityMixin", + "ServerPlayerEntitySyncHandlerMixin" ], "injectors": { "defaultRequire": 1