From 045df74fe0824e0def7725e21eeaea6f648e79bb Mon Sep 17 00:00:00 2001
From: Juuxel <6596629+Juuxel@users.noreply.github.com>
Date: Tue, 16 Jun 2020 00:00:54 +0300
Subject: [PATCH] Screen handler API (#584)

* Add screen handler API

* Use more descriptive letters for FabricHandledScreens generics

* Add missing Deprecated annotations

* Bump fabric-containers-v0 version

* Remove unused ServerPlayerEntityAccessor

* Remove [Fabric] from log messages

* Convert FabricHandledScreens to a class

* Add dependency on networking-v0

* Resolve threading issues with NetworkingClient

* Use Identifiers instead of int IDs, make ScreenHandlers a class

Also added a private constructor to FabricHandledScreens.

* NetworkingClient -> ClientNetworking

* Use more descriptive names for injections

* Use ID parameter instead of fetching again

* Add clarifying comment

* Port changes from FabLabs

* Fix compilation

* Replace the content of links in FabricHandledScreens for readability

* Sync with FabLabs

* Add test mod

* I suppose IInv shouldn't have CC0 anymore :eyes:

* Improve logging

- Networking now uses a logger instead of an exception
- The loggers are now suffixed with their side
- Fixed the client unregistered screen message using the title instead of the ID

* Fix test mod on 20w21a

* Fix ExtendedScreenHandlerFactory javadoc referencing old names

* Update test mod to 1.16-pre2

Just a simple mapping update. :)

* Fix incorrect javadoc references to screen-handler-api in containers-v0

* Remove Minecraft exclusion from build.gradle (thanks to Loom 0.4) + testmod change

Resource loader v0 is now testmodRuntimeOnly as it's not needed for compiling.
---
 fabric-containers-v0/build.gradle             |   2 +-
 .../client/screen/ContainerScreenFactory.java |   4 +
 .../client/screen/ScreenProviderRegistry.java |   4 +
 .../api/container/ContainerFactory.java       |   4 +
 .../container/ContainerProviderRegistry.java  |   4 +
 fabric-screen-handler-api-v1/build.gradle     |  12 +
 .../screenhandler/v1/ScreenRegistry.java      |  87 +++++++
 .../client/screenhandler/v1/package-info.java |  20 ++
 .../v1/ExtendedScreenHandlerFactory.java      |  36 +++
 .../v1/ScreenHandlerRegistry.java             | 140 +++++++++++
 .../api/screenhandler/v1/package-info.java    |  20 ++
 .../ExtendedScreenHandlerType.java            |  46 ++++
 .../fabric/impl/screenhandler/Networking.java |  72 ++++++
 .../client/ClientNetworking.java              |  87 +++++++
 .../ServerPlayerEntityMixin.java              |  81 ++++++
 .../fabric-screen-handler-api-v1/icon.png     | Bin 0 -> 1579 bytes
 ...fabric-screen-handler-api-v1.accesswidener |   8 +
 .../fabric-screen-handler-api-v1.mixins.json  |  13 +
 .../src/main/resources/fabric.mod.json        |  31 +++
 .../test/screenhandler/ScreenHandlerTest.java |  64 +++++
 .../test/screenhandler/block/BoxBlock.java    |  81 ++++++
 .../screenhandler/block/BoxBlockEntity.java   |  69 ++++++
 .../client/ClientScreenHandlerTest.java       |  40 +++
 .../client/PositionedScreen.java              |  62 +++++
 .../test/screenhandler/item/BagInventory.java |  47 ++++
 .../test/screenhandler/item/BagItem.java      |  47 ++++
 .../item/ImplementedInventory.java            | 232 ++++++++++++++++++
 .../screenhandler/item/PositionedBagItem.java |  76 ++++++
 .../screen/BagScreenHandler.java              |  65 +++++
 .../screen/BoxScreenHandler.java              |  50 ++++
 .../screen/PositionedBagScreenHandler.java    |  49 ++++
 .../screen/PositionedScreenHandler.java       |  23 ++
 .../blockstates/box.json                      |   5 +
 .../lang/en_us.json                           |   5 +
 .../models/block/box.json                     |   6 +
 .../models/item/bag.json                      |   6 +
 .../models/item/box.json                      |   3 +
 .../models/item/positioned_bag.json           |   6 +
 .../textures/block/box.png                    | Bin 0 -> 616 bytes
 .../textures/item/bag.png                     | Bin 0 -> 837 bytes
 .../textures/item/positioned_bag.png          | Bin 0 -> 2064 bytes
 .../loot_tables/blocks/box.json               |  19 ++
 .../src/testmod/resources/fabric.mod.json     |  20 ++
 settings.gradle                               |   1 +
 44 files changed, 1646 insertions(+), 1 deletion(-)
 create mode 100644 fabric-screen-handler-api-v1/build.gradle
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/ScreenRegistry.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/package-info.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ExtendedScreenHandlerFactory.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ScreenHandlerRegistry.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/package-info.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/ExtendedScreenHandlerType.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/Networking.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/client/ClientNetworking.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/mixin/screenhandler/ServerPlayerEntityMixin.java
 create mode 100644 fabric-screen-handler-api-v1/src/main/resources/assets/fabric-screen-handler-api-v1/icon.png
 create mode 100644 fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.accesswidener
 create mode 100644 fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.mixins.json
 create mode 100644 fabric-screen-handler-api-v1/src/main/resources/fabric.mod.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/ScreenHandlerTest.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlock.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlockEntity.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/ClientScreenHandlerTest.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/PositionedScreen.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagInventory.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagItem.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/ImplementedInventory.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/PositionedBagItem.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BagScreenHandler.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BoxScreenHandler.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedBagScreenHandler.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedScreenHandler.java
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/blockstates/box.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/lang/en_us.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/block/box.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/bag.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/box.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/positioned_bag.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/block/box.png
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/item/bag.png
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/item/positioned_bag.png
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/data/fabric-screen-handler-api-v1-testmod/loot_tables/blocks/box.json
 create mode 100644 fabric-screen-handler-api-v1/src/testmod/resources/fabric.mod.json

diff --git a/fabric-containers-v0/build.gradle b/fabric-containers-v0/build.gradle
index e866ea231..e79c36dac 100644
--- a/fabric-containers-v0/build.gradle
+++ b/fabric-containers-v0/build.gradle
@@ -1,5 +1,5 @@
 archivesBaseName = "fabric-containers-v0"
