Added SimpleNetworkManager (#120)

Signed-off-by: shedaniel <daniel@shedaniel.me>

Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>
Co-authored-by: shedaniel <daniel@shedaniel.me>
This commit is contained in:
LatvianModder
2021-07-12 12:12:48 +08:00
committed by shedaniel
parent 6b32aec5b9
commit 5b7cb36414
12 changed files with 590 additions and 1 deletions

View File

@@ -56,6 +56,12 @@ allprojects {
options.release = 16 options.release = 16
} }
javadoc {
// Architectury's common javadoc has references to platform code, which cannot be resolved normally.
// Let's just skip the errors!
failOnError = false
}
license { license {
header = rootProject.file("HEADER") header = rootProject.file("HEADER")

View File

@@ -0,0 +1,41 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.networking.simple;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
/**
* The base class for client -&gt; server messages managed by a {@link SimpleNetworkManager}.
*/
public abstract class BaseC2SMessage extends Message {
/**
* Sends this message to the server.
*/
@Environment(EnvType.CLIENT)
public final void sendToServer() {
if (Minecraft.getInstance().getConnection() != null) {
Minecraft.getInstance().getConnection().send(toPacket());
} else {
throw new IllegalStateException("Unable to send packet to the server while not in game!");
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.networking.simple;
import net.minecraft.network.protocol.Packet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.chunk.LevelChunk;
/**
* The base class for server -&gt; client messages managed by a {@link SimpleNetworkManager}.
*/
public abstract class BaseS2CMessage extends Message {
private void sendTo(ServerPlayer player, Packet<?> packet) {
if (player == null) {
throw new NullPointerException("Unable to send packet '" + getType().getId() + "' to a 'null' player!");
}
player.connection.send(packet);
}
/**
* Sends this message to a player.
*
* @param player the player
*/
public final void sendTo(ServerPlayer player) {
sendTo(player, toPacket());
}
/**
* Sends this message to multiple players.
*
* @param players the players
*/
public final void sendTo(Iterable<ServerPlayer> players) {
Packet<?> packet = toPacket();
for (ServerPlayer player : players) {
sendTo(player, packet);
}
}
/**
* Sends this message to all players in the server.
*
* @param server the server
*/
public final void sendToAll(MinecraftServer server) {
sendTo(server.getPlayerList().getPlayers());
}
/**
* Sends this message to all players in a level.
*
* @param level the level
*/
public final void sendToLevel(ServerLevel level) {
sendTo(level.players());
}
/**
* Sends this message to all players listening to a chunk.
*
* @param chunk the listened chunk
*/
public final void sendToChunkListeners(LevelChunk chunk) {
Packet<?> packet = toPacket();
((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false).forEach(e -> sendTo(e, packet));
}
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.networking.simple;
import dev.architectury.networking.NetworkManager;
import io.netty.buffer.Unpooled;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet;
/**
* The base class for messages managed by a {@link SimpleNetworkManager}.
*
* @see BaseC2SMessage
* @see BaseS2CMessage
*/
public abstract class Message {
Message() {
}
/**
* Returns the {@link MessageType} of this message
*
* @return the {@link MessageType} of this message
* @see SimpleNetworkManager#registerC2S(String, MessageDecoder)
* @see SimpleNetworkManager#registerS2C(String, MessageDecoder)
*/
public abstract MessageType getType();
/**
* Writes this message to a byte buffer.
*
* @param buf the byte buffer
*/
public abstract void write(FriendlyByteBuf buf);
/**
* Handles this message when it is received.
*
* @param context the packet context for handling this message
*/
public abstract void handle(NetworkManager.PacketContext context);
/**
* Converts this message into a corresponding vanilla {@link Packet}.
*
* @return the converted {@link Packet}
*/
public final Packet<?> toPacket() {
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
write(buf);
return NetworkManager.toPacket(getType().getSide(), getType().getId(), buf);
}
}

View File

@@ -0,0 +1,55 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.networking.simple;
import dev.architectury.networking.NetworkManager;
import net.minecraft.network.FriendlyByteBuf;
/**
* Decodes a {@link Message} from a {@link FriendlyByteBuf}.
*
* @param <T> the message type handled by this decoder
* @author LatvianModder
*/
@FunctionalInterface
public interface MessageDecoder<T extends Message> {
/**
* Decodes a {@code T} message from a byte buffer.
*
* @param buf the byte buffer
* @return the decoded instance
*/
T decode(FriendlyByteBuf buf);
/**
* Creates a network receiver from this decoder.
*
* <p>The returned receiver will first {@linkplain #decode(FriendlyByteBuf) decode a message}
* and then call {@link Message#handle(NetworkManager.PacketContext)} on the decoded message.
*
* @return the created receiver
*/
default NetworkManager.NetworkReceiver createReceiver() {
return (buf, context) -> {
Message packet = decode(buf);
context.queue(() -> packet.handle(context));
};
}
}

View File

@@ -0,0 +1,89 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.networking.simple;
import dev.architectury.networking.NetworkManager;
import net.minecraft.resources.ResourceLocation;
import java.util.Objects;
/**
* A unique type for a message in a {@link SimpleNetworkManager}.
*/
public final class MessageType {
private final SimpleNetworkManager manager;
private final ResourceLocation id;
private final NetworkManager.Side side;
MessageType(SimpleNetworkManager manager, ResourceLocation id, NetworkManager.Side side) {
this.manager = manager;
this.id = id;
this.side = side;
}
/**
* Returns the network manager that manages this message type
*
* @return the network manager that manages this message type
*/
public SimpleNetworkManager getManager() {
return manager;
}
/**
* Returns the ID of this message type
*
* @return the ID of this message type
*/
public ResourceLocation getId() {
return id;
}
/**
* Returns the network side of this message type
*
* @return the network side of this message type
*/
public NetworkManager.Side getSide() {
return side;
}
@Override
public String toString() {
return id.toString() + ":" + side.name().toLowerCase();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
MessageType messageType = (MessageType) o;
return id.equals(messageType.id) && side == messageType.side;
}
@Override
public int hashCode() {
return Objects.hash(id, side);
}
}

View File

@@ -0,0 +1,82 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.networking.simple;
import dev.architectury.networking.NetworkManager;
import dev.architectury.platform.Platform;
import dev.architectury.utils.Env;
import net.minecraft.resources.ResourceLocation;
/**
* A simple wrapper for {@link NetworkManager} to make it easier to register messages and send them to clients/servers.
*/
public class SimpleNetworkManager {
/**
* Creates a new {@code SimpleNetworkManager}.
*
* @param namespace a unique namespace for the messages ({@link #namespace})
* @return the created network manager
*/
public static SimpleNetworkManager create(String namespace) {
return new SimpleNetworkManager(namespace);
}
/**
* The unique namespace for the messages managed by this manager.
* This will typically be a mod ID.
*/
public final String namespace;
private SimpleNetworkManager(String namespace) {
this.namespace = namespace;
}
/**
* Registers a server -&gt; client message.
*
* @param id a unique ID for the message, must be a valid value for {@link ResourceLocation#getPath}
* @param decoder the message decoder for the message
* @return a {@link MessageType} describing the registered message
*/
public MessageType registerS2C(String id, MessageDecoder<BaseS2CMessage> decoder) {
MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.s2c());
if (Platform.getEnvironment() == Env.CLIENT) {
NetworkManager.NetworkReceiver receiver = decoder.createReceiver();
NetworkManager.registerReceiver(NetworkManager.s2c(), messageType.getId(), receiver);
}
return messageType;
}
/**
* Registers a client -&gt; server message.
*
* @param id a unique ID for the message, must be a valid value for {@link ResourceLocation#getPath}
* @param decoder the message decoder for the message
* @return a {@link MessageType} describing the registered message
*/
public MessageType registerC2S(String id, MessageDecoder<BaseC2SMessage> decoder) {
MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.c2s());
NetworkManager.NetworkReceiver receiver = decoder.createReceiver();
NetworkManager.registerReceiver(NetworkManager.c2s(), messageType.getId(), receiver);
return messageType;
}
}

View File

@@ -9,7 +9,7 @@ cf_type=release
archives_base_name=architectury archives_base_name=architectury
archives_base_name_snapshot=architectury-snapshot archives_base_name_snapshot=architectury-snapshot
base_version=2.1 base_version=2.2
maven_group=dev.architectury maven_group=dev.architectury
fabric_loader_version=0.11.6 fabric_loader_version=0.11.6

View File

@@ -27,6 +27,7 @@ import dev.architectury.test.debug.client.ClientOverlayMessageSink;
import dev.architectury.test.entity.TestEntity; import dev.architectury.test.entity.TestEntity;
import dev.architectury.test.events.DebugEvents; import dev.architectury.test.events.DebugEvents;
import dev.architectury.test.gamerule.TestGameRules; import dev.architectury.test.gamerule.TestGameRules;
import dev.architectury.test.networking.TestModNet;
import dev.architectury.test.particle.TestParticles; import dev.architectury.test.particle.TestParticles;
import dev.architectury.test.registry.TestRegistries; import dev.architectury.test.registry.TestRegistries;
import dev.architectury.test.registry.client.TestKeybinds; import dev.architectury.test.registry.client.TestKeybinds;
@@ -50,6 +51,7 @@ public class TestMod {
TestTags.initialize(); TestTags.initialize();
TestTrades.init(); TestTrades.init();
TestParticles.initialize(); TestParticles.initialize();
TestModNet.initialize();
if (Platform.getEnvironment() == Env.CLIENT) { if (Platform.getEnvironment() == Env.CLIENT) {
initializeClient(); initializeClient();
} }

View File

@@ -0,0 +1,57 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.test.networking;
import dev.architectury.networking.NetworkManager;
import dev.architectury.networking.simple.BaseC2SMessage;
import dev.architectury.networking.simple.MessageType;
import net.minecraft.Util;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.TextComponent;
public class ButtonClickedMessage extends BaseC2SMessage {
private final int buttonId;
/**
* To send this message from client to server, call new ButtonClickedMessage(id).sendToServer()
*/
public ButtonClickedMessage(int id) {
buttonId = id;
}
public ButtonClickedMessage(FriendlyByteBuf buf) {
buttonId = buf.readVarInt();
}
@Override
public MessageType getType() {
return TestModNet.BUTTON_CLICKED;
}
@Override
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(buttonId);
}
@Override
public void handle(NetworkManager.PacketContext context) {
context.getPlayer().sendMessage(new TextComponent("You clicked button #" + buttonId), Util.NIL_UUID);
}
}

View File

@@ -0,0 +1,60 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.test.networking;
import dev.architectury.networking.NetworkManager;
import dev.architectury.networking.simple.BaseS2CMessage;
import dev.architectury.networking.simple.MessageType;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.TextComponent;
public class SyncDataMessage extends BaseS2CMessage {
private final CompoundTag serverData;
/**
* To send this message, call new SyncDataMessage(tag).sendToPlayer(player) / sendToAll(server) / etc.
*
* @see BaseS2CMessage
*/
public SyncDataMessage(CompoundTag tag) {
serverData = tag;
}
public SyncDataMessage(FriendlyByteBuf buf) {
serverData = buf.readAnySizeNbt();
}
@Override
public MessageType getType() {
return TestModNet.SYNC_DATA;
}
@Override
public void write(FriendlyByteBuf buf) {
buf.writeNbt(serverData);
}
@Override
public void handle(NetworkManager.PacketContext context) {
context.getPlayer().sendMessage(new TextComponent("Received data from server: " + serverData), Util.NIL_UUID);
}
}

View File

@@ -0,0 +1,37 @@
/*
* This file is part of architectury.
* Copyright (C) 2020, 2021 architectury
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package dev.architectury.test.networking;
import dev.architectury.networking.simple.MessageType;
import dev.architectury.networking.simple.SimpleNetworkManager;
import dev.architectury.test.TestMod;
public interface TestModNet {
SimpleNetworkManager NET = SimpleNetworkManager.create(TestMod.MOD_ID);
// An example Client to Server message
MessageType BUTTON_CLICKED = NET.registerC2S("button_clicked", ButtonClickedMessage::new);
// An example Server to Client message
MessageType SYNC_DATA = NET.registerS2C("sync_data", SyncDataMessage::new);
static void initialize() {
}
}