mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-25 09:59:15 +00:00
Do A Barrel Roll Protocol (#315)
* Do A Barrel Roll Protocol * cleanup * [ci skip] cleanup * [ci skip] cleanup * [ci skip] rename patch
This commit is contained in:
@@ -2,6 +2,8 @@ package org.dreeam.leaf.config.modules.network;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollPackets;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollProtocol;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@@ -22,6 +24,13 @@ public class ProtocolSupport extends ConfigModules {
|
||||
public static boolean syncmaticaQuota = false;
|
||||
public static int syncmaticaQuotaLimit = 40000000;
|
||||
|
||||
public static boolean doABarrelRollProtocol = false;
|
||||
public static boolean doABarrelRollAllowThrusting = false;
|
||||
public static boolean doABarrelRollForceEnabled = false;
|
||||
public static boolean doABarrelRollForceInstalled = false;
|
||||
public static int doABarrelRollInstalledTimeout = 40;
|
||||
public static DoABarrelRollPackets.KineticDamage doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.VANILLA;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
jadeProtocol = config.getBoolean(getBasePath() + ".jade-protocol", jadeProtocol);
|
||||
@@ -38,5 +47,26 @@ public class ProtocolSupport extends ConfigModules {
|
||||
if (syncmaticaProtocol) {
|
||||
org.leavesmc.leaves.protocol.syncmatica.SyncmaticaProtocol.init();
|
||||
}
|
||||
|
||||
doABarrelRollProtocol = config.getBoolean(getBasePath() + ".do-a-barrel-roll-protocol", doABarrelRollProtocol);
|
||||
doABarrelRollAllowThrusting = config.getBoolean(getBasePath() + ".do-a-barrel-roll-allow-thrusting", doABarrelRollAllowThrusting);
|
||||
doABarrelRollForceEnabled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-enabled", doABarrelRollForceEnabled);
|
||||
doABarrelRollForceInstalled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-installed", doABarrelRollForceInstalled);
|
||||
doABarrelRollInstalledTimeout = config.getInt(getBasePath() + ".do-a-barrel-roll-installed-timeout", 0);
|
||||
doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.valueOf(config.getString(getBasePath() + ".do-a-barrel-roll-kinetic-damage", doABarrelRollKineticDamage.name()));
|
||||
if (doABarrelRollInstalledTimeout <= 0) {
|
||||
doABarrelRollInstalledTimeout = 40;
|
||||
}
|
||||
if (doABarrelRollProtocol) {
|
||||
DoABarrelRollProtocol.init(
|
||||
doABarrelRollAllowThrusting,
|
||||
doABarrelRollForceEnabled,
|
||||
doABarrelRollForceInstalled,
|
||||
doABarrelRollInstalledTimeout,
|
||||
doABarrelRollKineticDamage
|
||||
);
|
||||
} else {
|
||||
DoABarrelRollProtocol.deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package org.dreeam.leaf.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DoABarrelRollPackets {
|
||||
private static <T extends LeafCustomPayload> LeafCustomPayload.@NotNull Type<T> createType(String path) {
|
||||
return new LeafCustomPayload.Type<>(ResourceLocation.fromNamespaceAndPath(DoABarrelRollProtocol.NAMESPACE, path));
|
||||
}
|
||||
|
||||
public record ConfigResponseC2SPacket(int protocolVersion, boolean success) implements LeafCustomPayload {
|
||||
public static final Type<ConfigResponseC2SPacket> TYPE = createType("config_response");
|
||||
public static final StreamCodec<FriendlyByteBuf, ConfigResponseC2SPacket> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.INT, ConfigResponseC2SPacket::protocolVersion,
|
||||
ByteBufCodecs.BOOL, ConfigResponseC2SPacket::success,
|
||||
ConfigResponseC2SPacket::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull Type<ConfigResponseC2SPacket> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public record ConfigSyncS2CPacket(int protocolVersion,
|
||||
LimitedModConfigServer applicableConfig,
|
||||
boolean isLimited,
|
||||
ModConfigServer fullConfig
|
||||
) implements LeafCustomPayload {
|
||||
public static final Type<ConfigSyncS2CPacket> TYPE = createType("config_sync");
|
||||
public static final StreamCodec<FriendlyByteBuf, ConfigSyncS2CPacket> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.INT, ConfigSyncS2CPacket::protocolVersion,
|
||||
LimitedModConfigServer.getCodec(), ConfigSyncS2CPacket::applicableConfig,
|
||||
ByteBufCodecs.BOOL, ConfigSyncS2CPacket::isLimited,
|
||||
ModConfigServer.PACKET_CODEC, ConfigSyncS2CPacket::fullConfig,
|
||||
ConfigSyncS2CPacket::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull Type<ConfigSyncS2CPacket> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public record ConfigUpdateAckS2CPacket(int protocolVersion, boolean success) implements LeafCustomPayload {
|
||||
public static final Type<ConfigUpdateAckS2CPacket> TYPE = createType("config_update_ack");
|
||||
public static final StreamCodec<FriendlyByteBuf, ConfigUpdateAckS2CPacket> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.INT, ConfigUpdateAckS2CPacket::protocolVersion,
|
||||
ByteBufCodecs.BOOL, ConfigUpdateAckS2CPacket::success,
|
||||
ConfigUpdateAckS2CPacket::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull Type<ConfigUpdateAckS2CPacket> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public record ConfigUpdateC2SPacket(int protocolVersion, ModConfigServer config) implements LeafCustomPayload {
|
||||
public static final Type<ConfigUpdateC2SPacket> TYPE = createType("config_update");
|
||||
public static final StreamCodec<FriendlyByteBuf, ConfigUpdateC2SPacket> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.INT, ConfigUpdateC2SPacket::protocolVersion,
|
||||
ModConfigServer.PACKET_CODEC, ConfigUpdateC2SPacket::config,
|
||||
ConfigUpdateC2SPacket::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull Type<ConfigUpdateC2SPacket> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public record RollSyncC2SPacket(boolean rolling, float roll) implements LeafCustomPayload {
|
||||
public static final Type<RollSyncC2SPacket> TYPE = createType("roll_sync");
|
||||
public static final StreamCodec<FriendlyByteBuf, RollSyncC2SPacket> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, RollSyncC2SPacket::rolling,
|
||||
ByteBufCodecs.FLOAT, RollSyncC2SPacket::roll,
|
||||
RollSyncC2SPacket::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull Type<RollSyncC2SPacket> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public record RollSyncS2CPacket(int entityId, boolean rolling, float roll) implements LeafCustomPayload {
|
||||
public static final Type<RollSyncS2CPacket> TYPE = createType("roll_sync");
|
||||
public static final StreamCodec<FriendlyByteBuf, RollSyncS2CPacket> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.INT, RollSyncS2CPacket::entityId,
|
||||
ByteBufCodecs.BOOL, RollSyncS2CPacket::rolling,
|
||||
ByteBufCodecs.FLOAT, RollSyncS2CPacket::roll,
|
||||
RollSyncS2CPacket::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public @NotNull Type<RollSyncS2CPacket> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
public interface LimitedModConfigServer {
|
||||
boolean allowThrusting();
|
||||
|
||||
boolean forceEnabled();
|
||||
|
||||
static StreamCodec<ByteBuf, LimitedModConfigServer> getCodec() {
|
||||
return StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, LimitedModConfigServer::allowThrusting,
|
||||
ByteBufCodecs.BOOL, LimitedModConfigServer::forceEnabled,
|
||||
Impl::new
|
||||
);
|
||||
}
|
||||
|
||||
record Impl(boolean allowThrusting, boolean forceEnabled) implements LimitedModConfigServer {
|
||||
}
|
||||
}
|
||||
|
||||
public record ModConfigServer(boolean allowThrusting,
|
||||
boolean forceEnabled,
|
||||
boolean forceInstalled,
|
||||
int installedTimeout,
|
||||
KineticDamage kineticDamage
|
||||
) implements LimitedModConfigServer {
|
||||
public static final StreamCodec<ByteBuf, ModConfigServer> PACKET_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, ModConfigServer::allowThrusting,
|
||||
ByteBufCodecs.BOOL, ModConfigServer::forceEnabled,
|
||||
ByteBufCodecs.BOOL, ModConfigServer::forceInstalled,
|
||||
ByteBufCodecs.INT, ModConfigServer::installedTimeout,
|
||||
KineticDamage.CODEC, ModConfigServer::kineticDamage,
|
||||
ModConfigServer::new
|
||||
);
|
||||
}
|
||||
|
||||
public enum KineticDamage {
|
||||
VANILLA,
|
||||
HIGH_SPEED,
|
||||
NONE,
|
||||
INSTANT_KILL;
|
||||
|
||||
public static final StreamCodec<ByteBuf, KineticDamage> CODEC =
|
||||
ByteBufCodecs.STRING_UTF8.map(KineticDamage::valueOf, KineticDamage::name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
package org.dreeam.leaf.protocol;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.*;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.event.player.PlayerKickEvent;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollPackets.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
public class DoABarrelRollProtocol implements Protocol {
|
||||
protected static final String NAMESPACE = "do_a_barrel_roll";
|
||||
private static final Logger LOGGER = LogManager.getLogger(NAMESPACE);
|
||||
private static final int PROTOCOL_VERSION = 4;
|
||||
private static final ModConfigServer DEFAULT = new ModConfigServer(false, false, false, 40, KineticDamage.VANILLA);
|
||||
private static final Component SYNC_TIMEOUT_MESSAGE = Component.literal("Please install Do a Barrel Roll 2.4.0 or later to play on this server.");
|
||||
private static DoABarrelRollProtocol INSTANCE = null;
|
||||
|
||||
private final List<Protocols.TypeAndCodec<FriendlyByteBuf, ? extends LeafCustomPayload>> c2s = ImmutableList.of(
|
||||
new Protocols.TypeAndCodec<>(ConfigUpdateC2SPacket.TYPE, ConfigUpdateC2SPacket.STREAM_CODEC),
|
||||
new Protocols.TypeAndCodec<>(ConfigResponseC2SPacket.TYPE, ConfigResponseC2SPacket.STREAM_CODEC),
|
||||
new Protocols.TypeAndCodec<>(RollSyncC2SPacket.TYPE, RollSyncC2SPacket.STREAM_CODEC));
|
||||
|
||||
private final List<Protocols.TypeAndCodec<FriendlyByteBuf, ? extends LeafCustomPayload>> s2c = ImmutableList.of(
|
||||
new Protocols.TypeAndCodec<>(ConfigUpdateAckS2CPacket.TYPE, ConfigUpdateAckS2CPacket.STREAM_CODEC),
|
||||
new Protocols.TypeAndCodec<>(ConfigSyncS2CPacket.TYPE, ConfigSyncS2CPacket.STREAM_CODEC),
|
||||
new Protocols.TypeAndCodec<>(RollSyncS2CPacket.TYPE, RollSyncS2CPacket.STREAM_CODEC)
|
||||
);
|
||||
|
||||
private ModConfigServer config = DEFAULT;
|
||||
private boolean configUpdated = false;
|
||||
|
||||
private final Reference2ReferenceMap<ServerGamePacketListenerImpl, ClientInfo> syncStates = new Reference2ReferenceOpenHashMap<>();
|
||||
private final Reference2ReferenceMap<ServerGamePacketListenerImpl, DelayedRunnable> scheduledKicks = new Reference2ReferenceOpenHashMap<>();
|
||||
public final Reference2BooleanMap<ServerGamePacketListenerImpl> isRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>());
|
||||
public final Reference2FloatMap<ServerGamePacketListenerImpl> rollMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>());
|
||||
public final Reference2BooleanMap<ServerGamePacketListenerImpl> lastIsRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>());
|
||||
public final Reference2FloatMap<ServerGamePacketListenerImpl> lastRollMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>());
|
||||
|
||||
public static void deinit() {
|
||||
if (INSTANCE != null) {
|
||||
INSTANCE = null;
|
||||
Protocols.unregister(INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void init(
|
||||
boolean allowThrusting,
|
||||
boolean forceEnabled,
|
||||
boolean forceInstalled,
|
||||
int installedTimeout,
|
||||
KineticDamage kineticDamage
|
||||
) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new DoABarrelRollProtocol();
|
||||
Protocols.register(INSTANCE);
|
||||
}
|
||||
INSTANCE.config = new ModConfigServer(allowThrusting, forceEnabled, forceInstalled, installedTimeout, kineticDamage);
|
||||
INSTANCE.configUpdated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String namespace() {
|
||||
return NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Protocols.TypeAndCodec<FriendlyByteBuf, ? extends LeafCustomPayload>> c2s() {
|
||||
return c2s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Protocols.TypeAndCodec<FriendlyByteBuf, ? extends LeafCustomPayload>> s2c() {
|
||||
return s2c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(ServerPlayer player, @NotNull LeafCustomPayload payload) {
|
||||
switch (payload) {
|
||||
case ConfigUpdateC2SPacket ignored ->
|
||||
player.connection.send(Protocols.createPacket(new ConfigUpdateAckS2CPacket(PROTOCOL_VERSION, false)));
|
||||
case ConfigResponseC2SPacket configResponseC2SPacket -> {
|
||||
var reply = clientReplied(player.connection, configResponseC2SPacket);
|
||||
if (reply == HandshakeState.RESEND) {
|
||||
sendHandshake(player);
|
||||
}
|
||||
}
|
||||
case RollSyncC2SPacket rollSyncC2SPacket -> {
|
||||
var state = getHandshakeState(player.connection);
|
||||
if (state.state != HandshakeState.ACCEPTED) {
|
||||
return;
|
||||
}
|
||||
var rolling = rollSyncC2SPacket.rolling();
|
||||
var roll = rollSyncC2SPacket.roll();
|
||||
isRollingMap.put(player.connection, rolling);
|
||||
if (Float.isInfinite(roll)) {
|
||||
roll = 0.0F;
|
||||
}
|
||||
rollMap.put(player.connection, roll);
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(ServerPlayer player) {
|
||||
final var handler = player.connection;
|
||||
syncStates.remove(handler);
|
||||
isRollingMap.removeBoolean(handler);
|
||||
rollMap.removeFloat(handler);
|
||||
lastIsRollingMap.removeBoolean(handler);
|
||||
lastRollMap.removeFloat(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickTracker(ServerPlayer player) {
|
||||
if (!isRollingMap.containsKey(player.connection)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isRolling = isRollingMap.getBoolean(player.connection);
|
||||
var roll = rollMap.getFloat(player.connection);
|
||||
var lastIsRolling = lastIsRollingMap.getBoolean(player.connection);
|
||||
var lastRoll = lastRollMap.getFloat(player.connection);
|
||||
if (isRolling == lastIsRolling && roll == lastRoll) {
|
||||
return;
|
||||
}
|
||||
var payload = new RollSyncS2CPacket(player.getId(), isRolling, roll);
|
||||
var packet = Protocols.createPacket(payload);
|
||||
for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy.toArray(new ServerPlayerConnection[0])) {
|
||||
if (seenBy instanceof ServerGamePacketListenerImpl conn
|
||||
&& getHandshakeState(conn).state == HandshakeState.ACCEPTED) {
|
||||
seenBy.send(packet);
|
||||
}
|
||||
}
|
||||
lastIsRollingMap.put(player.connection, isRolling);
|
||||
lastRollMap.put(player.connection, roll);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickPlayer(ServerPlayer player) {
|
||||
if (getHandshakeState(player.connection).state == HandshakeState.NOT_SENT) {
|
||||
sendHandshake(player);
|
||||
}
|
||||
if (!isRollingMap.containsKey(player.connection)) {
|
||||
return;
|
||||
}
|
||||
if (!isRollingMap.getBoolean(player.connection)) {
|
||||
rollMap.put(player.connection, 0.0F);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickServer(MinecraftServer server) {
|
||||
var it = scheduledKicks.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
var entry = it.next();
|
||||
if (entry.getValue().isDone()) {
|
||||
it.remove();
|
||||
} else {
|
||||
entry.getValue().tick();
|
||||
}
|
||||
}
|
||||
|
||||
if (configUpdated) {
|
||||
configUpdated = false;
|
||||
for (ServerPlayer player : server.getPlayerList().players) {
|
||||
sendHandshake(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OptionalInt getSyncTimeout(ModConfigServer config) {
|
||||
return config.forceInstalled() ? OptionalInt.of(config.installedTimeout()) : OptionalInt.empty();
|
||||
}
|
||||
|
||||
private void sendHandshake(ServerPlayer player) {
|
||||
player.connection.send(Protocols.createPacket(initiateConfigSync(player.connection)));
|
||||
configSentToClient(player.connection);
|
||||
}
|
||||
|
||||
private void configSentToClient(ServerGamePacketListenerImpl handler) {
|
||||
getHandshakeState(handler).state = HandshakeState.SENT;
|
||||
|
||||
OptionalInt timeout = getSyncTimeout(config);
|
||||
if (timeout.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
scheduledKicks.put(handler, new DelayedRunnable(timeout.getAsInt(), () -> {
|
||||
if (getHandshakeState(handler).state != HandshakeState.ACCEPTED) {
|
||||
LOGGER.warn(
|
||||
"{} did not accept config syncing, config indicates we kick them.",
|
||||
handler.getPlayer().getName().getString()
|
||||
);
|
||||
handler.disconnect(SYNC_TIMEOUT_MESSAGE, PlayerKickEvent.Cause.PLUGIN);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private HandshakeState clientReplied(ServerGamePacketListenerImpl handler, ConfigResponseC2SPacket packet) {
|
||||
var info = getHandshakeState(handler);
|
||||
var player = handler.getPlayer();
|
||||
|
||||
if (info.state == HandshakeState.SENT) {
|
||||
var protocolVersion = packet.protocolVersion();
|
||||
if (protocolVersion < 1 || protocolVersion > PROTOCOL_VERSION) {
|
||||
LOGGER.warn(
|
||||
"{} sent unknown protocol version, expected range 1-{}, got {}. Will attempt to proceed anyway.",
|
||||
player.getName().getString(),
|
||||
PROTOCOL_VERSION,
|
||||
protocolVersion
|
||||
);
|
||||
}
|
||||
|
||||
if (protocolVersion == 2 && info.protocolVersion != 2) {
|
||||
LOGGER.info("{} is using an older protocol version, resending.", player.getName().getString());
|
||||
info.state = HandshakeState.RESEND;
|
||||
} else if (packet.success()) {
|
||||
LOGGER.info("{} accepted server config.", player.getName().getString());
|
||||
info.state = HandshakeState.ACCEPTED;
|
||||
} else {
|
||||
LOGGER.warn(
|
||||
"{} failed to process server config, check client logs find what went wrong.",
|
||||
player.getName().getString());
|
||||
info.state = HandshakeState.FAILED;
|
||||
}
|
||||
info.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
return info.state;
|
||||
}
|
||||
|
||||
private boolean isLimited(ServerGamePacketListenerImpl net) {
|
||||
return true;
|
||||
// return net.getPlayer().getBukkitEntity().hasPermission(DoABarrelRoll.MODID + ".configure");
|
||||
}
|
||||
|
||||
private ClientInfo getHandshakeState(ServerGamePacketListenerImpl handler) {
|
||||
return syncStates.computeIfAbsent(handler, key -> new ClientInfo(HandshakeState.NOT_SENT, PROTOCOL_VERSION, true));
|
||||
}
|
||||
|
||||
private ConfigSyncS2CPacket initiateConfigSync(ServerGamePacketListenerImpl handler) {
|
||||
var isLimited = isLimited(handler);
|
||||
getHandshakeState(handler).isLimited = isLimited;
|
||||
return new ConfigSyncS2CPacket(PROTOCOL_VERSION, config, isLimited, isLimited ? DEFAULT : config);
|
||||
}
|
||||
|
||||
private static class ClientInfo {
|
||||
private HandshakeState state;
|
||||
private int protocolVersion;
|
||||
private boolean isLimited;
|
||||
|
||||
private ClientInfo(HandshakeState state, int protocolVersion, boolean isLimited) {
|
||||
this.state = state;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.isLimited = isLimited;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelayedRunnable {
|
||||
private final Runnable runnable;
|
||||
private final int delay;
|
||||
private int ticks = 0;
|
||||
|
||||
private DelayedRunnable(int delay, Runnable runnable) {
|
||||
this.runnable = runnable;
|
||||
this.delay = delay;
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
if (++ticks >= delay) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDone() {
|
||||
return ticks >= delay;
|
||||
}
|
||||
}
|
||||
|
||||
private enum HandshakeState {
|
||||
NOT_SENT,
|
||||
SENT,
|
||||
ACCEPTED,
|
||||
FAILED,
|
||||
RESEND
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.dreeam.leaf.protocol;
|
||||
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface LeafCustomPayload extends CustomPacketPayload {
|
||||
@NotNull
|
||||
@Override
|
||||
Type<? extends LeafCustomPayload> type();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.dreeam.leaf.protocol;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
interface Protocol {
|
||||
String namespace();
|
||||
List<Protocols.TypeAndCodec<FriendlyByteBuf, ? extends LeafCustomPayload>> c2s();
|
||||
List<Protocols.TypeAndCodec<FriendlyByteBuf, ? extends LeafCustomPayload>> s2c();
|
||||
void tickServer(MinecraftServer server);
|
||||
void tickPlayer(ServerPlayer player);
|
||||
void tickTracker(ServerPlayer player);
|
||||
void disconnected(ServerPlayer conn);
|
||||
void handle(ServerPlayer player, @NotNull LeafCustomPayload payload);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package org.dreeam.leaf.protocol;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Protocols {
|
||||
private static final ObjectArrayList<Protocol> PROTOCOLS = new ObjectArrayList<>();
|
||||
|
||||
static void register(Protocol protocol) {
|
||||
PROTOCOLS.add(protocol);
|
||||
}
|
||||
|
||||
static void unregister(Protocol protocol) {
|
||||
PROTOCOLS.remove(protocol);
|
||||
}
|
||||
|
||||
public record TypeAndCodec<B extends FriendlyByteBuf, T extends LeafCustomPayload>(LeafCustomPayload.Type<T> type, StreamCodec<B, T> codec) {}
|
||||
|
||||
public static <B extends FriendlyByteBuf> void write(B byteBuf, LeafCustomPayload payload) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
if (protocol.namespace().equals(payload.type().id().getNamespace())) {
|
||||
encode(byteBuf, payload, protocol);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handle(ServerPlayer player, @NotNull DiscardedPayload payload) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
if (payload.type().id().getNamespace().equals(protocol.namespace())) {
|
||||
var leafCustomPayload = decode(protocol, payload);
|
||||
if (leafCustomPayload != null) {
|
||||
protocol.handle(player, leafCustomPayload);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void tickServer(MinecraftServer server) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
protocol.tickServer(server);
|
||||
}
|
||||
}
|
||||
|
||||
public static void tickPlayer(ServerPlayer player) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
protocol.tickPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
public static void tickTracker(ServerPlayer player) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
protocol.tickTracker(player);
|
||||
}
|
||||
}
|
||||
|
||||
public static void disconnected(ServerPlayer conn) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
protocol.disconnected(conn);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ClientboundCustomPayloadPacket createPacket(LeafCustomPayload payload) {
|
||||
return new ClientboundCustomPayloadPacket(payload);
|
||||
}
|
||||
|
||||
private static <B extends FriendlyByteBuf> void encode(B byteBuf, LeafCustomPayload payload, Protocol protocol) {
|
||||
for (var codec : protocol.s2c()) {
|
||||
if (codec.type().id().equals(payload.type().id())) {
|
||||
byteBuf.writeResourceLocation(payload.type().id());
|
||||
//noinspection unchecked,rawtypes
|
||||
((StreamCodec) codec.codec()).encode(byteBuf, payload);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable LeafCustomPayload decode(Protocol protocol, DiscardedPayload payload) {
|
||||
for (var packet : protocol.c2s()) {
|
||||
if (packet.type().id().equals(payload.type().id())) {
|
||||
return packet.codec().decode(new FriendlyByteBuf(Unpooled.wrappedBuffer(payload.data())));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user