Container API (#21)

This commit is contained in:
modmuss50 2018-12-22 18:38:57 +01:00 committed by Adrian Siekierka
parent 4e043a4781
commit 5b71b9c946
13 changed files with 549 additions and 1 deletions

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2016, 2017, 2018 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.gui;
import net.fabricmc.fabric.api.container.ContainerFactory;
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
import net.fabricmc.fabric.api.container.GuiFactory;
import net.fabricmc.fabric.impl.client.gui.GuiProviderImpl;
import net.minecraft.client.gui.ContainerGui;
import net.minecraft.container.Container;
import net.minecraft.util.Identifier;
public interface GuiProviderRegistry {
GuiProviderRegistry INSTANCE = GuiProviderImpl.INSTANCE;
/**
*
* Register a gui factory, this should only be done on the client side and not on the dedicated server.
*
* @param identifier a shared identifier, this identifier should also be used to register a container using {@link ContainerProviderRegistry}
* @param factory the gui factory, this should return a new {@link ContainerGui}
*/
void registerFactory(Identifier identifier, ContainerFactory<ContainerGui> factory);
/**
*
* Register a GuiFactory that will be used to create a new gui when provided with a container
*
* @param identifier a shared identifier, this identifier should also be used to register a container using {@link ContainerProviderRegistry}
* @param guiFactory the supplier that should be used to create the new gui
*/
<C extends Container> void registerFactory(Identifier identifier, GuiFactory<C> guiFactory);
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, 2017, 2018 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.container;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
@FunctionalInterface
public interface ContainerFactory<T> {
/**
*
* Creates the new gui or container
*
* @param identifier the Identifier is the name that was used when registering the factory
* @param player the player that is opening the gui/container
* @param buf the buffer contains the same data that was provided with {@Link ContainerProviderRegistry.openContainer}
* @return the new gui or container
*/
T create(Identifier identifier, PlayerEntity player, PacketByteBuf buf);
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2016, 2017, 2018 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.container;
import net.fabricmc.fabric.api.client.gui.GuiProviderRegistry;
import net.fabricmc.fabric.impl.container.ContainerProviderImpl;
import net.minecraft.container.Container;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import java.util.function.Consumer;
public interface ContainerProviderRegistry {
ContainerProviderRegistry INSTANCE = ContainerProviderImpl.INSTANCE;
/**
*
* Register a container factory
*
* @param identifier a shared identifier, this identifier should also be used to register a container using {@link GuiProviderRegistry}
* @param factory the ContainerFactory that should return a new {@link Container}
*/
void registerFactory(Identifier identifier, ContainerFactory<Container> factory);
/**
*
* This is used to open a container on the client, call this from the server
*
* @param identifier the identifier that was used when registering the container
* @param player the player that should open the container
* @param writer a PacketByteBuf where data can be written to, this data is then accessible by the container factory when creating the container or the gui
*/
void openContainer(Identifier identifier, ServerPlayerEntity player, Consumer<PacketByteBuf> writer);
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016, 2017, 2018 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.container;
import net.minecraft.client.gui.ContainerGui;
import net.minecraft.container.Container;
@FunctionalInterface
public interface GuiFactory<C extends Container> {
ContainerGui create(C container);
}

View file

@ -17,6 +17,7 @@
package net.fabricmc.fabric.impl;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.impl.client.gui.GuiProviderImpl;
import net.fabricmc.fabric.networking.CustomPayloadPacketRegistry;
import net.fabricmc.fabric.registry.RegistrySyncManager;
import net.minecraft.client.MinecraftClient;
@ -29,5 +30,7 @@ public class FabricAPIClientInitializer implements ClientModInitializer {
// if not hosting server, apply packet
RegistrySyncManager.receivePacket(ctx, buf, !MinecraftClient.getInstance().method_1496());
});
((GuiProviderImpl)GuiProviderImpl.INSTANCE).init();
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2016, 2017, 2018 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.client.gui;
import net.fabricmc.fabric.api.client.gui.GuiProviderRegistry;
import net.fabricmc.fabric.api.container.ContainerFactory;
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
import net.fabricmc.fabric.api.container.GuiFactory;
import net.fabricmc.fabric.impl.container.ContainerProviderImpl;
import net.fabricmc.fabric.networking.CustomPayloadPacketRegistry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.ContainerGui;
import net.minecraft.container.Container;
import net.minecraft.util.Identifier;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
public class GuiProviderImpl implements GuiProviderRegistry {
/**
* Use the instance provided by GuiProviderRegistry
*/
public static final GuiProviderRegistry INSTANCE = new GuiProviderImpl();
private static final Logger LOGGER = LogManager.getLogger();
private static final Identifier OPEN_CONTAINER = new Identifier("fabric", "open_container");
private static final Map<Identifier, ContainerFactory<ContainerGui>> FACTORIES = new HashMap<>();
public void registerFactory(Identifier identifier, ContainerFactory<ContainerGui> factory) {
if (FACTORIES.containsKey(identifier)) {
throw new RuntimeException("A factory has already been registered as " + identifier.toString());
}
FACTORIES.put(identifier, factory);
}
@Override
public <C extends Container> void registerFactory(Identifier identifier, GuiFactory<C> guiFactory) {
registerFactory(identifier, (identifier1, player, buf) -> {
C container = ((ContainerProviderImpl)ContainerProviderRegistry.INSTANCE).createContainer(identifier1, player, buf);
if(container == null){
LOGGER.error("A null container was created for " + identifier1.toString());
return null;
}
return guiFactory.create(container);
});
}
public void init() {
CustomPayloadPacketRegistry.CLIENT.register(OPEN_CONTAINER, (packetContext, packetByteBuf) -> {
Identifier identifier = packetByteBuf.readIdentifier();
int syncId = packetByteBuf.readUnsignedByte();
MinecraftClient.getInstance().execute(() -> {
ContainerFactory<ContainerGui> factory = FACTORIES.get(identifier);
if (factory == null) {
LOGGER.error("No factory found for " + identifier.toString());
return;
}
ContainerGui gui = factory.create(identifier, packetContext.getPlayer(), packetByteBuf);
gui.container.syncId = syncId;
MinecraftClient.getInstance().openGui(gui);
});
});
}
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2016, 2017, 2018 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.container;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.container.ContainerFactory;
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
import net.minecraft.client.network.packet.CustomPayloadClientPacket;
import net.minecraft.container.Container;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
public class ContainerProviderImpl implements ContainerProviderRegistry {
/**
* Use the instance provided by ContainerProviderRegistry
*/
public static final ContainerProviderRegistry INSTANCE = new ContainerProviderImpl();
private static final Logger LOGGER = LogManager.getLogger();
private static final Identifier OPEN_CONTAINER = new Identifier("fabric", "open_container");
private static final Map<Identifier, ContainerFactory<Container>> FACTORIES = new HashMap<>();
@Override
public void registerFactory(Identifier identifier, ContainerFactory<Container> factory) {
if (FACTORIES.containsKey(identifier)) {
throw new RuntimeException("A factory has already been registered as " + identifier.toString());
}
FACTORIES.put(identifier, factory);
}
@Override
public void openContainer(Identifier identifier, ServerPlayerEntity player, Consumer<PacketByteBuf> writer) {
SyncIdProvider syncIDProvider = (SyncIdProvider) player;
int syncId = syncIDProvider.fabric_incrementSyncId();
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
buf.writeIdentifier(identifier);
buf.writeByte(syncId);
writer.accept(buf);
player.networkHandler.sendPacket(new CustomPayloadClientPacket(OPEN_CONTAINER, buf));
PacketByteBuf clonedBuf = new PacketByteBuf(buf.duplicate());
clonedBuf.readIdentifier();
clonedBuf.readUnsignedByte();
Container container = createContainer(identifier, player, clonedBuf);
if(container == null){
return;
}
player.container = container;
player.container.syncId = syncId;
player.container.addListener(player);
}
public <C extends Container> C createContainer(Identifier identifier, PlayerEntity player, PacketByteBuf buf){
ContainerFactory<Container> factory = FACTORIES.get(identifier);
if (factory == null) {
LOGGER.error("No container factory found for %s ", identifier.toString());
return null;
}
return (C) factory.create(identifier, player, buf);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2016, 2017, 2018 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.container;
/**
* This is a interface that is present on a ServerPlayerEntity, it allows access to the sync id.
*/
public interface SyncIdProvider {
/**
* gets and sets the new player sync id, and returns the new value
*
* @return the new sync id of the player
*/
int fabric_incrementSyncId();
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016, 2017, 2018 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.container;
import net.fabricmc.fabric.impl.container.SyncIdProvider;
import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(ServerPlayerEntity.class)
public abstract class MixinServerPlayerEntity implements SyncIdProvider {
@Shadow private int containerSyncId;
@Shadow protected abstract void incrementContainerSyncId();
@Override
public int fabric_incrementSyncId() {
incrementContainerSyncId();
return containerSyncId;
}
}

View file

@ -6,6 +6,7 @@
"block.MixinBlockBuilder",
"block.entity.MixinBlockEntity",
"commands.MixinServerCommandManager",
"container.MixinServerPlayerEntity",
"entity.MixinEntityTracker",
"entity.MixinEntityTrackerEntry",
"events.objectbuilder.MixinBlock",

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2016, 2017, 2018 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.containers;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
import net.fabricmc.fabric.commands.CommandRegistry;
import net.minecraft.container.Container;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.command.ServerCommandManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
public class ContainerMod implements ModInitializer {
public static final Identifier EXAMPLE_CONTAINER = new Identifier("fabric_container", "example_container");
@Override
public void onInitialize() {
//Registers a basic server side command that shows that the openContainer works from the server side.
CommandRegistry.INSTANCE.register(false, serverCommandSourceCommandDispatcher ->
serverCommandSourceCommandDispatcher.register(ServerCommandManager
.literal("container")
.executes(context -> {
BlockPos pos = context.getSource().getEntity().getPos();
//Opens a container, sending the block pos
ContainerProviderRegistry.INSTANCE.openContainer(EXAMPLE_CONTAINER, context.getSource().getPlayer(), buf -> buf.writeBlockPos(pos));
return 1;
})));
//Registers a container factory that opens our example Container, this reads the block pos from the buffer
ContainerProviderRegistry.INSTANCE.registerFactory(EXAMPLE_CONTAINER, (identifier, player, buf) -> {
BlockPos pos = buf.readBlockPos();
return new ExampleContainer(pos, player);
});
}
//A basic container that prints to console when opened, this should print on the client + server
public static class ExampleContainer extends Container {
BlockPos pos;
public ExampleContainer(BlockPos pos, PlayerEntity playerEntity) {
this.pos = pos;
System.out.println("Opened container, " + pos);
}
@Override
public boolean canUse(PlayerEntity playerEntity) {
return true;
}
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2016, 2017, 2018 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.containers;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.gui.GuiProviderRegistry;
import net.minecraft.client.gui.ContainerGui;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
public class ContainerModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
//Registers a gui factory that opens our example gui, this reads the block pos from the buffer
GuiProviderRegistry.INSTANCE.registerFactory(ContainerMod.EXAMPLE_CONTAINER, (identifier, player, buf) -> {
BlockPos pos = buf.readBlockPos();
return new ExampleContainerGui(pos, player);
});
//Registers a gui factory that opens our example gui, this uses the container created by ContainerProviderRegistry
GuiProviderRegistry.INSTANCE.registerFactory(ContainerMod.EXAMPLE_CONTAINER, ExampleContainerGui2::new);
}
//A container gui that shows the block pos that was sent
public static class ExampleContainerGui extends ContainerGui {
BlockPos pos;
public ExampleContainerGui(BlockPos pos, PlayerEntity playerEntity) {
super(new ContainerMod.ExampleContainer(pos, playerEntity));
this.pos = pos;
}
@Override
protected void drawBackground(float v, int i, int i1) {
fontRenderer.draw(pos.toString(), width / 2, height / 2, 0);
}
}
//A container gui that shows how you can take in a container provided by a GuiFactory
public static class ExampleContainerGui2 extends ContainerGui {
BlockPos pos;
public ExampleContainerGui2(ContainerMod.ExampleContainer container) {
super(container);
this.pos = container.pos;
}
@Override
protected void drawBackground(float v, int i, int i1) {
fontRenderer.draw(pos.toString(), width / 2, height / 2, 0);
}
}
}

View file

@ -7,6 +7,8 @@
"license": "Apache-2.0",
"initializers": [
"net.fabricmc.fabric.colormapper.ColorProviderMod",
"net.fabricmc.fabric.events.ServerEventMod"
"net.fabricmc.fabric.events.ServerEventMod",
"net.fabricmc.fabric.containers.ContainerMod",
"net.fabricmc.fabric.containers.ContainerModClient"
]
}