diff --git a/common/src/main/java/me/shedaniel/architectury/hooks/PlayerHooks.java b/common/src/main/java/me/shedaniel/architectury/hooks/PlayerHooks.java new file mode 100644 index 00000000..d542d003 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/hooks/PlayerHooks.java @@ -0,0 +1,43 @@ +/* + * Copyright 2020 shedaniel + * + * 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 me.shedaniel.architectury.hooks; + +import me.shedaniel.architectury.ArchitecturyPopulator; +import me.shedaniel.architectury.Populatable; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.world.entity.player.Player; + +@Environment(EnvType.CLIENT) +public final class PlayerHooks { + private PlayerHooks() {} + + @Populatable + private static final Impl IMPL = null; + + public static boolean isFake(Player player) { + return IMPL.isFake(player); + } + + public interface Impl { + boolean isFake(Player player); + } + + static { + ArchitecturyPopulator.populate(PlayerHooks.class); + } +} diff --git a/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java b/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java new file mode 100644 index 00000000..f20dde03 --- /dev/null +++ b/common/src/main/java/me/shedaniel/architectury/networking/NetworkChannel.java @@ -0,0 +1,130 @@ +/* + * Copyright 2020 shedaniel + * + * 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 me.shedaniel.architectury.networking; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import me.shedaniel.architectury.networking.NetworkManager.PacketContext; +import me.shedaniel.architectury.platform.Platform; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Objects; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Forge {@code SimpleChannel} like network wrapper of {@link NetworkManager}. + */ +public final class NetworkChannel { + private final ResourceLocation id; + private final IntSet takenIds = new IntOpenHashSet(); + private final Table, Pair>> encoders = HashBasedTable.create(); + + private NetworkChannel(ResourceLocation id) { + this.id = id; + } + + public static NetworkChannel create(ResourceLocation id) { + return new NetworkChannel(id); + } + + public void register(NetworkManager.Side side, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) { + register(Optional.ofNullable(side), type, encoder, decoder, messageConsumer); + } + + public void register(Optional side, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) { + for (int i = 0; ; i++) { + if (!takenIds.contains(i)) { + register(side, i, type, encoder, decoder, messageConsumer); + break; + } + } + } + + public void register(NetworkManager.Side side, int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) { + register(Optional.ofNullable(side), id, type, encoder, decoder, messageConsumer); + } + + public void register(Optional side, int id, Class type, BiConsumer encoder, Function decoder, BiConsumer> messageConsumer) { + takenIds.add(id); + ResourceLocation messageId = new ResourceLocation(this.id.getNamespace(), this.id.getPath() + "_" + id); + if (!side.isPresent() || side.get() == NetworkManager.s2c()) { + if (Platform.getEnv() == EnvType.CLIENT) { + NetworkManager.registerReceiver(NetworkManager.s2c(), messageId, (buf, context) -> { + messageConsumer.accept(decoder.apply(buf), () -> context); + }); + } + encoders.put(NetworkManager.s2c(), type, Pair.of(messageId, encoder)); + } + if (!side.isPresent() || side.get() == NetworkManager.c2s()) { + NetworkManager.registerReceiver(NetworkManager.c2s(), messageId, (buf, context) -> { + messageConsumer.accept(decoder.apply(buf), () -> context); + }); + encoders.put(NetworkManager.c2s(), type, Pair.of(messageId, encoder)); + } + } + + private Pair encode(NetworkManager.Side side, T message) { + Pair> pair = Objects.requireNonNull(encoders.get(side, message.getClass())); + FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + ((BiConsumer) pair.getRight()).accept(message, buf); + return Pair.of(pair.getLeft(), buf); + } + + public Packet toPacket(NetworkManager.Side side, T message) { + Pair encoded = encode(side, message); + return NetworkManager.toPacket(side, encoded.getLeft(), encoded.getRight()); + } + + public void sendToPlayer(ServerPlayer player, T message) { + Pair encoded = encode(NetworkManager.s2c(), message); + NetworkManager.sendToPlayer(player, encoded.getLeft(), encoded.getRight()); + } + + public void sendToPlayers(Iterable players, T message) { + Packet packet = toPacket(NetworkManager.s2c(), message); + for (ServerPlayer player : players) { + player.connection.send(packet); + } + } + + @Environment(EnvType.CLIENT) + public void sendToServer(T message) { + Minecraft.getInstance().getConnection().send(toPacket(NetworkManager.c2s(), message)); + } + + @Environment(EnvType.CLIENT) + public boolean canServerReceive(Class type) { + return NetworkManager.canServerReceive(encoders.get(NetworkManager.c2s(), type).getLeft()); + } + + public boolean canPlayerReceive(ServerPlayer player, Class type) { + return NetworkManager.canPlayerReceive(player, encoders.get(NetworkManager.s2c(), type).getLeft()); + } +} diff --git a/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/PlayerHooksImpl.java b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/PlayerHooksImpl.java new file mode 100644 index 00000000..bc0dc07b --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/hooks/fabric/PlayerHooksImpl.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 shedaniel + * + * 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 me.shedaniel.architectury.hooks.fabric; + +import me.shedaniel.architectury.hooks.PlayerHooks; +import net.minecraft.world.entity.player.Player; + +public class PlayerHooksImpl implements PlayerHooks.Impl { + @Override + public boolean isFake(Player player) { + return false; + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/hooks/forge/PlayerHooksImpl.java b/forge/src/main/java/me/shedaniel/architectury/hooks/forge/PlayerHooksImpl.java new file mode 100644 index 00000000..f590abdf --- /dev/null +++ b/forge/src/main/java/me/shedaniel/architectury/hooks/forge/PlayerHooksImpl.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 shedaniel + * + * 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 me.shedaniel.architectury.hooks.forge; + +import me.shedaniel.architectury.hooks.PlayerHooks; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraftforge.common.util.FakePlayer; + +public class PlayerHooksImpl implements PlayerHooks.Impl { + @Override + public boolean isFake(PlayerEntity playerEntity) { + return playerEntity instanceof FakePlayer; + } +}