-version = getSubprojectVersion(project, "0.1.7")
+version = getSubprojectVersion(project, "0.1.8")
 
 dependencies {
 	compile project(path: ':fabric-api-base', configuration: 'dev')
diff --git a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ContainerScreenFactory.java b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ContainerScreenFactory.java
index db4b2d196..163c5a23d 100644
--- a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ContainerScreenFactory.java
+++ b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ContainerScreenFactory.java
@@ -19,6 +19,10 @@ package net.fabricmc.fabric.api.client.screen;
 import net.minecraft.client.gui.screen.ingame.HandledScreen;
 import net.minecraft.screen.ScreenHandler;
 
+/**
+ * @deprecated Use {@link net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry.Factory} instead.
+ */
+@Deprecated
 @FunctionalInterface
 public interface ContainerScreenFactory<C extends ScreenHandler> {
 	HandledScreen create(C container);
diff --git a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ScreenProviderRegistry.java b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ScreenProviderRegistry.java
index a132b746f..7047db7cf 100644
--- a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ScreenProviderRegistry.java
+++ b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/client/screen/ScreenProviderRegistry.java
@@ -24,6 +24,10 @@ import net.fabricmc.fabric.api.container.ContainerFactory;
 import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
 import net.fabricmc.fabric.impl.client.container.ScreenProviderRegistryImpl;
 
+/**
+ * @deprecated Use {@link net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry} instead.
+ */
+@Deprecated
 public interface ScreenProviderRegistry {
 	ScreenProviderRegistry INSTANCE = ScreenProviderRegistryImpl.INSTANCE;
 
diff --git a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerFactory.java b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerFactory.java
index 4a5c0cee1..15daa3dd2 100644
--- a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerFactory.java
+++ b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerFactory.java
@@ -20,6 +20,10 @@ import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.util.Identifier;
 import net.minecraft.network.PacketByteBuf;
 
+/**
+ * @deprecated Use {@link net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry.ExtendedClientHandlerFactory} instead.
+ */
+@Deprecated
 @FunctionalInterface
 public interface ContainerFactory<T> {
 	/**
diff --git a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerProviderRegistry.java b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerProviderRegistry.java
index 7d82f98a4..2dd365c6b 100644
--- a/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerProviderRegistry.java
+++ b/fabric-containers-v0/src/main/java/net/fabricmc/fabric/api/container/ContainerProviderRegistry.java
@@ -27,6 +27,10 @@ import net.minecraft.network.PacketByteBuf;
 import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry;
 import net.fabricmc.fabric.impl.container.ContainerProviderImpl;
 
+/**
+ * @deprecated Use {@link net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry} instead.
+ */
+@Deprecated
 public interface ContainerProviderRegistry {
 	ContainerProviderRegistry INSTANCE = ContainerProviderImpl.INSTANCE;
 
diff --git a/fabric-screen-handler-api-v1/build.gradle b/fabric-screen-handler-api-v1/build.gradle
new file mode 100644
index 000000000..1a3f806c1
--- /dev/null
+++ b/fabric-screen-handler-api-v1/build.gradle
@@ -0,0 +1,12 @@
+archivesBaseName = "fabric-screen-handler-api-v1"
+version = getSubprojectVersion(project, "1.0.0")
+
+minecraft {
+	accessWidener = file('src/main/resources/fabric-screen-handler-api-v1.accesswidener')
+}
+
+dependencies {
+	compile(project(path: ':fabric-api-base', configuration: 'dev'))
+	compile(project(path: ':fabric-networking-v0', configuration: 'dev'))
+	testmodRuntimeOnly(project(path: ':fabric-resource-loader-v0', configuration: 'dev'))
+}
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/ScreenRegistry.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/ScreenRegistry.java
new file mode 100644
index 000000000..725859b59
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/ScreenRegistry.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.api.client.screenhandler.v1;
+
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.HandledScreens;
+import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.text.Text;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+
+/**
+ * An API for registering handled screens that represent screen handlers on the client.
+ * Exposes vanilla's private {@link HandledScreens#register HandledScreens.register()} to modders as {@link #register ScreenRegistry.register()}.
+ *
+ * <h2>Example</h2>
+ * <pre>
+ * {@code
+ * // In a client-side initialization method:
+ * ScreenRegistry.register(MyScreenHandlers.OVEN, OvenScreen::new);
+ *
+ * // Screen class
+ * public class OvenScreen extends HandledScreen<OvenScreenHandler> {
+ * 	public OvenScreen(OvenScreenHandler handler, PlayerInventory inventory, Text title) {
+ * 		super(handler, inventory, title);
+ * 	}
+ * }
+ * }
+ * </pre>
+ *
+ * @see net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry registering screen handlers
+ */
+@Environment(EnvType.CLIENT)
+public final class ScreenRegistry {
+	private ScreenRegistry() {
+	}
+
+	/**
+	 * Registers a new screen factory for a screen handler type.
+	 *
+	 * @param type          the screen handler type object
+	 * @param screenFactory the screen handler factory
+	 * @param <H>           the screen handler type
+	 * @param <S>           the screen type
+	 */
+	public static <H extends ScreenHandler, S extends Screen & ScreenHandlerProvider<H>> void register(ScreenHandlerType<? extends H> type, Factory<? super H, ? extends S> screenFactory) {
+		// Convert our factory to the vanilla provider here as the vanilla interface won't be available to modders.
+		HandledScreens.<H, S>register(type, screenFactory::create);
+	}
+
+	/**
+	 * A factory for handled screens.
+	 *
+	 * @param <H> the screen handler type
+	 * @param <S> the screen type
+	 */
+	@FunctionalInterface
+	public interface Factory<H extends ScreenHandler, S extends Screen & ScreenHandlerProvider<H>> {
+		/**
+		 * Creates a new handled screen.
+		 *
+		 * @param handler   the screen handler
+		 * @param inventory the player inventory
+		 * @param title     the title of the screen
+		 * @return the created screen
+		 */
+		S create(H handler, PlayerInventory inventory, Text title);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/package-info.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/package-info.java
new file mode 100644
index 000000000..47bd0433c
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/client/screenhandler/v1/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * API for working with screen handlers on the client.
+ */
+package net.fabricmc.fabric.api.client.screenhandler.v1;
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ExtendedScreenHandlerFactory.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ExtendedScreenHandlerFactory.java
new file mode 100644
index 000000000..e1fb76878
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ExtendedScreenHandlerFactory.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.api.screenhandler.v1;
+
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.NamedScreenHandlerFactory;
+import net.minecraft.server.network.ServerPlayerEntity;
+
+/**
+ * An extension of {@code NamedScreenHandlerFactory} that can write additional data to a screen opening packet.
+ *
+ * @see ScreenHandlerRegistry#registerExtended(net.minecraft.util.Identifier, ScreenHandlerRegistry.ExtendedClientHandlerFactory)
+ */
+public interface ExtendedScreenHandlerFactory extends NamedScreenHandlerFactory {
+	/**
+	 * Writes additional server -&gt; client screen opening data to the buffer.
+	 *
+	 * @param player the player that is opening the screen
+	 * @param buf    the packet buffer
+	 */
+	void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf);
+}
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ScreenHandlerRegistry.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ScreenHandlerRegistry.java
new file mode 100644
index 000000000..2a93d0fed
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/ScreenHandlerRegistry.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.api.screenhandler.v1;
+
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.fabricmc.fabric.impl.screenhandler.ExtendedScreenHandlerType;
+
+/**
+ * An API for creating and registering {@linkplain ScreenHandlerType screen handler types}.
+ *
+ * <p>This class exposes the private {@link ScreenHandlerType} constructor,
+ * as well as adds support for creating types using Fabric's extended screen handler API.
+ *
+ * <p>Screen handlers types are used to synchronize {@linkplain ScreenHandler screen handlers}
+ * between the server and the client. Screen handlers manage the items and integer properties that are
+ * needed to show on screens, such as the items in a chest or the progress of a furnace.
+ *
+ * <h2>Simple and extended screen handlers</h2>
+ * Simple screen handlers are the type of screen handlers used in vanilla.
+ * They can automatically synchronize items and integer properties between the server and the client,
+ * but they don't support having custom data sent in the opening packet.
+ *
+ * <p>This module adds <i>extended screen handlers</i> that can synchronize their own custom data
+ * when they are opened, which can be useful for defining additional properties of a screen on the server.
+ * For example, a mod can synchronize text that will show up as a label.
+ *
+ * <h2>Example</h2>
+ * <pre>
+ * {@code
+ * // Creating the screen handler type
+ * public static final ScreenHandlerType<OvenScreenHandler> OVEN = ScreenHandlerRegistry.registerSimple(new Identifier("my_mod", "oven"), OvenScreenHandler::new);
+ *
+ * // Screen handler class
+ * public class OvenScreenHandler extends ScreenHandler {
+ * 	public OvenScreenHandler(int syncId) {
+ * 		super(MyScreenHandlers.OVEN, syncId);
+ * 	}
+ * }
+ *
+ * // Opening the screen
+ * NamedScreenHandlerFactory factory = ...;
+ * player.openHandledScreen(factory); // only works on ServerPlayerEntity instances
+ * }
+ * </pre>
+ *
+ * @see net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry registering screens for screen handlers
+ */
+public final class ScreenHandlerRegistry {
+	private ScreenHandlerRegistry() {
+	}
+
+	/**
+	 * Creates and registers a new {@code ScreenHandlerType} that creates client-sided screen handlers using the factory.
+	 *
+	 * @param id      the registry ID
+	 * @param factory the client-sided screen handler factory
+	 * @param <T>     the screen handler type
+	 * @return the created type object
+	 */
+	public static <T extends ScreenHandler> ScreenHandlerType<T> registerSimple(Identifier id, SimpleClientHandlerFactory<T> factory) {
+		// Wrap our factory in vanilla's factory; it will not be public for users.
+		ScreenHandlerType<T> type = new ScreenHandlerType<>(factory::create);
+		return Registry.register(Registry.SCREEN_HANDLER, id, type);
+	}
+
+	/**
+	 * Creates and registers a new {@code ScreenHandlerType} that creates client-sided screen handlers with additional
+	 * networked opening data.
+	 *
+	 * <p>These screen handlers must be opened with a {@link ExtendedScreenHandlerFactory}.
+	 *
+	 * @param id      the registry ID
+	 * @param factory the client-sided screen handler factory
+	 * @param <T>     the screen handler type
+	 * @return the created type object
+	 */
+	public static <T extends ScreenHandler> ScreenHandlerType<T> registerExtended(Identifier id, ExtendedClientHandlerFactory<T> factory) {
+		ScreenHandlerType<T> type = new ExtendedScreenHandlerType<>(factory);
+		return Registry.register(Registry.SCREEN_HANDLER, id, type);
+	}
+
+	/**
+	 * A factory for client-sided screen handler instances.
+	 *
+	 * @param <T> the screen handler type
+	 */
+	public interface SimpleClientHandlerFactory<T extends ScreenHandler> {
+		/**
+		 * Creates a new client-sided screen handler.
+		 *
+		 * @param syncId    the synchronization ID
+		 * @param inventory the player inventory
+		 * @return the created screen handler
+		 */
+		@Environment(EnvType.CLIENT)
+		T create(int syncId, PlayerInventory inventory);
+	}
+
+	/**
+	 * A factory for client-sided screen handler instances
+	 * with additional screen opening data.
+	 *
+	 * @param <T> the screen handler type
+	 * @see ExtendedScreenHandlerFactory
+	 */
+	public interface ExtendedClientHandlerFactory<T extends ScreenHandler> {
+		/**
+		 * Creates a new client-sided screen handler with additional screen opening data.
+		 *
+		 * @param syncId    the synchronization ID
+		 * @param inventory the player inventory
+		 * @param buf       the packet buffer
+		 * @return the created screen handler
+		 */
+		@Environment(EnvType.CLIENT)
+		T create(int syncId, PlayerInventory inventory, PacketByteBuf buf);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/package-info.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/package-info.java
new file mode 100644
index 000000000..3d1ff2e3d
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/api/screenhandler/v1/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * The Fabric screen handler API for creating screen handlers.
+ */
+package net.fabricmc.fabric.api.screenhandler.v1;
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/ExtendedScreenHandlerType.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/ExtendedScreenHandlerType.java
new file mode 100644
index 000000000..951338f03
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/ExtendedScreenHandlerType.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.impl.screenhandler;
+
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
+
+public final class ExtendedScreenHandlerType<T extends ScreenHandler> extends ScreenHandlerType<T> {
+	private final ScreenHandlerRegistry.ExtendedClientHandlerFactory<T> factory;
+
+	public ExtendedScreenHandlerType(ScreenHandlerRegistry.ExtendedClientHandlerFactory<T> factory) {
+		super(null);
+		this.factory = factory;
+	}
+
+	@Environment(EnvType.CLIENT)
+	@Override
+	public T create(int syncId, PlayerInventory inventory) {
+		throw new UnsupportedOperationException("Use ExtendedScreenHandlerType.create(int, PlayerInventory, PacketByteBuf)!");
+	}
+
+	@Environment(EnvType.CLIENT)
+	public T create(int syncId, PlayerInventory inventory, PacketByteBuf buf) {
+		return factory.create(syncId, inventory, buf);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/Networking.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/Networking.java
new file mode 100644
index 000000000..07ab4eb6b
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/Networking.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.impl.screenhandler;
+
+import java.util.Objects;
+
+import io.netty.buffer.Unpooled;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
+import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
+
+public final class Networking {
+	private static final Logger LOGGER = LogManager.getLogger("fabric-screen-handler-api-v1/server");
+
+	// [Packet format]
+	// typeId: identifier
+	// syncId: varInt
+	// title: text
+	// customData: buf
+	public static final Identifier OPEN_ID = new Identifier("fabric-screen-handler-api-v1", "open_screen");
+
+	/**
+	 * Opens an extended screen handler by sending a custom packet to the client.
+	 *
+	 * @param player  the player
+	 * @param factory the screen handler factory
+	 * @param handler the screen handler instance
+	 * @param syncId  the synchronization ID
+	 */
+	public static void sendOpenPacket(ServerPlayerEntity player, ExtendedScreenHandlerFactory factory, ScreenHandler handler, int syncId) {
+		Objects.requireNonNull(player, "player is null");
+		Objects.requireNonNull(factory, "factory is null");
+		Objects.requireNonNull(handler, "handler is null");
+
+		Identifier typeId = Registry.SCREEN_HANDLER.getId(handler.getType());
+
+		if (typeId == null) {
+			LOGGER.warn("Trying to open unregistered screen handler {}", handler);
+			return;
+		}
+
+		PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
+		buf.writeIdentifier(typeId);
+		buf.writeVarInt(syncId);
+		buf.writeText(factory.getDisplayName());
+		factory.writeScreenOpeningData(player, buf);
+
+		ServerSidePacketRegistry.INSTANCE.sendToPlayer(player, OPEN_ID, buf);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/client/ClientNetworking.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/client/ClientNetworking.java
new file mode 100644
index 000000000..7f4058de0
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/impl/screenhandler/client/ClientNetworking.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.impl.screenhandler.client;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.HandledScreens;
+import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
+import net.fabricmc.fabric.impl.screenhandler.ExtendedScreenHandlerType;
+import net.fabricmc.fabric.impl.screenhandler.Networking;
+
+@Environment(EnvType.CLIENT)
+public final class ClientNetworking implements ClientModInitializer {
+	private static final Logger LOGGER = LogManager.getLogger("fabric-screen-handler-api-v1/client");
+
+	@Override
+	public void onInitializeClient() {
+		ClientSidePacketRegistry.INSTANCE.register(Networking.OPEN_ID, (ctx, buf) -> {
+			Identifier typeId = buf.readIdentifier();
+			int syncId = buf.readVarInt();
+			Text title = buf.readText();
+			buf.retain();
+			ctx.getTaskQueue().execute(() -> openScreen(typeId, syncId, title, buf));
+		});
+	}
+
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	private void openScreen(Identifier typeId, int syncId, Text title, PacketByteBuf buf) {
+		ScreenHandlerType<?> type = Registry.SCREEN_HANDLER.get(typeId);
+
+		if (type == null) {
+			LOGGER.warn("Unknown screen handler ID: {}", typeId);
+			return;
+		}
+
+		if (!(type instanceof ExtendedScreenHandlerType<?>)) {
+			LOGGER.warn("Received extended opening packet for non-extended screen handler {}", typeId);
+			return;
+		}
+
+		HandledScreens.Provider screenFactory = HandledScreens.getProvider(type);
+
+		if (screenFactory != null) {
+			MinecraftClient client = MinecraftClient.getInstance();
+			PlayerEntity player = client.player;
+
+			Screen screen = screenFactory.create(
+					((ExtendedScreenHandlerType<?>) type).create(syncId, player.inventory, buf),
+					player.inventory,
+					title
+			);
+
+			player.currentScreenHandler = ((ScreenHandlerProvider<?>) screen).getScreenHandler();
+			client.openScreen(screen);
+		} else {
+			LOGGER.warn("Screen not registered for screen handler {}!", typeId);
+		}
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/mixin/screenhandler/ServerPlayerEntityMixin.java b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/mixin/screenhandler/ServerPlayerEntityMixin.java
new file mode 100644
index 000000000..adcc34e2c
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/java/net/fabricmc/fabric/mixin/screenhandler/ServerPlayerEntityMixin.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.mixin.screenhandler;
+
+import java.util.OptionalInt;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+import net.minecraft.network.Packet;
+import net.minecraft.screen.NamedScreenHandlerFactory;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.network.ServerPlayNetworkHandler;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
+import net.fabricmc.fabric.impl.screenhandler.ExtendedScreenHandlerType;
+import net.fabricmc.fabric.impl.screenhandler.Networking;
+
+@Mixin(ServerPlayerEntity.class)
+public class ServerPlayerEntityMixin {
+	@Shadow
+	private int screenHandlerSyncId;
+
+	@Unique
+	private final ThreadLocal<ScreenHandler> fabric_openedScreenHandler = new ThreadLocal<>();
+
+	@Inject(method = "openHandledScreen(Lnet/minecraft/screen/NamedScreenHandlerFactory;)Ljava/util/OptionalInt;", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayNetworkHandler;sendPacket(Lnet/minecraft/network/Packet;)V"), locals = LocalCapture.CAPTURE_FAILHARD)
+	private void fabric_storeOpenedScreenHandler(NamedScreenHandlerFactory factory, CallbackInfoReturnable<OptionalInt> info, ScreenHandler handler) {
+		if (factory instanceof ExtendedScreenHandlerFactory) {
+			fabric_openedScreenHandler.set(handler);
+		} else if (handler.getType() instanceof ExtendedScreenHandlerType<?>) {
+			Identifier id = Registry.SCREEN_HANDLER.getId(handler.getType());
+			throw new IllegalArgumentException("[Fabric] Extended screen handler " + id + " must be opened with an ExtendedScreenHandlerFactory!");
+		}
+	}
+
+	@Redirect(method = "openHandledScreen(Lnet/minecraft/screen/NamedScreenHandlerFactory;)Ljava/util/OptionalInt;", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayNetworkHandler;sendPacket(Lnet/minecraft/network/Packet;)V"))
+	private void fabric_replaceVanillaScreenPacket(ServerPlayNetworkHandler networkHandler, Packet<?> packet, NamedScreenHandlerFactory factory) {
+		if (factory instanceof ExtendedScreenHandlerFactory) {
+			ScreenHandler handler = fabric_openedScreenHandler.get();
+
+			if (handler.getType() instanceof ExtendedScreenHandlerType<?>) {
+				Networking.sendOpenPacket((ServerPlayerEntity) (Object) this, (ExtendedScreenHandlerFactory) factory, handler, screenHandlerSyncId);
+			} else {
+				Identifier id = Registry.SCREEN_HANDLER.getId(handler.getType());
+				throw new IllegalArgumentException("[Fabric] Non-extended screen handler " + id + " must not be opened with an ExtendedScreenHandlerFactory!");
+			}
+		} else {
+			// Use vanilla logic for non-extended screen handlers
+			networkHandler.sendPacket(packet);
+		}
+	}
+
+	@Inject(method = "openHandledScreen(Lnet/minecraft/screen/NamedScreenHandlerFactory;)Ljava/util/OptionalInt;", at = @At("RETURN"))
+	private void fabric_clearStoredScreenHandler(NamedScreenHandlerFactory factory, CallbackInfoReturnable<OptionalInt> info) {
+		fabric_openedScreenHandler.remove();
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/main/resources/assets/fabric-screen-handler-api-v1/icon.png b/fabric-screen-handler-api-v1/src/main/resources/assets/fabric-screen-handler-api-v1/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..2931efbf610873c0084debb8690902b0103d27fe
GIT binary patch
literal 1579
zcmbVMTWB0r7@iGm)TAXsYw<=rnU=;v=W=GRbL=!tc4Brl6GO7t2vVJ$IlDV#XU;e?
z+r2ymsZdMQqAyaFLLUo;RumtE8Z@?uf_*4nP^4;o6fOFoSkN<j8Kcx&2hN<!|DEr@
zod5ryo}Zi9I&|j{!!TP5d3Ax#ARIRj(!bws|3s%--25Xx!whc=M~pe~^6d;0zuPJ<
z1xwm2GKkIVCMjXQX}c87F#9H&u5K*j0F-dWauoLax87!fWh(5!lqPC!4p*&w+rx|P
zxuVftHe{2X*bnwK5hbv3po6Aeb9~fP*bXnE>+o1$K?f2nE9_*b5G-l)AV)k5Qhb^-
zU{V4ZnTKgnmXdpcB*Kg!W(1hvM2N&RO30x1u~eI9meGQGe@_?PDQq<eTqFpMMuTr8
zc;ZzAD9f@SN`fSD6v6o|C(xUm<L~S<sMt3=%MC2zfRIryk$Rx8)Y7gCwj0qpelJWk
zU_w)O1;~q`OC6wTL^!wZ)jG;eL%=m`V<+$_7DiaNN&@0n$=^^%;y)>%q1CiV$8~M7
z?MQ_mOdqCh^a65Sv|ntwSXjV5se1;VK1|Kr8G7TQoQL&*ctt{L{fC<Gn+$Tf@L{sx
z-h-B7l7_!$(;ckp0an;h&OwQT=^{*{R1(1qCuWf-cA*+Et#T`Z%AA<tq-;@w6o-;o
zy8Q!6v&Pf|{a?YRfy%_Qb?UQa>lG}xPK5<gBvFpkh({Gu>k^yK3%T69N6J=>3jBqc
zDNvZsrJ<Zoa}p0b=4u)$IDViz1}>-yOXI^^mWf1cmY^XST)CVzIGjvEPENowmy}ax
zvJ8_(Cf#+H-dBlH53`_u-~6BVAMz|(g?jCVdBWNZ(+A}(pFV7>S3QgPiQcMaflkIC
z-3Ti|VT~{au*vq0ts9O&m$p&Gl=L6+q6_m$IcVq}o~+Pl{g>1esQp4%wp~|*zk1n`
zZ7T6Toc4`y88s}riCP|ZXrJ?FLz@^KTcyqLje<H^TzdE0PhK5b5x2)LE6;s)`N3ZX
z2Cv?+vFqVy1|k2_cl$rw7=Ck$@N2wL$?9VEp7Gi35A3;$)g3FR_g;G8x7eAVQRC>y
zu95Yz%F&S{<0~f)Iomek?+hQ%MhCu%T^zsg>C_L`1`Br`xNY&))k9yTQb$JC>)w_f
zpU<A$%?N8BFlS$R^rq&-d+#&n&pq`bxOC;qliz$4TRVRI>(^tu^Q)y%W~lVz`jz;_
jF?g&s@Y=Qe&c#kW|JbvqK0Y=Rw)4XDoVqsk_>;c_`@;F@

literal 0
HcmV?d00001

diff --git a/fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.accesswidener b/fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.accesswidener
new file mode 100644
index 000000000..65704847e
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.accesswidener
@@ -0,0 +1,8 @@
+accessWidener	v1	named
+accessible	class	net/minecraft/screen/ScreenHandlerType$Factory
+accessible	method	net/minecraft/screen/ScreenHandlerType <init> (Lnet/minecraft/screen/ScreenHandlerType$Factory;)V
+extendable	method	net/minecraft/screen/ScreenHandlerType <init> (Lnet/minecraft/screen/ScreenHandlerType$Factory;)V
+
+accessible	class	net/minecraft/client/gui/screen/ingame/HandledScreens$Provider
+accessible	method	net/minecraft/client/gui/screen/ingame/HandledScreens register (Lnet/minecraft/screen/ScreenHandlerType;Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;)V
+accessible	method	net/minecraft/client/gui/screen/ingame/HandledScreens getProvider (Lnet/minecraft/screen/ScreenHandlerType;)Lnet/minecraft/client/gui/screen/ingame/HandledScreens$Provider;
diff --git a/fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.mixins.json b/fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.mixins.json
new file mode 100644
index 000000000..69e30be3d
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/resources/fabric-screen-handler-api-v1.mixins.json
@@ -0,0 +1,13 @@
+{
+  "required": true,
+  "package": "net.fabricmc.fabric.mixin.screenhandler",
+  "compatibilityLevel": "JAVA_8",
+  "mixins": [
+    "ServerPlayerEntityMixin"
+  ],
+  "client": [
+  ],
+  "injectors": {
+    "defaultRequire": 1
+  }
+}
diff --git a/fabric-screen-handler-api-v1/src/main/resources/fabric.mod.json b/fabric-screen-handler-api-v1/src/main/resources/fabric.mod.json
new file mode 100644
index 000000000..6539d01a1
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/main/resources/fabric.mod.json
@@ -0,0 +1,31 @@
+{
+  "schemaVersion": 1,
+  "id": "fabric-screen-handler-api-v1",
+  "name": "Fabric Screen Handler API (v1)",
+  "version": "${version}",
+  "environment": "*",
+  "license": "Apache-2.0",
+  "icon": "assets/fabric-screen-handler-api-v1/icon.png",
+  "contact": {
+    "homepage": "https://fabricmc.net",
+    "irc": "irc://irc.esper.net:6667/fabric",
+    "issues": "https://github.com/FabricMC/fabric/issues",
+    "sources": "https://github.com/FabricMC/fabric"
+  },
+  "authors": [
+    "FabricMC"
+  ],
+  "depends": {
+    "fabricloader": ">=0.8.0",
+    "fabric-api-base": "*",
+    "fabric-networking-v0": "*"
+  },
+  "entrypoints": {
+    "client": ["net.fabricmc.fabric.impl.screenhandler.client.ClientNetworking"]
+  },
+  "description": "Hooks and extensions for creating screen handlers.",
+  "mixins": [
+    "fabric-screen-handler-api-v1.mixins.json"
+  ],
+  "accessWidener": "fabric-screen-handler-api-v1.accesswidener"
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/ScreenHandlerTest.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/ScreenHandlerTest.java
new file mode 100644
index 000000000..c65da0e43
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/ScreenHandlerTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler;
+
+import net.minecraft.block.AbstractBlock;
+import net.minecraft.block.Block;
+import net.minecraft.block.Blocks;
+import net.minecraft.block.entity.BlockEntityType;
+import net.minecraft.item.BlockItem;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemGroup;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import net.fabricmc.api.ModInitializer;
+import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry;
+import net.fabricmc.fabric.test.screenhandler.block.BoxBlock;
+import net.fabricmc.fabric.test.screenhandler.block.BoxBlockEntity;
+import net.fabricmc.fabric.test.screenhandler.item.BagItem;
+import net.fabricmc.fabric.test.screenhandler.item.PositionedBagItem;
+import net.fabricmc.fabric.test.screenhandler.screen.BagScreenHandler;
+import net.fabricmc.fabric.test.screenhandler.screen.BoxScreenHandler;
+import net.fabricmc.fabric.test.screenhandler.screen.PositionedBagScreenHandler;
+
+public class ScreenHandlerTest implements ModInitializer {
+	public static final String ID = "fabric-screen-handler-api-v1-testmod";
+
+	public static final Item BAG = new BagItem(new Item.Settings().group(ItemGroup.TOOLS).maxCount(1));
+	public static final Item POSITIONED_BAG = new PositionedBagItem(new Item.Settings().group(ItemGroup.TOOLS).maxCount(1));
+	public static final Block BOX = new BoxBlock(AbstractBlock.Settings.copy(Blocks.OAK_WOOD));
+	public static final Item BOX_ITEM = new BlockItem(BOX, new Item.Settings().group(ItemGroup.DECORATIONS));
+	public static final BlockEntityType<?> BOX_ENTITY = BlockEntityType.Builder.create(BoxBlockEntity::new, BOX).build(null);
+	public static final ScreenHandlerType<BagScreenHandler> BAG_SCREEN_HANDLER = ScreenHandlerRegistry.registerSimple(id("bag"), BagScreenHandler::new);
+	public static final ScreenHandlerType<PositionedBagScreenHandler> POSITIONED_BAG_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(id("positioned_bag"), PositionedBagScreenHandler::new);
+	public static final ScreenHandlerType<BoxScreenHandler> BOX_SCREEN_HANDLER = ScreenHandlerRegistry.registerExtended(id("box"), BoxScreenHandler::new);
+
+	public static Identifier id(String path) {
+		return new Identifier(ID, path);
+	}
+
+	@Override
+	public void onInitialize() {
+		Registry.register(Registry.ITEM, id("bag"), BAG);
+		Registry.register(Registry.ITEM, id("positioned_bag"), POSITIONED_BAG);
+		Registry.register(Registry.BLOCK, id("box"), BOX);
+		Registry.register(Registry.ITEM, id("box"), BOX_ITEM);
+		Registry.register(Registry.BLOCK_ENTITY_TYPE, id("box"), BOX_ENTITY);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlock.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlock.java
new file mode 100644
index 000000000..280ef3ff5
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlock.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.block;
+
+import net.minecraft.block.BlockRenderType;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.BlockWithEntity;
+import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.screen.NamedScreenHandlerFactory;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Hand;
+import net.minecraft.util.ItemScatterer;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.BlockView;
+import net.minecraft.world.World;
+
+public class BoxBlock extends BlockWithEntity {
+	public BoxBlock(Settings settings) {
+		super(settings);
+	}
+
+	@Override
+	public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
+		if (!world.isClient) {
+			NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, pos);
+
+			if (screenHandlerFactory != null) {
+				player.openHandledScreen(screenHandlerFactory);
+			}
+		}
+
+		return ActionResult.SUCCESS;
+	}
+
+	@Override
+	public BlockEntity createBlockEntity(BlockView world) {
+		return new BoxBlockEntity();
+	}
+
+	@Override
+	public BlockRenderType getRenderType(BlockState state) {
+		return BlockRenderType.MODEL;
+	}
+
+	@Override
+	public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
+		if (state.getBlock() != newState.getBlock()) {
+			BlockEntity be = world.getBlockEntity(pos);
+
+			if (be instanceof Inventory) {
+				ItemScatterer.spawn(world, pos, (Inventory) be);
+				world.updateComparators(pos, this);
+			}
+
+			super.onStateReplaced(state, world, pos, newState, moved);
+		}
+	}
+
+	@Override
+	public int getComparatorOutput(BlockState state, World world, BlockPos pos) {
+		return ScreenHandler.calculateComparatorOutput(world.getBlockEntity(pos));
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlockEntity.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlockEntity.java
new file mode 100644
index 000000000..2cab02d23
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/block/BoxBlockEntity.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.block;
+
+import net.minecraft.block.entity.LootableContainerBlockEntity;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.text.Text;
+import net.minecraft.text.TranslatableText;
+import net.minecraft.util.collection.DefaultedList;
+
+import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
+import net.fabricmc.fabric.test.screenhandler.ScreenHandlerTest;
+import net.fabricmc.fabric.test.screenhandler.screen.BoxScreenHandler;
+
+public class BoxBlockEntity extends LootableContainerBlockEntity implements ExtendedScreenHandlerFactory {
+	private DefaultedList<ItemStack> items = DefaultedList.ofSize(size(), ItemStack.EMPTY);
+
+	public BoxBlockEntity() {
+		super(ScreenHandlerTest.BOX_ENTITY);
+	}
+
+	@Override
+	protected DefaultedList<ItemStack> getInvStackList() {
+		return items;
+	}
+
+	@Override
+	protected void setInvStackList(DefaultedList<ItemStack> list) {
+		this.items = list;
+	}
+
+	@Override
+	protected Text getContainerName() {
+		return new TranslatableText(getCachedState().getBlock().getTranslationKey());
+	}
+
+	@Override
+	protected ScreenHandler createScreenHandler(int syncId, PlayerInventory playerInventory) {
+		return new BoxScreenHandler(syncId, playerInventory, this);
+	}
+
+	@Override
+	public int size() {
+		return 3 * 3;
+	}
+
+	@Override
+	public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) {
+		buf.writeBlockPos(pos);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/ClientScreenHandlerTest.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/ClientScreenHandlerTest.java
new file mode 100644
index 000000000..6edce1eaa
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/ClientScreenHandlerTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.client;
+
+import net.minecraft.client.gui.screen.ingame.Generic3x3ContainerScreen;
+
+import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry;
+import net.fabricmc.fabric.test.screenhandler.ScreenHandlerTest;
+import net.fabricmc.fabric.test.screenhandler.screen.BoxScreenHandler;
+import net.fabricmc.fabric.test.screenhandler.screen.PositionedBagScreenHandler;
+import net.fabricmc.api.ClientModInitializer;
+
+public class ClientScreenHandlerTest implements ClientModInitializer {
+	@Override
+	public void onInitializeClient() {
+		ScreenRegistry.register(ScreenHandlerTest.BAG_SCREEN_HANDLER, Generic3x3ContainerScreen::new);
+		ScreenRegistry.<PositionedBagScreenHandler, PositionedScreen<PositionedBagScreenHandler>>register(
+				ScreenHandlerTest.POSITIONED_BAG_SCREEN_HANDLER,
+				PositionedScreen::new
+		);
+		ScreenRegistry.<BoxScreenHandler, PositionedScreen<BoxScreenHandler>>register(
+				ScreenHandlerTest.BOX_SCREEN_HANDLER,
+				PositionedScreen::new
+		);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/PositionedScreen.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/PositionedScreen.java
new file mode 100644
index 000000000..631479061
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/client/PositionedScreen.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.client;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+
+import net.minecraft.client.gui.screen.ingame.HandledScreen;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.text.LiteralText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.BlockPos;
+
+import net.fabricmc.fabric.test.screenhandler.screen.PositionedScreenHandler;
+
+public class PositionedScreen<T extends ScreenHandler & PositionedScreenHandler> extends HandledScreen<T> {
+	private static final Identifier TEXTURE = new Identifier("minecraft", "textures/gui/container/dispenser.png");
+
+	public PositionedScreen(T handler, PlayerInventory inventory, Text title) {
+		super(handler, inventory, title);
+	}
+
+	@Override
+	public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
+		renderBackground(matrices);
+		super.render(matrices, mouseX, mouseY, delta);
+		drawMouseoverTooltip(matrices, mouseX, mouseY);
+	}
+
+	@Override
+	protected void drawForeground(MatrixStack matrices, int mouseX, int mouseY) {
+		BlockPos pos = handler.getPos();
+		Text usedTitle = pos != null ? new LiteralText("(" + pos.toShortString() + ")") : title;
+		textRenderer.draw(matrices, usedTitle, (float) (backgroundWidth / 2 - textRenderer.getWidth(usedTitle) / 2), 6.0F, 0x404040);
+		textRenderer.draw(matrices, playerInventory.getDisplayName(), 8.0F, backgroundHeight - 96 + 2, 0x404040);
+	}
+
+	@Override
+	protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) {
+		RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+		client.getTextureManager().bindTexture(TEXTURE);
+		int x = (width - backgroundWidth) / 2;
+		int y = (height - backgroundHeight) / 2;
+		drawTexture(matrices, x, y, 0, 0, backgroundWidth, backgroundHeight);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagInventory.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagInventory.java
new file mode 100644
index 000000000..aec664e09
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagInventory.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.item;
+
+import net.minecraft.inventory.Inventories;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.util.collection.DefaultedList;
+
+final class BagInventory implements ImplementedInventory {
+	private final ItemStack stack;
+	private final DefaultedList<ItemStack> items = DefaultedList.ofSize(9, ItemStack.EMPTY);
+
+	BagInventory(ItemStack stack) {
+		this.stack = stack;
+		CompoundTag tag = stack.getSubTag("Items");
+
+		if (tag != null) {
+			Inventories.fromTag(tag, items);
+		}
+	}
+
+	@Override
+	public DefaultedList<ItemStack> getItems() {
+		return items;
+	}
+
+	@Override
+	public void markDirty() {
+		CompoundTag tag = stack.getOrCreateSubTag("Items");
+		Inventories.toTag(tag, items);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagItem.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagItem.java
new file mode 100644
index 000000000..fd5b78208
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/BagItem.java
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.item;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.NamedScreenHandlerFactory;
+import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
+import net.minecraft.util.Hand;
+import net.minecraft.util.TypedActionResult;
+import net.minecraft.world.World;
+
+import net.fabricmc.fabric.test.screenhandler.screen.BagScreenHandler;
+
+public class BagItem extends Item {
+	public BagItem(Settings settings) {
+		super(settings);
+	}
+
+	@Override
+	public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
+		ItemStack stack = user.getStackInHand(hand);
+		user.openHandledScreen(createScreenHandlerFactory(stack));
+		return TypedActionResult.success(stack);
+	}
+
+	private NamedScreenHandlerFactory createScreenHandlerFactory(ItemStack stack) {
+		return new SimpleNamedScreenHandlerFactory((syncId, inventory, player) -> {
+			return new BagScreenHandler(syncId, inventory, new BagInventory(stack));
+		}, stack.getName());
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/ImplementedInventory.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/ImplementedInventory.java
new file mode 100644
index 000000000..a84ce895d
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/ImplementedInventory.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.item;
+
+import java.util.List;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.Inventories;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.inventory.SidedInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.util.collection.DefaultedList;
+import net.minecraft.util.math.Direction;
+
+/**
+ * A simple {@code SidedInventory} implementation with only default methods + an item list getter.
+ *
+ * <h2>Reading and writing to tags</h2>
+ * Use {@link Inventories#fromTag(CompoundTag, DefaultedList)} and {@link Inventories#toTag(CompoundTag, DefaultedList)}
+ * on {@linkplain #getItems() the item list}.
+ *
+ * @author Juuz
+ */
+@FunctionalInterface
+public interface ImplementedInventory extends SidedInventory {
+	/**
+	 * Gets the item list of this inventory.
+	 * Must return the same instance every time it's called.
+	 *
+	 * @return the item list
+	 */
+	DefaultedList<ItemStack> getItems();
+
+	// Creation
+
+	/**
+	 * Creates an inventory from the item list.
+	 *
+	 * @param items the item list
+	 * @return a new inventory
+	 */
+	static ImplementedInventory of(DefaultedList<ItemStack> items) {
+		return () -> items;
+	}
+
+	/**
+	 * Creates a new inventory with the size.
+	 *
+	 * @param size the inventory size
+	 * @return a new inventory
+	 */
+	static ImplementedInventory ofSize(int size) {
+		return of(DefaultedList.ofSize(size, ItemStack.EMPTY));
+	}
+
+	// SidedInventory
+
+	/**
+	 * Gets the available slots to automation on the side.
+	 *
+	 * <p>The default implementation returns an array of all slots.
+	 *
+	 * @param side the side
+	 * @return the available slots
+	 */
+	@Override
+	default int[] getAvailableSlots(Direction side) {
+		int[] result = new int[getItems().size()];
+
+		for (int i = 0; i < result.length; i++) {
+			result[i] = i;
+		}
+
+		return result;
+	}
+
+	/**
+	 * Returns true if the stack can be inserted in the slot at the side.
+	 *
+	 * <p>The default implementation returns true.
+	 *
+	 * @param slot  the slot
+	 * @param stack the stack
+	 * @param side  the side
+	 * @return true if the stack can be inserted
+	 */
+	@Override
+	default boolean canInsert(int slot, ItemStack stack, Direction side) {
+		return true;
+	}
+
+	/**
+	 * Returns true if the stack can be extracted from the slot at the side.
+	 *
+	 * <p>The default implementation returns true.
+	 *
+	 * @param slot  the slot
+	 * @param stack the stack
+	 * @param side  the side
+	 * @return true if the stack can be extracted
+	 */
+	@Override
+	default boolean canExtract(int slot, ItemStack stack, Direction side) {
+		return true;
+	}
+
+	// Inventory
+
+	/**
+	 * Returns the inventory size.
+	 *
+	 * <p>The default implementation returns the size of {@link #getItems()}.
+	 *
+	 * @return the inventory size
+	 */
+	@Override
+	default int size() {
+		return getItems().size();
+	}
+
+	/**
+	 * @return true if this inventory has only empty stacks, false otherwise
+	 */
+	@Override
+	default boolean isEmpty() {
+		for (int i = 0; i < size(); i++) {
+			ItemStack stack = getStack(i);
+
+			if (!stack.isEmpty()) {
+				return false;
+			}
+		}
+
+		return true;
+	}
+
+	/**
+	 * Gets the item in the slot.
+	 *
+	 * @param slot the slot
+	 * @return the item in the slot
+	 */
+	@Override
+	default ItemStack getStack(int slot) {
+		return getItems().get(slot);
+	}
+
+	/**
+	 * Takes a stack of the size from the slot.
+	 *
+	 * <p>(default implementation) If there are less items in the slot than what are requested,
+	 * takes all items in that slot.
+	 *
+	 * @param slot  the slot
+	 * @param count the item count
+	 * @return a stack
+	 */
+	@Override
+	default ItemStack removeStack(int slot, int count) {
+		ItemStack result = Inventories.splitStack(getItems(), slot, count);
+
+		if (!result.isEmpty()) {
+			markDirty();
+		}
+
+		return result;
+	}
+
+	/**
+	 * Removes the current stack in the {@code slot} and returns it.
+	 *
+	 * <p>The default implementation uses {@link Inventories#removeStack(List, int)}
+	 *
+	 * @param slot the slot
+	 * @return the removed stack
+	 */
+	@Override
+	default ItemStack removeStack(int slot) {
+		return Inventories.removeStack(getItems(), slot);
+	}
+
+	/**
+	 * Replaces the current stack in the {@code slot} with the provided stack.
+	 *
+	 * <p>If the stack is too big for this inventory ({@link Inventory#getMaxCountPerStack()} ()}),
+	 * it gets resized to this inventory's maximum amount.
+	 *
+	 * @param slot  the slot
+	 * @param stack the stack
+	 */
+	@Override
+	default void setStack(int slot, ItemStack stack) {
+		getItems().set(slot, stack);
+
+		if (stack.getCount() > getMaxCountPerStack()) {
+			stack.setCount(getMaxCountPerStack());
+		}
+	}
+
+	/**
+	 * Clears {@linkplain #getItems() the item list}}.
+	 */
+	@Override
+	default void clear() {
+		getItems().clear();
+	}
+
+	@Override
+	default void markDirty() {
+		// Override if you want behavior.
+	}
+
+	@Override
+	default boolean canPlayerUse(PlayerEntity player) {
+		return true;
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/PositionedBagItem.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/PositionedBagItem.java
new file mode 100644
index 000000000..13373b385
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/item/PositionedBagItem.java
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.item;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.ItemUsageContext;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.ScreenHandler;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.text.Text;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Hand;
+import net.minecraft.util.TypedActionResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
+import net.fabricmc.fabric.test.screenhandler.screen.PositionedBagScreenHandler;
+
+public class PositionedBagItem extends BagItem {
+	public PositionedBagItem(Settings settings) {
+		super(settings);
+	}
+
+	@Override
+	public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
+		ItemStack stack = user.getStackInHand(hand);
+		user.openHandledScreen(createScreenHandlerFactory(stack, null));
+		return TypedActionResult.success(stack);
+	}
+
+	@Override
+	public ActionResult useOnBlock(ItemUsageContext context) {
+		PlayerEntity user = context.getPlayer();
+		ItemStack stack = user.getStackInHand(context.getHand());
+		BlockPos pos = context.getBlockPos();
+		user.openHandledScreen(createScreenHandlerFactory(stack, pos));
+		return ActionResult.SUCCESS;
+	}
+
+	private ExtendedScreenHandlerFactory createScreenHandlerFactory(ItemStack stack, BlockPos pos) {
+		return new ExtendedScreenHandlerFactory() {
+			@Override
+			public ScreenHandler createMenu(int syncId, PlayerInventory inventory, PlayerEntity player) {
+				return new PositionedBagScreenHandler(syncId, inventory, new BagInventory(stack), pos);
+			}
+
+			@Override
+			public Text getDisplayName() {
+				return stack.getName();
+			}
+
+			@Override
+			public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) {
+				buf.writeBoolean(pos != null);
+				buf.writeBlockPos(pos != null ? pos : BlockPos.ORIGIN);
+			}
+		};
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BagScreenHandler.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BagScreenHandler.java
new file mode 100644
index 000000000..a1eac6781
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BagScreenHandler.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.screen;
+
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.inventory.SimpleInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.Generic3x3ContainerScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.screen.slot.SlotActionType;
+
+import net.fabricmc.fabric.test.screenhandler.ScreenHandlerTest;
+import net.fabricmc.fabric.test.screenhandler.item.BagItem;
+
+public class BagScreenHandler extends Generic3x3ContainerScreenHandler {
+	private final ScreenHandlerType<?> type;
+
+	public BagScreenHandler(int syncId, PlayerInventory playerInventory) {
+		this(syncId, playerInventory, new SimpleInventory(9));
+	}
+
+	public BagScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory) {
+		this(ScreenHandlerTest.BAG_SCREEN_HANDLER, syncId, playerInventory, inventory);
+	}
+
+	protected BagScreenHandler(ScreenHandlerType<?> type, int syncId, PlayerInventory playerInventory, Inventory inventory) {
+		super(syncId, playerInventory, inventory);
+		this.type = type;
+	}
+
+	@Override
+	public ScreenHandlerType<?> getType() {
+		return type;
+	}
+
+	@Override
+	public ItemStack onSlotClick(int slotId, int clickData, SlotActionType actionType, PlayerEntity playerEntity) {
+		if (slotId >= 0) { // slotId < 0 are used for networking internals
+			ItemStack stack = getSlot(slotId).getStack();
+
+			if (stack.getItem() instanceof BagItem) {
+				// Prevent moving bags around
+				return stack;
+			}
+		}
+
+		return super.onSlotClick(slotId, clickData, actionType, playerEntity);
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BoxScreenHandler.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BoxScreenHandler.java
new file mode 100644
index 000000000..6d3dc7e13
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/BoxScreenHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.screen;
+
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.screen.Generic3x3ContainerScreenHandler;
+import net.minecraft.screen.ScreenHandlerType;
+import net.minecraft.util.math.BlockPos;
+
+import net.fabricmc.fabric.test.screenhandler.ScreenHandlerTest;
+
+public class BoxScreenHandler extends Generic3x3ContainerScreenHandler implements PositionedScreenHandler {
+	private final BlockPos pos;
+
+	public BoxScreenHandler(int syncId, PlayerInventory playerInventory, PacketByteBuf buf) {
+		super(syncId, playerInventory);
+		this.pos = buf.readBlockPos();
+	}
+
+	public BoxScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory) {
+		super(syncId, playerInventory, inventory);
+		this.pos = BlockPos.ORIGIN;
+	}
+
+	@Override
+	public BlockPos getPos() {
+		return pos;
+	}
+
+	@Override
+	public ScreenHandlerType<?> getType() {
+		return ScreenHandlerTest.BOX_SCREEN_HANDLER;
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedBagScreenHandler.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedBagScreenHandler.java
new file mode 100644
index 000000000..2c7102f94
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedBagScreenHandler.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.screen;
+
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.inventory.SimpleInventory;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.util.math.BlockPos;
+
+import net.fabricmc.fabric.test.screenhandler.ScreenHandlerTest;
+
+public class PositionedBagScreenHandler extends BagScreenHandler implements PositionedScreenHandler {
+	private final BlockPos pos;
+
+	public PositionedBagScreenHandler(int syncId, PlayerInventory playerInventory, PacketByteBuf buf) {
+		this(syncId, playerInventory, new SimpleInventory(9), readOptionalPos(buf));
+	}
+
+	private static BlockPos readOptionalPos(PacketByteBuf buf) {
+		boolean hasPos = buf.readBoolean();
+		BlockPos pos = buf.readBlockPos();
+		return hasPos ? pos : null;
+	}
+
+	public PositionedBagScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory, BlockPos pos) {
+		super(ScreenHandlerTest.POSITIONED_BAG_SCREEN_HANDLER, syncId, playerInventory, inventory);
+		this.pos = pos;
+	}
+
+	@Override
+	public BlockPos getPos() {
+		return pos;
+	}
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedScreenHandler.java b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedScreenHandler.java
new file mode 100644
index 000000000..9432db94a
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/java/net/fabricmc/fabric/test/screenhandler/screen/PositionedScreenHandler.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package net.fabricmc.fabric.test.screenhandler.screen;
+
+import net.minecraft.util.math.BlockPos;
+
+public interface PositionedScreenHandler {
+	BlockPos getPos();
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/blockstates/box.json b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/blockstates/box.json
new file mode 100644
index 000000000..861b9b4f8
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/blockstates/box.json
@@ -0,0 +1,5 @@
+{
+  "variants": {
+    "": { "model": "fabric-screen-handler-api-v1-testmod:block/box" }
+  }
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/lang/en_us.json b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/lang/en_us.json
new file mode 100644
index 000000000..f74eb07c3
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/lang/en_us.json
@@ -0,0 +1,5 @@
+{
+  "block.fabric-screen-handler-api-v1-testmod.box": "Box",
+  "item.fabric-screen-handler-api-v1-testmod.bag": "Bag",
+  "item.fabric-screen-handler-api-v1-testmod.positioned_bag": "Positioned Bag"
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/block/box.json b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/block/box.json
new file mode 100644
index 000000000..7307a9c37
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/block/box.json
@@ -0,0 +1,6 @@
+{
+  "parent": "minecraft:block/cube_all",
+  "textures": {
+    "all": "fabric-screen-handler-api-v1-testmod:block/box"
+  }
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/bag.json b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/bag.json
new file mode 100644
index 000000000..e9479a5ec
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/bag.json
@@ -0,0 +1,6 @@
+{
+  "parent": "item/generated",
+  "textures": {
+    "layer0": "fabric-screen-handler-api-v1-testmod:item/bag"
+  }
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/box.json b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/box.json
new file mode 100644
index 000000000..ff03794e4
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/box.json
@@ -0,0 +1,3 @@
+{
+  "parent": "fabric-screen-handler-api-v1-testmod:block/box"
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/positioned_bag.json b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/positioned_bag.json
new file mode 100644
index 000000000..2b7938cbd
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/models/item/positioned_bag.json
@@ -0,0 +1,6 @@
+{
+  "parent": "item/generated",
+  "textures": {
+    "layer0": "fabric-screen-handler-api-v1-testmod:item/positioned_bag"
+  }
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/block/box.png b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/block/box.png
new file mode 100644
index 0000000000000000000000000000000000000000..b57b82765819e20ba8d615522c4a1247f6ba1859
GIT binary patch
literal 616
zcmV-u0+;=XP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0004mX+uL$Nkc;*
zaB^>EX>4Tx04R}tkv&MmKpe$iKcpfR5i>|}$WWau6curlDi*;)X)CnqU~=gfG%+M8
zE{=k0!NHHks)LKOt`4q(Aou~|<mja6A|?JWDYS_3;J6>}?mh0_0sdx<sb(+^sG4PD
zk_j<iSP?_72tvRh;)qDh)Kl4o96ZO@J$!t<i}5V)bAOJ0rD!t1Clb#x-LQz)iKjO$
zo%24iz$%hLd`>)W&;^Mfxh}i>#<}dUpJxh2E;C0g5R2t5mb;i$43&6_II5@`<@@t4
zE1b7DtF=07-;=*ET-27cT&FpLBo>iE8X{!WQAG_FVzg?cn8?t6!oweN{AqH@<f?*^
zV;&n&Avu2VKlt6PS(==1lfnt0=f$=^MuE^S&}!KB_pxoaPJqBOaHVzp8%<#5lk`SM
ziyi?3+rY(jN0aw}%N=0&NtX=CkpeXRr4sOdM&DEbhHinrRkyeHK29Hi9CfvH0~{Oz
zV`a)-^LTe}cW?imY4`U7sgrWO0G?El00006VoOIv0PX<p0Pc!u@Du<5010qNS#tmY
zE+YT{E+YYWr9XB6000McNliru<OCEEG6r%Dtv>(&02y>eSad^gZEa<4bO1wgWnpw>
zWFU8GbZ8()Nlj2!fese{002NqL_t(I%VV6L>F}R{OmOG<M+PQJG(Ug;m4O9WfE4ij
z{Z|GC1_tt700W~2j2bXF3}8Xd7-YGC63q+@3=9AqJR1{`WoKsq0000<MNUMnLSTYw
C`1=b0

literal 0
HcmV?d00001

diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/item/bag.png b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/item/bag.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a474f87a2c7f36e59fa4a2bf4cc9c9c18c9fe34
GIT binary patch
literal 837
zcmV-L1G@Z)P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0004mX+uL$Nkc;*
zaB^>EX>4Tx04R}tkv&MmKpe$iQ>CI62Ro=ZWT;Lph>AE$6^me@v=v%)FuC*#nlvOS
zE{=k0!NHHks)LKOt`4q(Aou~|<mja6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La)
z#baVNw<-o+;m0ug5J5m<rk+SIX5cx#?&0I>U6f~epZjz4D|wRvK9P8i>4rtTK|Hf*
z>74h8L#!kz#OK5jgDyz?$aUG}H_k<e1)do)GO2mu5V2V7V7Y@?$xw->iNlJjQNECM
zS>e3JS*_Gq>z@3D!MwJT<~q$$#Ib|~k`N)IhB7L!5T#Wk#YBqsV;=qy$DbsZOs+B*
zITlcb3d!+<|H1EW&BD~An-q)z-7mKNF$M&7fo9#dzmILZc>?&Kfh(=;uQq_$Ptxmc
zEqnwFYy%h9ZB5<-E_Z;TCtWfmNAlAY3I*W(jJ_!c4Bi60Yi@6?eVjf38R}~J1~@nb
z#*37_?(y!P&ffk#)9UXBYgBTi5QZ(M00006VoOIv0Nwz&02c5cV7>qV010qNS#tmY
zE+YT{E+YYWr9XB6000McNliru<O3NCFEafu7ySSL02y>eSad^gZEa<4bO1wgWnpw>
zWFU8GbZ8()Nlj2!fese{00A9IL_t(I%hi&<N&`U<#($R~F&B|EBDTJRji60X#A^h7
zgEZIJ`waHhzQGk<W5h<V^&KRL|9Z)VTZgq+Z!bCTLcq#_E%uw8Z@&3<;NM3uce{98
zuIhZdfdG(UV(Imu+lA>AjDdFBB_|W~>NIFpP>{r_)j<TRKY$<)cj)C&uy}--cp7ck
zwbd413gHU$ciwB?wrUj&6T9{K!TR%<&DSygmpA0>#x6x5!^G<0G32=eR3XpNy#pjp
z?Lq>HQ>)Pli1^tFL!Z|{<V>#fK;qQu?ml!nuB(VUg^~WDAonA0XsI9!U2!41S_p2e
z3SJUqS=n)xtRRFEGp7C<y@3_UUXciLdp);I&6{Y9Q<7z6(wJYH_~-Zr<Na%FrhzwJ
P00000NkvXXu0mjf<yCps

literal 0
HcmV?d00001

diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/item/positioned_bag.png b/fabric-screen-handler-api-v1/src/testmod/resources/assets/fabric-screen-handler-api-v1-testmod/textures/item/positioned_bag.png
new file mode 100644
index 0000000000000000000000000000000000000000..542e1607fb0461fb097de35790c833d935eb95cd
GIT binary patch
literal 2064
zcmV+r2=DiaP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV000E$dQ@0+Qek%>
zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|aw&f@e{AU%t1SA0x%fSri^bTtI9SnB9Id68=
zXPI*i1|bCsN%nR9um2ABA6!g{U5IKar{r;k95NRS%<rqLJNdBg=fU5Dck}L9U`z>m
z*^idae8)UPp3i(edJ5*_8%4RFh3ox7If~ehf!pFSB(x3qb`+JPNcdO|`}sPSQ#O5j
zgtO0&v%cJbpPRQJ$|)w&fO}xCnli@5J}CUw;@Zf0?u*y0=>4<1MH!g53yEKM-Y8}N
z3Vwn+ON1Or#yFy%^R&@APzDGX=i^)UKVILVcW>bz+TYRojOd&B!?wFwNPHIL154@Y
zznrIdEBZj|;_i**WRK-{^N`oKM|L-T^EXDbG5S7T2?<9|=TP@i)(CRf^w!#y(9|b~
zAT$hS%V>tQsEDQJtH01HWnxe{C{Uw9gZk<#RX_~fS<y3B$Fe33WMr<BP6@NoU<J)h
z45U|tgkY8*EVRt#Wv-I>=6oZFGC>)l=l^f+Rl%`xg2>zBE8yZ8bwJ?>^v@1rV1&kT
zYFrM!+sAA2Dqt`!HyaLE{N=WaE7Gsvl0~#YFrwc{OxlhDxCo{rz>t6epGYDlAW~)}
zf+GzC6+mYRe1QRqksCmA5J?~hISkv_qqIah@}5R9l;DtjNfg{55kXc65%HtJ5JM$O
z3^B$OHCoKEBuO*FQqnZ5YEjXkQIo2g)2zkBf<;TFW|pmF&Vs5NGx9N8&bbsAEzn%B
zyFj3ni!Zst6|Z#ZWmk4rsR*AMYOJYhwVG>b+@v9oTWZ>@<yJa%gi;SZ_SCgo&%F$U
z+K>@O9BHT<Hu5MBYlqfP?GIR^!x}H9rl3BoVOI54L9?91$r%{qKp<Sf8W=-hPtLqi
z2t1jaocYYiipT&NCpnEKV_-0J;yCEx?l9-L{4=~cwZ$9%UFO`R?nUN4^Y#^MZN8rC
zVr-}3bgCC*AJ)4Swld$A-}dfz0<ZS}D*97JBlS7p3v@~GYmC>_E4}+c0FjlJg;qzS
z@G~s;d0`}4nsOdKgeycs(jc^X4k#E(=vhy)_oV2D+jDpct_>*2aSbAJw<EOYn5B>K
zJ2$!C35>Muo8VR6^FC`&QjLyCS{NKaphV1}x2{Ldq(!M|KP6<=1NhtC{Z8ORuPw<M
zYDXcuO1w7B;>hZDlaVZB5np*-#$F#WNPUTa_i~`jcxZN?7Oy=NnPs@5gcWfQfty@-
z_vUd{zjcU^`vnU3kpvZpzOdx%Z$LV}s6`FmM+Zs0`A(a|3fk99z+aZV6jb6bjJcx}
zF9x&?i|!mKzSdK(dUjX;4wCCPvA@E7X9eo*32gF**c_gayYV9kcIdexElbsN3~k;1
z3>3u;%5}{_c5x5E&Z*^b%P&W5oI>9iLiX<xgb!N0XEscHgQnLqM7Mg1?6hmquov)6
zkke<MJ(hHP$nqEPZq^=mKF7?pZ%&%mG9%@L#b`c!TWUYligQ8~0Hy6Uz<c0)jM5Fy
zO?;nR{K*wRMB+J;`<~=Gx4O5tAk)|R;{ff6yLJjn4O@onj!nt7>q@ba0{)Pk%pO_}
zYWI3cyZr5LV~$Hx3ALsZN1*GT#QGlD^|eE%&%Z)5jk!N6v_JI!j3OPwHT=7F{{!@g
z?58iNo8tfg0fcEoLr_UWLm+T+Z)Rz1WdHzpoPCi!NW(xJ#a~lJk%~w=s5oS(P8N!a
zI7$_ZV4<`XT6HkF^b49aBq=VAf@{ISkHxBki?gl{u7V)=0pjH7r060g{x2!Ci1FaK
zAMfrx?%n}Hqrz0PD+;KZWu)RUF`HWzdtTwkFa{AtP-3Q@NH1pKIlk`U<Lh07XL+Cd
zbMz~DlL0=Fc#i3YMZ7^gvuWv^_lZNSBq_w_#1jTxkob}7vdeFriw+AsGh}2^^TZ)y
zvDm?K2eXo)5>FF{6;-2rA?vcjd5g1Jsj=2Q`3nPiZ6(chnxlwg2?-=2LPiZ`RA3=O
zt44~66z#`6{3DJ(NiLaOWiWCqpb8a|;|KqP-`$#psYy2}5CghjZ2Myj=-CCDb=&?v
zw(aH#;C}|Lw6?$60A@c)ueY`E5iqa~TwJ#`c@MbU0fwG*$&eh$Pg5uqfcG={rW_Ew
z1^U+9-dg)OeE>4l)$$E+a0rYSDSO@H-MyW?{d=a>-w&cPa=Lfw10(<d00v@9M??Ss
z00000`9r&Z00009a7bBm001r{001r{0eGc9b^rhX2XskIMF->q84)uAEHPZF0000P
zbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbN_DMuRR5;7kk}+<=KoCXW
zu7hxdA|x#3C?G@;iAzvYSfb=G6s|~-k{i&{AaM#>8d5vL>`;s?udxl0Qsq~>vj5Ec
zvjhKLrgGcNf9INgwG;qY#X@MHYvIR1Ch(H~mI^FCO*=uMBo-zSIMKT`aQKGB+B}Zn
z>9~EDx1J@t!fO}$<CA$l13$(zf=f=&F|cnYu#M&q&wNGCqk%?ySRlze2b9@EujwSx
z28|JtSQriz*NK{goWNH=B~D2YS~VBgPU^F{NUdO5Aaz>ZiDGv=bmt8dbT=Abp%~yj
uazSJb?9P>CYTv|6G*;@$ZyB{s{IhPMcvCBc18A}U0000<MNUMnLSTa3bIfP}

literal 0
HcmV?d00001

diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/data/fabric-screen-handler-api-v1-testmod/loot_tables/blocks/box.json b/fabric-screen-handler-api-v1/src/testmod/resources/data/fabric-screen-handler-api-v1-testmod/loot_tables/blocks/box.json
new file mode 100644
index 000000000..f8a3e07dd
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/data/fabric-screen-handler-api-v1-testmod/loot_tables/blocks/box.json
@@ -0,0 +1,19 @@
+{
+  "type": "minecraft:block",
+  "pools": [
+    {
+      "rolls": 1,
+      "entries": [
+        {
+          "type": "minecraft:item",
+          "name": "fabric-screen-handler-api-v1-testmod:box"
+        }
+      ],
+      "conditions": [
+        {
+          "condition": "minecraft:survives_explosion"
+        }
+      ]
+    }
+  ]
+}
diff --git a/fabric-screen-handler-api-v1/src/testmod/resources/fabric.mod.json b/fabric-screen-handler-api-v1/src/testmod/resources/fabric.mod.json
new file mode 100644
index 000000000..f3d6f8bad
--- /dev/null
+++ b/fabric-screen-handler-api-v1/src/testmod/resources/fabric.mod.json
@@ -0,0 +1,20 @@
+{
+  "schemaVersion": 1,
+  "id": "fabric-screen-handler-api-v1-testmod",
+  "name": "Fabric Screen Handler API (v1) Test Mod",
+  "version": "1.0.0",
+  "environment": "*",
+  "license": "Apache-2.0",
+  "depends": {
+    "fabric-resource-loader-v0": "*",
+    "fabric-screen-handler-api-v1": "*"
+  },
+  "entrypoints": {
+    "main": [
+      "net.fabricmc.fabric.test.screenhandler.ScreenHandlerTest"
+    ],
+    "client": [
+      "net.fabricmc.fabric.test.screenhandler.client.ClientScreenHandlerTest"
+    ]
+  }
+}
diff --git a/settings.gradle b/settings.gradle
index b6faf41d5..4dcd34a31 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -44,6 +44,7 @@ include 'fabric-rendering-v1'
 include 'fabric-rendering-data-attachment-v1'
 include 'fabric-rendering-fluids-v1'
 include 'fabric-resource-loader-v0'
+include 'fabric-screen-handler-api-v1'
 include 'fabric-tag-extensions-v0'
 include 'fabric-textures-v0'
 include 'fabric-tool-attribute-api-v1'