mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-14 19:25:23 -05:00
Add overwriting screen handler factory (#2373)
This adds `FabricScreenHandlerFactory` (interface-injected to `NamedScreenHandlerFactory`). This also fixes a crash when passing `SimpleNamedScreenHandlerFactory` that wraps a `ExtendedScreenHandlerFactory`. The mixin now un-wraps the factory.
This commit is contained in:
parent
056269f0d8
commit
1cc24b1b0e
7 changed files with 106 additions and 5 deletions
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An extension to {@link net.minecraft.screen.NamedScreenHandlerFactory}.
|
||||
* Unlike {@link ExtendedScreenHandlerFactory}, this can be used by any screen
|
||||
* handlers, and is implemented via interface injection.
|
||||
*/
|
||||
public interface FabricScreenHandlerFactory {
|
||||
/**
|
||||
* {@return whether the server should send {@link
|
||||
* net.minecraft.network.packet.s2c.play.CloseScreenS2CPacket} when opening the screen}
|
||||
*
|
||||
* <p>In vanilla, opening a new screen will always send the close screen packet.
|
||||
* This, among other things, causes the mouse cursor to move to the center of the screen,
|
||||
* which might not be expected in some cases. If this returns {@code false}, the packet
|
||||
* is not sent to the client, stopping the behavior.
|
||||
*/
|
||||
default boolean shouldCloseCurrentScreen() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -38,6 +38,16 @@
|
|||
* Screen handlers can be opened using
|
||||
* {@link net.minecraft.entity.player.PlayerEntity#openHandledScreen(net.minecraft.screen.NamedScreenHandlerFactory)}.
|
||||
* Note that calling it on the logical client does nothing. To open an extended screen handler, the factory passed in
|
||||
* should be an {@link net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory}.
|
||||
* should be an {@link net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory}, or a
|
||||
* {@link net.minecraft.screen.SimpleNamedScreenHandlerFactory} that wraps such factory.
|
||||
*
|
||||
* <h2>Overwriting screen handlers</h2>
|
||||
* You might have noticed that calling {@link net.minecraft.entity.player.PlayerEntity#openHandledScreen(net.minecraft.screen.NamedScreenHandlerFactory) openHandledScreen} while on another screen will move
|
||||
* the cursor to the center of the screen. This is because the current screen gets closed before
|
||||
* opening the screen, resetting the cursor position. Since this behavior can be problematic,
|
||||
* this API provides a way to disable this. By overriding {@link
|
||||
* net.fabricmc.fabric.api.screenhandler.v1.FabricScreenHandlerFactory#shouldCloseCurrentScreen()}
|
||||
* on the screen handler factory to return {@code false} and passing that to the {@code
|
||||
* openHandledScreen} method, it will stop closing the screen and instead "overwrites" it.
|
||||
*/
|
||||
package net.fabricmc.fabric.api.screenhandler.v1;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
|
||||
import net.fabricmc.fabric.api.screenhandler.v1.FabricScreenHandlerFactory;
|
||||
|
||||
@Mixin(NamedScreenHandlerFactory.class)
|
||||
public interface NamedScreenHandlerFactoryMixin extends FabricScreenHandlerFactory {
|
||||
}
|
|
@ -30,8 +30,9 @@ 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.screen.SimpleNamedScreenHandlerFactory;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
|
@ -40,16 +41,32 @@ import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType;
|
|||
import net.fabricmc.fabric.impl.screenhandler.Networking;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public class ServerPlayerEntityMixin {
|
||||
public abstract class ServerPlayerEntityMixin {
|
||||
@Shadow
|
||||
private int screenHandlerSyncId;
|
||||
|
||||
@Shadow
|
||||
public abstract void closeHandledScreen();
|
||||
|
||||
@Shadow
|
||||
public abstract void closeScreenHandler();
|
||||
|
||||
@Unique
|
||||
private final ThreadLocal<ScreenHandler> fabric_openedScreenHandler = new ThreadLocal<>();
|
||||
|
||||
@Redirect(method = "openHandledScreen(Lnet/minecraft/screen/NamedScreenHandlerFactory;)Ljava/util/OptionalInt;", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;closeHandledScreen()V"))
|
||||
private void fabric_closeHandledScreenIfAllowed(ServerPlayerEntity player, NamedScreenHandlerFactory factory) {
|
||||
if (factory.shouldCloseCurrentScreen()) {
|
||||
this.closeHandledScreen();
|
||||
} else {
|
||||
// Called by closeHandledScreen in vanilla
|
||||
this.closeScreenHandler();
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
if (factory instanceof ExtendedScreenHandlerFactory || (factory instanceof SimpleNamedScreenHandlerFactory simpleFactory && simpleFactory.baseFactory instanceof ExtendedScreenHandlerFactory)) {
|
||||
fabric_openedScreenHandler.set(handler);
|
||||
} else if (handler.getType() instanceof ExtendedScreenHandlerType<?>) {
|
||||
Identifier id = Registry.SCREEN_HANDLER.getId(handler.getType());
|
||||
|
@ -59,6 +76,10 @@ public class ServerPlayerEntityMixin {
|
|||
|
||||
@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 SimpleNamedScreenHandlerFactory simpleFactory && simpleFactory.baseFactory instanceof ExtendedScreenHandlerFactory extendedFactory) {
|
||||
factory = extendedFactory;
|
||||
}
|
||||
|
||||
if (factory instanceof ExtendedScreenHandlerFactory) {
|
||||
ScreenHandler handler = fabric_openedScreenHandler.get();
|
||||
|
||||
|
|
|
@ -6,3 +6,5 @@ extendable method net/minecraft/screen/ScreenHandlerType <init> (Lnet/minecraft/
|
|||
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;
|
||||
|
||||
accessible field net/minecraft/screen/SimpleNamedScreenHandlerFactory baseFactory Lnet/minecraft/screen/ScreenHandlerFactory;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"package": "net.fabricmc.fabric.mixin.screenhandler",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"NamedScreenHandlerFactoryMixin",
|
||||
"ServerPlayerEntityMixin"
|
||||
],
|
||||
"client": [
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
],
|
||||
"accessWidener": "fabric-screen-handler-api-v1.accesswidener",
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "stable"
|
||||
"fabric-api:module-lifecycle": "stable",
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/class_3908": ["net/fabricmc/fabric/api/screenhandler/v1/FabricScreenHandlerFactory"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue