Screen handler API ()

* 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 👀

* 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.
This commit is contained in:
Juuxel 2020-06-16 00:00:54 +03:00 committed by GitHub
parent c824e0ac2c
commit 045df74fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 1646 additions and 1 deletions

View file

@ -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')

View file

@ -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);

View file

@ -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;

View file

@ -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> {
/**

View file

@ -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;

View file

@ -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'))
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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();
}
}

Binary file not shown.

After

(image error) Size: 1.5 KiB

View file

@ -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;

View file

@ -0,0 +1,13 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.screenhandler",
"compatibilityLevel": "JAVA_8",
"mixins": [
"ServerPlayerEntityMixin"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -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"
}

View file

@ -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);
}
}

View file

@ -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));
}
}

View file

@ -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);
}
}

View file

@ -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
);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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());
}
}

View file

@ -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;
}
}

View file

@ -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);
}
};
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "fabric-screen-handler-api-v1-testmod:block/box" }
}
}

View file

@ -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"
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "fabric-screen-handler-api-v1-testmod:block/box"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "fabric-screen-handler-api-v1-testmod:item/bag"
}
}

View file

@ -0,0 +1,3 @@
{
"parent": "fabric-screen-handler-api-v1-testmod:block/box"
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "fabric-screen-handler-api-v1-testmod:item/positioned_bag"
}
}

View file

@ -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"
}
]
}
]
}

View file

@ -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"
]
}
}

View file

@ -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'